fork()

S

steve

I was given the following code, and asked what the possible outputs
could be. We're learning about processes and forking.

int value;
int main(){
int pid, number = 1;
value = 2;
pid = fork();
value = value + 2;
number = number + 2;
if (pid) {
printf(" 0 %d %d \n", value, number);
waitpid(pid, NULL, 0);
}
else{
printf(" 1 %d %d \n", value, number);
}
return 0;
}

I've come up with 3 possibilities so far:

1) the fork() fails; the parent's pid is assigned a negative integer,
so the if() condition passes and the output is 0 4 3

2) the fork() succeeds, the child* (pid = 0 from successful fork())
prints 1 4 3 and then returns; the parent (pid = child's process id #)
starts up prints 0 4 3 and returns

3) the fork() succeeds, but the child's quantum expires^ before it
reaches the printf(), the parent (pid = child's process id #) starts,
prints 0 4 3 and then waits for the child; the child (pid = 0 from
successful fork()) starts back up and prints 1 4 3 and returns; the
parent returns

* I guess the child takes precedence over the parent (system
dependent?) because every time I've run the program the output has been
1 4 3 followed by 0 4 3.

^ I guess the CPU's quantum length would have to be incredibly small
for case 3 to happen, but it's possible, right?

I'm new to this material, so any comments would be truly appreciated.
 
A

Artie Gold

I was given the following code, and asked what the possible outputs
could be. We're learning about processes and forking.

int value;
int main(){
int pid, number = 1;
value = 2;
pid = fork();

Sorry, but fork() is not part of ISO C. Take your question to
where it will likely be answered thoroughly
and gracefully.

HTH,
--ag
 
W

Walter Roberson

I was given the following code, and asked what the possible outputs
could be. We're learning about processes and forking.

fork() is not part of the C language. I suggest you take the
question to a more appropriate newsgroup, such as comp.unix.programmer

I've come up with 3 possibilities so far:

Oh, there's more than 3, especially since your program
doesn't use volatile, and your program doesn't declare printf().
It also has some POSIX problems, but POSIX is off topic for
comp.lang.c .
 
K

Keith Thompson

I was given the following code, and asked what the possible outputs
could be. We're learning about processes and forking.

int value;
int main(){
int pid, number = 1;
value = 2;
pid = fork();
value = value + 2;
number = number + 2;
if (pid) {
printf(" 0 %d %d \n", value, number);
waitpid(pid, NULL, 0);
}
else{
printf(" 1 %d %d \n", value, number);
}
return 0;
}
[snip]

"int main()" is valid, but "int main(void)" is preferred.

You're missing a "#include <stdio.h>", which is required if you're
going to use printf(). You're also missing whatever header declares
fork() and waitpid().

But in fact, the code above wouldn't even compile without a #include
for at least one of the headers that defines the NULL macro. Please
post complete, compilable programs.

The fork() function is not defined in standard C. The people who can
answer your questions about it hang out in comp.unix.programmer.
 
S

SM Ryan

# * I guess the child takes precedence over the parent (system
# dependent?) because every time I've run the program the output has been
# 1 4 3 followed by 0 4 3.

If you need to serialise code, use a language/library that
guarentees serialisation rather than hoping and guessing.
 
W

Walter Roberson

why does that matter? there's no need for volatile here.

The original poster's program modifies both "value" and "number"
inside the forked process. The operation of fork() and the
semantics of modifying values in multiple processes are not
specified (or imagined) in the C standards. C specifies that
if a variable might be modified by mechanisms outside of C, then one
must qualify the variable with "volatile".

Without the "volatile", the C compiler is justified in loading the
variables into registers before the fork() and holding them there,
thus producing two -identical- output lines.
 
C

CBFalconer

SM said:
# * I guess the child takes precedence over the parent (system
# dependent?) because every time I've run the program the output
# has been 1 4 3 followed by 0 4 3.

If you need to serialise code, use a language/library that
guarentees serialisation rather than hoping and guessing.

Please take this to a group where it is on-topic. Also please fix
your non-conventional quote marker character.

--
"If you want to post a followup via groups.google.com, don't use
the broken "Reply" link at the bottom of the article. Click on
"show options" at the top of the article, then click on the
"Reply" at the bottom of the article headers." - Keith Thompson
More details at: <http://cfaj.freeshell.org/google/>
Also see <http://www.safalra.com/special/googlegroupsreply/>
 
J

Jordan Abel

The original poster's program modifies both "value" and "number"
inside the forked process. The operation of fork() and the
semantics of modifying values in multiple processes are not
specified (or imagined) in the C standards. C specifies that
if a variable might be modified by mechanisms outside of C, then one
must qualify the variable with "volatile".

The particular outside-of-C mechanism in use does not provide for the
modification of the variable in one process by another process, so it is
not necessary. [this is the problem with off-topic responses - some of
them are inevitably wrong]
 
B

Ben Bacarisse

The original poster's program modifies both "value" and "number" inside
the forked process. The operation of fork() and the semantics of modifying
values in multiple processes are not specified (or imagined) in the C
standards.

Having said that, surely all bets are then off? As far a standard C is
concerned, the program might do anything at all.
C specifies that if a variable might be modified by mechanisms
outside of C, then one must qualify the variable with "volatile".

To speculate any further, a meaning must be given to fork(). Taking the
POSIX version as a reasonable guess, I would not say that any variables
are being modified by any mechanism "outside of C". (Sure, the program
forks, but there is no IPC or shared memory.)
Without the "volatile", the C compiler is justified in loading the
variables into registers before the fork() and holding them there, thus
producing two -identical- output lines.

I take it you mean identical but for the leading 0/1?. If so, is that not
what happens, registers or no registers, volatile or no volatile, and,
indeed, what the OP stated initially?
 
W

Walter Roberson

To speculate any further, a meaning must be given to fork(). Taking the
POSIX version as a reasonable guess, I would not say that any variables
are being modified by any mechanism "outside of C". (Sure, the program
forks, but there is no IPC or shared memory.)

POSIX.1 1990 section 3.1.1 "Process Creation", subsection
3.1.1.2 "Description" [of fork()], says that the child process
shall be an "exact copy of the calling process" with certain exceptions.
None of those exceptions deal with the matter of whether the variables
are shared between the resulting processes. All that is required
in that regard is,

After fork(), both the parent and the child processes shall be
capable of executing independently before either terminates.

"executing independantly" is not qualified, so it is not clear
what happens with variables. The list of exceptions about "exact copy"
makes it clear that it is permissible for the two processes to share
some characteristics (e.g., having to do with file or directory
descriptors.) There is a whole section of POSIX.1 devoted to the
issues involved in sharing file descriptors, so clearly -some-
parts of the process are shared. [Note in this regard that POSIX.1
does -not- define any manner of "threads", so the issues are
considered to arise between fork()'d processes.]

It has been some time since I went through POSIX.1 in detail;
I do not at the moment recall any section of POSIX.1 that requires
duplicating virtual memory or copy-on-write semantics for fork().
Although POSIX.1 Part 1 defines its APIs in terms of C, POSIX.1
is intended to be language-agnostic; as such it should neither
permit nor forbid the language from offering (or accidently
implementing) shared memory across forks. As C does not prohibit
sharing from happening in case of fork(), we must consider the
matter to be completely undefined behaviour when volatile is not
used, and at least -more- specified behaviour when volatile is used.
 
B

Ben Bacarisse

<description of fork() snipped>

As C does not prohibit sharing from happening in case of fork(), we
must consider the matter to be completely undefined behaviour when
volatile is not used, and at least -more- specified behaviour when
volatile is used.

I am having trouble with this. It suggests that any program that uses
fork() must mark *all* its variables volatile in order to avoid undefined
behaviour and even then all you get is "more" specified behaviour.

This implies that standard C does not have strong enough semantics for
programs that use POSIX fork() to have a defined meaning.
 
J

Jordan Abel

I am having trouble with this. It suggests that any program that uses
fork() must mark *all* its variables volatile in order to avoid undefined
behaviour and even then all you get is "more" specified behaviour.

This implies that standard C does not have strong enough semantics for
programs that use POSIX fork() to have a defined meaning.

Well, of course it doesn't. POSIX fork() isn't provided in standard C.
Therefore standard C doesn't define the meaning of any program using
fork(). That still doesn't mean you have to mark stuff as volatile,
given what the semantics [as provided in posix] actually are.
 
W

Walter Roberson

Well, of course it doesn't. POSIX fork() isn't provided in standard C.
Therefore standard C doesn't define the meaning of any program using
fork(). That still doesn't mean you have to mark stuff as volatile,
given what the semantics [as provided in posix] actually are.

You are making a claim that the semantics of fork() "as provided in
posix" have some specific behaviour. Please cite specific sections
that establish the semantics that you believe to be "provided in
posix".

I have ISO 9945-1 / IEEE 1003.1 1990 right here, and I do not
find the semantics you impute. All I find is the wording I mentioned
before in the main description of fork() about the two processes
executing "independantly", together with some vague implications
in the "informative" (but not normative) Rationale, B.3.1.1 Process
Creation, in the description of ENOMEM.

B.3.1.1 -specifically- says that only some implementations of fork()
are capable of returning ENOMEM, so any the most one can conclude from
that discussion is that duplicating process memory is an -allowed-
result of fork() but not a -required- result of fork().

I refer you back to the wording Keith dug up in the C standard, to the
effect that "undefined behaviour includes all cases where this standard
does not specify a behaviour". Just because fork() does not result in
shared memory effects on any POSIX.1 implementation you have
encountered does not mean that there cannot be a POSIX.1 conformant
implementation in which fork() does have shared memory effects.
 
J

Jordan Abel

Well, of course it doesn't. POSIX fork() isn't provided in standard C.
Therefore standard C doesn't define the meaning of any program using
fork(). That still doesn't mean you have to mark stuff as volatile,
given what the semantics [as provided in posix] actually are.

You are making a claim that the semantics of fork() "as provided in
posix" have some specific behaviour. Please cite specific sections
that establish the semantics that you believe to be "provided in
posix".

I have ISO 9945-1 / IEEE 1003.1 1990 right here, and I do not
find the semantics you impute. All I find is the wording I mentioned
before in the main description of fork() about the two processes
executing "independantly", together with some vague implications
in the "informative" (but not normative) Rationale, B.3.1.1 Process
Creation, in the description of ENOMEM.

I think that if they influence each other's variables, they're hardly
independent.
B.3.1.1 -specifically- says that only some implementations of fork()
are capable of returning ENOMEM, so any the most one can conclude from
that discussion is that duplicating process memory is an -allowed-
result of fork() but not a -required- result of fork().

Rather than "returning" ENOMEM (funny thing to call it - but we all
understand "returning E?????" to mean returning an error value and
setting errno to the mentioned value), an implementation could block
both processes until memory is available. Or it could use copy-on-write
semantics (which, then, would succeed at the fork call _despite_ a
shortage of available memory), blocking or segfaulting the process when
it runs out of memory [and hoping, presumably, that the child dies or
execs before it or the parent modify a significant number of pages]
I refer you back to the wording Keith dug up in the C standard, to the
effect that "undefined behaviour includes all cases where this standard
does not specify a behaviour". Just because fork() does not result in
shared memory effects on any POSIX.1 implementation you have
encountered does not mean that there cannot be a POSIX.1 conformant
implementation in which fork() does have shared memory effects.

I believe the "independently" wording you mentioned precludes that, and
I just named two other reasons fork() could be incapable of resulting in
ENOMEM.
 
R

Richard Tobin

Just because fork() does not result in
shared memory effects on any POSIX.1 implementation you have
encountered does not mean that there cannot be a POSIX.1 conformant
implementation in which fork() does have shared memory effects.

This would not be POSIX-conformant in any sense except an absurdly
pedantic one. Everyone knows what POSIX intends here. If you find
the wording insufficiently precise, this is not the place to bring it
up.

-- Richard
 
W

Walter Roberson

This would not be POSIX-conformant in any sense except an absurdly
pedantic one. Everyone knows what POSIX intends here.

There are many discussions in comp.lang.c that revolve around
what the C standard actually -says- rather than what "everyone knows"
happens in real implementations. Just look at the DS9000 specs ;-)

The OP intended two variables to be modified by different processes,
believing that the modifications in one should result in modifications
in the other, and several of the replies were in terms of time slices
rather than in terms of "the two processes just don't share memory".
The OP -intended- intra-process interactions, and the only way within
C that one can deal with asynchronous interactions is to use volatile.


POSIX does not define "independant" execution at all clearly; either
POSIX defines that there will not be intraprocess effects (in which
case volatile becomes redundant, as does most of the program as a whole),
or else POSIX does -not- define that there will be no intraprocess effects,
in which case volatile is the only chance that one has within C
of getting the interactions right.

Having gone through POSIX.1 this morning, I don't believe that defines
the matter either way, so I disagree with Jordan that volatile is not
necessary for the program -- at least when the program is
run on the DS9003.1 series.

volatile exists for the cases where the programmer cannot promise
that variables will only be modified through overt synchronous
routines that conform to the C specifications. fork() in the
presence of possible (or at least expected) shared memory cannot
meet that promise, so the programmer, expecting that promise
to be broken, should have used volatile.
 
J

Jordan Abel

There are many discussions in comp.lang.c that revolve around
what the C standard actually -says- rather than what "everyone knows"
happens in real implementations. Just look at the DS9000 specs ;-)

The OP intended two variables to be modified by different processes,
believing that the modifications in one should result in modifications
in the other, and several of the replies were in terms of time slices
rather than in terms of "the two processes just don't share memory".
The OP -intended- intra-process interactions, and the only way within
C that one can deal with asynchronous interactions is to use volatile.

No, the question was about the effects of "timeslices", etc, on the
_output_ of the two programs [which _do_ share an open file, namely
stdout]
Having gone through POSIX.1 this morning, I don't believe that defines
the matter either way, so I disagree with Jordan that volatile is not
necessary for the program -- at least when the program is
run on the DS9003.1 series.

But if the programmer does not INTEND that the variable in the parent
process should be modified by actions taken in the child process, it
doesn't make sense to add volatile - it might make sense to do something
else about it, but adding volatile doesn't fit the programmer's intent.
volatile exists for the cases where the programmer cannot promise
that variables will only be modified through overt synchronous
routines that conform to the C specifications. fork() in the
presence of possible (or at least expected) shared memory

You're the only one who thinks there is any wording that allows fork()
to have shared memory semantics for modifiable variables
 
T

tedu

Walter said:
You are making a claim that the semantics of fork() "as provided in
posix" have some specific behaviour. Please cite specific sections
that establish the semantics that you believe to be "provided in
posix".

http://www.opengroup.org/onlinepubs/009695399/functions/vfork.html
"The vfork() function differs from fork() only in that the child
process can share code and data with the calling process (parent
process)."

now if vfork() is different because it can share, and that is the only
difference, i think it's pretty clear that fork() cannot share.
 
K

Keith Thompson

tedu said:
http://www.opengroup.org/onlinepubs/009695399/functions/vfork.html
"The vfork() function differs from fork() only in that the child
process can share code and data with the calling process (parent
process)."

now if vfork() is different because it can share, and that is the only
difference, i think it's pretty clear that fork() cannot share.

And neither fork() nor vfork() is standard C. You can discuss this in
comp.unix.programmer if you like.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,073
Latest member
DarinCeden

Latest Threads

Top