[comp.os.minix] standard I/O and exit/_exit

gert@nikhefh.UUCP (Gert Poletiek) (07/06/87)

In article <3169@felix.UUCP> zemon@felix.UUCP (Art Zemon) writes:
>In article <2352@hoptoad.uucp> gnu@hoptoad.uucp (John Gilmore) writes:
>>
>>Wouldn't it be easier to replace the supplied exit() with one
>>that worked, e.g. that implemented onexit(), so stdio could get
>>its buffers flushed without rewriting every program?
>
>The original idea behind this was to keep the tools in
>/usr/bin and /bin small by not including the stdio library.
>Exit(), if it called _cleanup(), would bring in large chunks
>of otherwise unused code.
>
>I think a better solution would be to rename the existing
>exit() to _exit() and create an exit() subroutine which
>calls _cleanup() and change the tools which don't use stdio
>to call _exit().  Whew!  I said all that in one breath. :-)
>--
>	-- Art Zemon
>	   FileNet Corporation
>	   Costa Mesa, California
>	   ...!hplabs!felix!zemon

A solution that does not cause any overhead when not using the standard I/O
library, and still flushes the buffers of the standard I/O library when it
*is* used would be this:

In the runtime startup module for every program (crt0 ??)  one should declare
a pointer to a cleanupo function:

extern int		(_cleanup (*)());

Not having initialized this pointer it will be zero.

Then in the standard I/O library module that allocates the stream buffers
one would have to include the following:
(**note that the identifiers are probably not the same as those in the Minix
library: I don't have the tape yet **)

static __stdio_cleanup ()
{
	register int	i;

	for ( i=0; i<_NFILE; i++ )		/* for all streams */
		fclose ( _iobuf[i] );		/* close it */
}

int	(_cleanup (*)()) = __stdio_cleanup;


And in the module for exit:

extern int	(_cleanup (*)());

exit ( code )
int code;
{
	if ( _cleanup )			/* is standard I/O used ? */
		(*_cleanup)();		/* flush stdio buffers */
	/*
		rest of the exit routine as it is now 
	*/
}


In addition one needs a module that initializes the _cleanup pointer in case
standard I/O is not used:

int	(_cleanup (*)()) = 0;

This module should be in the end of the library so the linker will encounter
it always *after* the module containing the other initialisation of
_cleanup.

Hope that helps a bit.


Gert Poletiek
Dutch National Institute for High Energy Physics, NIKHEF-H
Amsterdam
The Netherlands.

trt@rti.UUCP (Thomas Truscott) (07/06/87)

This is off the subject, but is one of my pet annoyances:

> static __stdio_cleanup ()
> {
> 	register int	i;
> 
> 	for ( i=0; i<_NFILE; i++ )		/* for all streams */
> 		fclose ( _iobuf[i] );		/* close it */
> }

Please make the 'fclose()' an 'fflush()' instead
That would avoid the pointless
	close(0);
	close(1);
	close(2);
sequence at the end of *every* stdio-linked program.
I did this back in 1979 and have yet to encounter a bug.

It is ironic that the comment for _cleanup() is usually
	/*
	 * Flush buffers on exit
	 */
And then it calls fclose() instead!
	Tom Truscott

fnf@mcdsun.UUCP (Fred Fish) (07/08/87)

In article <374@nikhefh.UUCP> gert@nikhefh.UUCP (Gert Poletiek) writes:
>This module should be in the end of the library so the linker will encounter
>it always *after* the module containing the other initialisation of
>_cleanup.

Just so others don't fall into this same trap, this technique no longer
works with current System V linker and archiver (ld/ar).  The archiver
maintains a table of contents of global references, and the file offset
to the module that resolves them.  The linker takes the first one it
finds, ergo second and subsequent references are never seen.  The
special ordering of archive members seems to be a hack that was carried
over from the days when the linker scanned the archive sequentially looking
for unresolved symbols.

This discussion started in comp.os.minix, but is really a more global 
problem.

-Fred
-- 
= Drug tests; just say *NO*!
= Fred Fish  Motorola Computer Division, 3013 S 52nd St, Tempe, Az 85282  USA
= seismo!noao!mcdsun!fnf    (602) 438-5976

allbery@ncoast.UUCP (07/09/87)

As quoted from <374@nikhefh.UUCP> by gert@nikhefh.UUCP (Gert Poletiek):
+---------------
| This module should be in the end of the library so the linker will encounter
| it always *after* the module containing the other initialisation of
| _cleanup.
+---------------

Won't work.  The MINIX linker, unlike the UNIX one, always links everything,
not only the stuff that is referenced.  (boo hiss!)
-- 
[Copyright 1987 Brandon S. Allbery, all rights reserved] \ ncoast 216 781 6201
[Redistributable only if redistribution is subsequently permitted.] \ 2400 bd.
Brandon S. Allbery, moderator of comp.sources.misc and comp.binaries.ibm.pc
{{ames,harvard,mit-eddie}!necntc,{well,ihnp4}!hoptoad,cbosgd}!ncoast!allbery
<<The opinions herein are those of my cat, therefore they must be correct!>>

rmtodd@uokmax.UUCP (07/11/87)

In article <2827@ncoast.UUCP> allbery@ncoast.UUCP (Brandon Allbery) writes:
>+---------------
>| This module should be in the end of the library so the linker will encounter
>| it always *after* the module containing the other initialisation of
>| _cleanup.
>+---------------
>
>Won't work.  The MINIX linker, unlike the UNIX one, always links everything,
>not only the stuff that is referenced.  (boo hiss!)
Wait a minute.  I've never had the MINIX assembler/linker link in any module
that isn't referenced.  Never.  I've just set up my library to handle the
exit/_exit functions correctly and it seems to work just fine, linking in
only the stuff that needs to be linked in.  The technique I used to set up
the modules wasn't original with me--I saw it mentioned by someone named
Doug Braun the last time the exit/_exit question arose in this newsgroup.
Here's how it applies to MINIX and how I altered the library:
	1. Cd to the library source dir (in my system /u/src/lib).
	2. Edit printdat.c and include in it at the end the contents of
cleanup.c , deleting the redundant #include.  This way whenever the printdat
module is loaded (which is whenever _io_table is referenced, i.e. any stdio
file is referred to), the version of cleanup that flushes all stdio buffers
is also linked in.  You no longer need cleanup.c, so delete it.
	3. Edit exit.c.  Change the given exit() function so that it is now
called _exit() (the new name for the direct system exit call).  Add a new
function exit() at the end that looks like this:
-------------------------------------------------------------------------
	PUBLIC int exit(status)
	int status;
	{
		_cleanup();
		_exit(status);
	}
-------------------------------------------------------------------------
	4.  Create a new file 'fakecleanup.c' with this in it:
	int _cleanup() {}
	5. Compile the changed and added files and rebuild the library.
Note that the 'ar' commands 'r' (replace file) option is known to screw up
the library's order (deleting the file from the old position and replacing it
at the end.  Yuk.)  It's safest to recreate the library from scratch.  What I
do is cd to a subdirectory of /u/src/lib, unpack libc.a ("ar xv libc.a"),
delete libc.a, move the *.s files I've just made from /u/src/lib to the
subdirectory, and use a shell script to rebuild libc.a, which I then copy
to /usr/lib.  The shell script I use is included below; it shows a proper
ordering of the files in the archive.  Granted, it would be nice if 'ar'
was fully functional, but this will do in the meantime.  It takes about 3-4
minutes to rebuild the archive.

><<The opinions herein are those of my cat, therefore they must be correct!>>
Has your cat been using MINIX a long time? :-) :-)
(sorry, I couldn't resist...)

--------------------------cut here for make.libc script ------------------
ar av libc.a getgrent.s getpwent.s crypt.s
ar av libc.a fgets.s fprintf.s fputs.s fread.s freopen.s fclose.s
ar av libc.a fopen.s fseek.s ftell.s fwrite.s gets.s scanf.s getc.s printdat.s
ar av libc.a fflush.s setbuf.s sprintf.s doprintf.s putc.s ungetc.s strcmp.s
ar av libc.a access.s chdir.s chmod.s chown.s chroot.s creat.s dup.s dup2.s
ar av libc.a exec.s exit.s fakecleanup.s fork.s isatty.s fstat.s getegid.s getenv.s
ar av libc.a geteuid.s getgid.s getpass.s close.s getuid.s ioctl.s kill.s
ar av libc.a link.s lseek.s malloc.s brk.s brk2.s brksize.s mknod.s mktemp.s
ar av libc.a getpid.s mount.s open.s perror.s pipe.s prints.s read.s setgid.s
ar av libc.a setuid.s sleep.s alarm.s pause.s signal.s catchsig.s stat.s
ar av libc.a stime.s strcat.s strcpy.s strlen.s strncat.s strncmp.s strncpy.s
ar av libc.a sync.s time.s times.s umask.s umount.s unlink.s utime.s wait.s 
ar av libc.a stderr.s write.s syslib.s call.s atoi.s message.s sendrec.s
ar av libc.a printk.s abort.s itoa.s stb.s abs.s atol.s ctype.s index.s bcopy.s
ar av libc.a getutil.s rand.s rindex.s adi.s and.s cii.s cms.s cmu4.s com.s
ar av libc.a csa2.s csb2.s cuu.s .dup.s dvi.s dvi4.s dvu.s dvu4.s exg.s fakfp.s
ar av libc.a gto.s iaar.s ilar.s inn.s ior.s isar.s lar2.s loi.s mli.s mli4.s
ar av libc.a ngi.s nop.s rck.s rmi.s rmi4.s rmu.s rmu4.s rol.s ror.s sar2.s
ar av libc.a sbi.s set.s sli.s sri.s sti.s xor.s error.s unknown.s trp.s
ar av libc.a setjmp.s