Unix Programming - atexit+vfork+exit combination behaves differently on Aix & x-linux

This is Interesting: Free IT Magazines  
Home > Archive > Unix Programming > October 2005 > atexit+vfork+exit combination behaves differently on Aix & x-linux





You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

Author atexit+vfork+exit combination behaves differently on Aix & x-linux
param

2005-10-24, 3:48 pm

Hi

In the following program, the registered function (function registered
using atexit()) is executed by both parent and child on aix. but on
x-linux it is executed only once by child. I am not able to understand
the combination atexit+vfork+exit behaviour.
Please help me out.

#include <sys/types.h>
#include <unistd.h>
#define MAX 50

int main(void)
{
int a;
pid_t pid;
char buf[MAX];
void destruct(void);

if(atexit(destruct))
perror("main: atexit"), exit(1);

pid=vfork();
switch(pid)
{
case 0: strcpy(buf,"I m a child");
write(STDOUT_FILENO,buf,strlen(buf));
exit(0);
case -1: perror("main: vfork");
exit(1);
default: //strcpy(buf,"I m a root(parent)");
// write(STDOUT_FILENO,buf,strlen(buf));
printf("I m a root\n");
}

exit(0);
}

void destruct(void)
{
printf("I m a destructor");
}

Ouput (Aix):
I m a childI m a destructorI m a rootI m a destructor

Output (x-linux):
I m a childI m a destructorI m a root


Thanks
Param

loic-dev@gmx.net

2005-10-24, 3:48 pm

Salut Param,

> In the following program, the registered function (function registered
> using atexit()) is executed by both parent and child on aix. but on
> x-linux it is executed only once by child. I am not able to understand
> the combination atexit+vfork+exit behaviour.
> Please help me out.


The reason is that it is a really *bad* idea (TM) to use vfork().
Indeed when you vfork() a process, both parent and child can *both*
share code and data. In this case, any changes in the child is
reflected in the parent! Try for instance the following program on
Linux:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <assert.h>

int
main()
{
pid_t pid;
int x =3D 0;

printf ("Parent, before vfork(): x =3D %d\n", x);

pid =3D vfork();
assert (pid!=3D-1);

if ( pid =3D=3D 0 ) {
/*
* child set x to 42 and exit
*/
x=3D42;
_exit(0);
}

printf ("Parent, after vfork(): x =3D %d\n", x);
}

The idea behind vfork() was to speed-up *NEW* process creation. Often,
process fork() and then exec() immediately afterwards. In the bad old
days, fork() copied the whole process data space. This operation is
expensive, and a waste of time, especially if the next operation
consists to REPLACE the whole process image. That's where vfork() comes
to play. The child shares almost everything with the parent, thus
avoiding unnecessary copy. Then the whole image is replaced upon
exec(). IOW, the only thing useful you can do in a vfork()'ed child is
exec(), and eventually _exit(). Anything else is undefined. Especially
exit().

vfork() is now of historical interest, and might be withdrawn in the
future. Indeed, fork() are nowadays implemented with the so call
Copy-On-Write (COW) optimization, eliminating the need for vfork().

On some implementations, vfork() is equivalent to fork(). I don't know
AIX, but I guess that might be the reason why "it worked" there. This
is however not the case on Linux, as you noticed. =20

HTH,
Lo=EFc.

Casper H.S. Dik

2005-10-24, 3:48 pm

"param" <sparameswara@gmail.com> writes:

>In the following program, the registered function (function registered
>using atexit()) is executed by both parent and child on aix. but on
>x-linux it is executed only once by child. I am not able to understand
>the combination atexit+vfork+exit behaviour.


Calling exit() in the child of a vfork() or any other non
async-signal-safe functions gives undefined behaviour.

That's what you see.

(Even after fork(), it's preferred to call _exit() as calling
exit() may cause pending output to be flushed twice).
Casper
--
Expressed in this posting are my opinions. They are in no way related
to opinions held by my employer, Sun Microsystems.
Statements on Sun products included here are not gospel and may
be fiction rather than truth.
Rob Arthan

2005-10-24, 3:48 pm

loic-dev@gmx.net wrote:

> Salut Param,
>
>
> The reason is that it is a really *bad* idea (TM) to use vfork().
> Indeed when you vfork() a process, both parent and child can *both*
> share code and data.
> [snip]


You do seem to get the behaviour that Salut Param expects if you use fork
rather than vfork on Linux, but POSIX doesn't guarantee that. See the
recent discussion on exit and _exit which (as I understood it) concluded
that if you fork and don't exec a new process, then the behaviour is
undefined if both parent and child call exit. Typically one should call
exit and the other should call _exit.

http://groups.google.co.uk/group/co...8a077a356a752c4

Regards,

Rob.
Loic Domaigne

2005-10-24, 3:48 pm

Hi Rob,

>
>
> You do seem to get the behaviour that Salut Param expects if you use fork
> rather than vfork on Linux, but POSIX doesn't guarantee that. See the
> recent discussion on exit and _exit which (as I understood it) concluded
> that if you fork and don't exec a new process, then the behaviour is
> undefined if both parent and child call exit.


The reason why this is "undefined" is that the buffered data would be
then flushed twice. Like in:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

main()
{
pid_t pid;

/*
* buffer a message
*/
printf ("Hello, I am the parent");

/*
* fork
*/
pid = fork();
assert (pid!=-1);

/*
* both parent and child exits causing "Hello..." to be
* printed out twice
*/
exit (0);
}


However, if the parent would have taken care to flush stdout (on my
example), the problem wouldn't had occurred:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

main()
{
pid_t pid;

/*
* buffer a message
*/
printf ("Hello, I am the parent");
fflush (stdout); /* flush buffer */
/*
* fork
*/
pid = fork();
assert (pid!=-1);

/*
* the message appears now only one times as expected
*/
exit (0);
}


Cheers,
Loïc.
Casper H.S. Dik

2005-10-24, 3:48 pm

Loic Domaigne <loic-dev@gmx.net> writes:

>The reason why this is "undefined" is that the buffered data would be
>then flushed twice. Like in:


There's also an issue with some implementations which will adjust seek
pointers on seekable input to "unconsume" input which has not been read.


On such implementations,

(head -1; head -1) < file

prints the first two lines of a file, rather than the first line and a
partial line at the beginning of the second block.

You can see what happens when this happens in two branches of fork.

Casper
--
Expressed in this posting are my opinions. They are in no way related
to opinions held by my employer, Sun Microsystems.
Statements on Sun products included here are not gospel and may
be fiction rather than truth.
Michael Kerrisk

2005-10-24, 3:48 pm

>The idea behind vfork() was to speed-up *NEW* process creation. Often,
>process fork() and then exec() immediately afterwards. In the bad old
>days, fork() copied the whole process data space. This operation is
>expensive, and a waste of time, especially if the next operation
>consists to REPLACE the whole process image. That's where vfork() comes
>to play. The child shares almost everything with the parent, thus
>avoiding unnecessary copy. Then the whole image is replaced upon
>exec(). IOW, the only thing useful you can do in a vfork()'ed child is
>exec(), and eventually _exit(). Anything else is undefined. Especially
>exit().
>
>vfork() is now of historical interest, and might be withdrawn in the
>future. Indeed, fork() are nowadays implemented with the so call
>Copy-On-Write (COW) optimization, eliminating the need for vfork().


This is mostly true -- but even with COW, vfork() can still be quite a
bit faster than fork(). However, the time required for an exec()
dwarfs that required for fork()/vfork(), so that the difference
between the latter two calls becomes largely irrelevant.

>On some implementations, vfork() is equivalent to fork(). I don't know
>AIX, but I guess that might be the reason why "it worked" there. This
>is however not the case on Linux, as you noticed.


Just to add a little to this. The atexit() function is maintaining a
list of exit handlers in *user-space*. After a vfork(), the child is
using its parent's memory (until it either terminates or does an
exec()), and calling exit() in the child causes the exit handlers to
be invoked, and removed from the list: after the child terminates, the
parent resumes, *with an empty list of exit handlers*.

In systems that implement vfork() as fork(), the child gets a *copy*
of the parent's exit handler list, and that is why the handlers are
called twice. Presumably AIX is such an implementation.

Cheers,

Michael
Casper H.S. Dik

2005-10-24, 3:48 pm

Michael Kerrisk <michael.kerrisk.at.gmx.net@nospam.com> writes:

>This is mostly true -- but even with COW, vfork() can still be quite a
>bit faster than fork(). However, the time required for an exec()
>dwarfs that required for fork()/vfork(), so that the difference
>between the latter two calls becomes largely irrelevant.


With the possible exception of fork vs vfork on a 1GB application only
to exec shortly thereafter.

>In systems that implement vfork() as fork(), the child gets a *copy*
>of the parent's exit handler list, and that is why the handlers are
>called twice. Presumably AIX is such an implementation.


Whatever the implementation, calling exit() twice after fork or vfork
is just wrong.

Casper
--
Expressed in this posting are my opinions. They are in no way related
to opinions held by my employer, Sun Microsystems.
Statements on Sun products included here are not gospel and may
be fiction rather than truth.
sebasttj@gmail.com

2005-10-24, 3:49 pm

Casper H.S. Dik wrote:
> "param" <sparameswara@gmail.com> writes:
>
>
> Calling exit() in the child of a vfork() or any other non
> async-signal-safe functions gives undefined behaviour.
>
> That's what you see.
>
> (Even after fork(), it's preferred to call _exit() as calling
> exit() may cause pending output to be flushed twice).


The C standard defines a return from main() as being equivalent to a
call to exit(). Does that mean that it's also unsafe to return from
main() in the child?

Josh

Casper H.S. Dik

2005-10-24, 3:49 pm

sebasttj@gmail.com writes:

>Casper H.S. Dik wrote:
[vbcol=seagreen]
>The C standard defines a return from main() as being equivalent to a
>call to exit(). Does that mean that it's also unsafe to return from
>main() in the child?


One would assume so and running the following program in a way which
forces more buffering than line buffering demonstrates that.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int
main(int argc, char **argv)
{
printf("Just one line\n");
fork();
return (0);
}

Casper
--
Expressed in this posting are my opinions. They are in no way related
to opinions held by my employer, Sun Microsystems.
Statements on Sun products included here are not gospel and may
be fiction rather than truth.
Chris Friesen

2005-10-24, 3:49 pm

Casper H.S. Dik wrote:
> sebasttj@gmail.com writes:


>
>
> One would assume so and running the following program in a way which
> forces more buffering than line buffering demonstrates that.


This is why I generally call fflush() before fork() if I might have
outstanding I/O.

Chris
Michael Kerrisk

2005-10-25, 2:49 am

On 24 Oct 2005 13:45:47 GMT, Casper H.S. Dik <Casper.Dik@Sun.COM>
wrote:

>Michael Kerrisk <michael.kerrisk.at.gmx.net@nospam.com> writes:
>
>
>With the possible exception of fork vs vfork on a 1GB application only
>to exec shortly thereafter.


Yes, my statement was perhaps a little strong. If we are talking
about large processes (>= tens of MB) and small executables, then the
times for fork() and exec() become comparable.

>
>Whatever the implementation, calling exit() twice after fork or vfork
>is just wrong.


Oh -- I wasn't disagreeing with that. That's why I said "Just to add
a little to this [the explanation by Loic]". To be precise, SUSv3
says that calling any function other than execve() or _exit() in the
child of a vfork() results in undefined behaviour; in practice I think
we would say: it probably does not give us the results we want.

Cheers,

Michael
Henry Townsend

2005-10-25, 7:48 am

In-Reply-To: <hagrl1dv6epifjmp9b302vf3v9u9qgetob@4ax.com>
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit
Message-ID: <yKqdnazU5LcIhsPeRVn-iQ@comcast.com>
Lines: 23
NNTP-Posting-Host: 66.30.112.212
X-Trace: sv3-YnVzxa5bBqzqyjZm/hR/yFk7p9Sm/ mjX9w5XjuBXTHo+vsqnOqXvYzLwtnXFc+GKXDs6+
wKP7Iudaob!N63aEHPQBj7wYgRNBacePzoz+BCJ3
gwep6ph6SBCpjFpW4wGaPgR6OuDcvzP4L5MlPHwQ
qfZAg==
X-Complaints-To: abuse@comcast.net
X-DMCA-Complaints-To: dmca@comcast.net
X-Abuse-and-DMCA-Info: Please be sure to forward a copy of ALL headers
X-Abuse-and-DMCA-Info: Otherwise we will be unable to process your complaint properly
X-Postfilter: 1.3.32
Xref: number1.nntp.dca.giganews.com comp.unix.programmer:161826

Michael Kerrisk wrote:
> On 24 Oct 2005 13:45:47 GMT, Casper H.S. Dik <Casper.Dik@Sun.COM>
> wrote:
>
>
> Yes, my statement was perhaps a little strong. If we are talking
> about large processes (>= tens of MB) and small executables, then the
> times for fork() and exec() become comparable.


But this is an issue for more than timing - if the process gets too
large for swap it may not be possible for it to fork and the only way to
run a subprocess is to use a vfork. AFAIK this is the only remaining
valid use of vfork.

HT
Casper H.S. Dik

2005-10-25, 7:48 am

Henry Townsend <henry.townsend@not.here> writes:

>But this is an issue for more than timing - if the process gets too
>large for swap it may not be possible for it to fork and the only way to
>run a subprocess is to use a vfork. AFAIK this is the only remaining
>valid use of vfork.


Quite; under the hood, Solaris' posix_spwan and other calls use vfork()
because of this (libraries are free to use a MT-Unreliable call such as
vfork even in MT-programs because the implementation can know sufficient
about the restrictions on vfork() to overcome them)

Casper
--
Expressed in this posting are my opinions. They are in no way related
to opinions held by my employer, Sun Microsystems.
Statements on Sun products included here are not gospel and may
be fiction rather than truth.
Michael Kerrisk

2005-10-25, 7:48 am

On 25 Oct 2005 12:31:58 GMT, Casper H.S. Dik <Casper.Dik@Sun.COM>
wrote:

>Henry Townsend <henry.townsend@not.here> writes:
>
>
>Quite; under the hood, Solaris' posix_spwan and other calls use vfork()
>because of this (libraries are free to use a MT-Unreliable call such as
>vfork even in MT-programs because the implementation can know sufficient
>about the restrictions on vfork() to overcome them)


Maybe I'm missing something. Why would a COW implementation of fork()
consume swap space (if the child is performing an immediate exec())?

Cheers,

Michael
Casper H.S. Dik

2005-10-25, 5:53 pm

Michael Kerrisk <michael.kerrisk.at.gmx.net@nospam.com> writes:

>Maybe I'm missing something. Why would a COW implementation of fork()
>consume swap space (if the child is performing an immediate exec())?


Depends on whether the OS does immediate allocation (need to allocate all
swap space for COW pages) or lazy allocation.

In the latter case, the OS may need to send a SIGKILL when a ZFOD page
fault is handled which is considered bad form in some circles.

Casper
--
Expressed in this posting are my opinions. They are in no way related
to opinions held by my employer, Sun Microsystems.
Statements on Sun products included here are not gospel and may
be fiction rather than truth.
Michael Kerrisk

2005-10-25, 5:53 pm

On 25 Oct 2005 13:20:54 GMT, Casper H.S. Dik <Casper.Dik@Sun.COM>
wrote:

>Michael Kerrisk <michael.kerrisk.at.gmx.net@nospam.com> writes:
>
>
>Depends on whether the OS does immediate allocation (need to allocate all
>swap space for COW pages) or lazy allocation.


Ahh -- yes, I'd overlooked that.

Thanks.

Michael
Sponsored Links






Free braindumps | Software forum | Database administration forum

Copyright 2003 - 2008 webservertalk.com