fork()-ing questions

M

Monty

In researching the Perl fork() command, I've come across many examples
of code and have been able to ascertain that when fork() is called,
the parent process retains it's own PID wil the child process takes a
PID of 0, at least in relation to the parent process.
This is pretty simple to see and understand, except for the
understanding that the process invoking fork() creates a clone of
itself, in which case the code examples I see leave one thing
unexplained. Take for instance the basic fork() routine similar to
what I've found on the web:

$pid = fork();

if ($pid == 0) {
# Then I'm the child process
} else {
# I'm the parent process
}

Programmatically correct considerations aside (I know I should include
an exit for the child process), I'm wondering--if the child process is
a clone of the parent--why the child process doesn't execute the "$pid
= fork()" statement. Is there some limitation to the amount of code
that gets "cloned"?

Secondly, I see a code snippet out there that confuses me and I'd just
like clarification of what it's saying. The following line:

unless ($pid = fork()) {
# Do something as the child process
}

'unless' is, as I understand it, used to loop when a condition is
false. False, in Perl, is the value 0 (along with undef and the empty
string). What is this the result of this fork() and it's effect on
the 'unless' statement? If the fork() produces a child process, then
doesn't the result of the entire '$pid = fork()' statement produce a
true value, as in the call to fork() executed correctly? If it did,
then doesn't the 'unless' statement test as true, meaning the loop
code gets bypassed?

It's all very confusing. Any clarification on this would be
appreciated.
 
D

Darren Dunham

Monty said:
In researching the Perl fork() command, I've come across many examples
of code and have been able to ascertain that when fork() is called,
the parent process retains it's own PID wil the child process takes a
PID of 0, at least in relation to the parent process.

Huh? No.

fork() is called once, but returns twice (in two separate processes).

The parent process has the same PID as the calling process. In that
process, the return code of fork() is the PID of the other process (the
child).

In the child process, a new PID is created and the return code of the
fork call is 0.

#!/usr/bin/perl
use warnings;
use strict;
print "Before fork, PID = $$\n";
my $code = fork;
print "After fork, PID = $$, fork returned $code.\n";

$ perl /tmp/fork.pl
Before fork, PID = 26341
After fork, PID = 26341, fork returned 26342.
After fork, PID = 26342, fork returned 0.
This is pretty simple to see and understand, except for the
understanding that the process invoking fork() creates a clone of
itself, in which case the code examples I see leave one thing
unexplained. Take for instance the basic fork() routine similar to
what I've found on the web:
$pid = fork();
if ($pid == 0) {
# Then I'm the child process
} else {
# I'm the parent process
}
Programmatically correct considerations aside (I know I should include
an exit for the child process), I'm wondering--if the child process is
a clone of the parent--why the child process doesn't execute the "$pid
= fork()" statement. Is there some limitation to the amount of code
that gets "cloned"?

No. The return of fork() is different in the two processes. $pid is
not the PID of the process, it's the result of the fork() call.
Secondly, I see a code snippet out there that confuses me and I'd just
like clarification of what it's saying. The following line:
unless ($pid = fork()) {
# Do something as the child process
}
'unless' is, as I understand it, used to loop when a condition is
false. False, in Perl, is the value 0 (along with undef and the empty
string). What is this the result of this fork() and it's effect on
the 'unless' statement? If the fork() produces a child process, then
doesn't the result of the entire '$pid = fork()' statement produce a
true value, as in the call to fork() executed correctly? If it did,
then doesn't the 'unless' statement test as true, meaning the loop
code gets bypassed?

No. This is all in the first sentence of fork.

$ perldoc -f fork
fork Does a fork(2) system call to create a new process running the
same program at the same point. It returns the child pid to
the parent process, 0 to the child process, or "undef" if the
fork is unsuccessful.
It's all very confusing. Any clarification on this would be
appreciated.

Reread the fork documentation in perldoc and understand the difference
between the return value of a function and the PID of a process ($$).
 
M

Monty

Huh? No.

fork() is called once, but returns twice (in two separate processes).

The parent process has the same PID as the calling process. In that
process, the return code of fork() is the PID of the other process (the
child).

In the child process, a new PID is created and the return code of the
fork call is 0.

#!/usr/bin/perl
use warnings;
use strict;
print "Before fork, PID = $$\n";
my $code = fork;
print "After fork, PID = $$, fork returned $code.\n";

$ perl /tmp/fork.pl
Before fork, PID = 26341
After fork, PID = 26341, fork returned 26342.
After fork, PID = 26342, fork returned 0.






No. The return of fork() is different in the two processes. $pid is
not the PID of the process, it's the result of the fork() call.


No. This is all in the first sentence of fork.

$ perldoc -f fork
fork Does a fork(2) system call to create a new process running the
same program at the same point. It returns the child pid to
the parent process, 0 to the child process, or "undef" if the
fork is unsuccessful.


Reread the fork documentation in perldoc and understand the difference
between the return value of a function and the PID of a process ($$).

--
Darren Dunham (e-mail address removed)
Senior Technical Consultant TAOS http://www.taos.com/
Got some Dr Pepper? San Francisco, CA bay area
< This line left intentionally blank to confuse you. >- Hide quoted text -

- Show quoted text -

Ok, thanks. This is getting somewhat clearer. I wasn't aware of the
perldoc command and have been using the web to answer questions. That
first statement in the fork() docs helps.

My second question is still foggy, and I may not have asked it
correctly. Considering the statment 'unless ($pid = fork())', if the
fork is successful, then the entire '$pid = fork()' statement is
evaluated as true and the loop code gets skipped--which is ok in the
parent process. Then, if the perldoc is to be taken at its word, the
child process that gets forked only consists of the code following the
fork() statement, which in this case is the loop code. I see the
benefit of coding exits in child process code.

How am I doing so far?
 
A

anno4000

Monty said:
In researching the Perl fork() command,

Did that research include reading "perldoc -f fork"? That would
be the starting point of any research.
I've come across many examples
of code and have been able to ascertain that when fork() is called,
the parent process retains it's own PID

correct, so far.
wil the child process takes a
PID of 0, at least in relation to the parent process.

Where did you get that? The child gets its own PID, which fork
returns to the parent. fork() returns 0 to the child (that's how the
code can *tell* it is the child code). Otherwise, the child can
access its own pid via $$, it doesn't have to be told by fork().

There is no such thing as one PID "in relation" to another. PIDs
are integers, which are system-wide unique at any time, that's
their purpose.
This is pretty simple to see and understand, except for the
understanding that the process invoking fork() creates a clone of
itself, in which case the code examples I see leave one thing
unexplained. Take for instance the basic fork() routine similar to
what I've found on the web:

$pid = fork();

if ($pid == 0) {
# Then I'm the child process
} else {
# I'm the parent process
}

Programmatically correct considerations aside (I know I should include
an exit for the child process), I'm wondering--if the child process is
a clone of the parent--why the child process doesn't execute the "$pid
= fork()" statement.

Because there is no child process when fork() is executed. The
parent process runs it for both. fork() *returns* twice, once to
the parent and once to the child, with different return values.
Is there some limitation to the amount of code
that gets "cloned"?

No, everything is cloned, but so is the point of execution. That
it, both continue immediately after fork() returns. Then they must
decide who is who.
Secondly, I see a code snippet out there that confuses me and I'd just
like clarification of what it's saying. The following line:

unless ($pid = fork()) {
# Do something as the child process
}

'unless' is, as I understand it, used to loop when a condition is
false. False, in Perl, is the value 0 (along with undef and the empty
string). What is this the result of this fork() and it's effect on
the 'unless' statement? If the fork() produces a child process, then
doesn't the result of the entire '$pid = fork()' statement produce a
true value, as in the call to fork() executed correctly? If it did,
then doesn't the 'unless' statement test as true, meaning the loop
code gets bypassed?

I think something is wrong about your mental model of what happens
with fork(). If my comments above (firstly) plus the lecture of
"perldoc -f fork" (mostly) don't help you set it right, I wouldn't
know what else to say. If they do help you understand fork(), I
think you will be able to answer the questions in the last
paragraph yourself.

Anno
 
J

Jürgen Exner

Monty said:
Secondly, I see a code snippet out there that confuses me and I'd just
like clarification of what it's saying. The following line:

unless ($pid = fork()) {
# Do something as the child process
}

'unless' is, as I understand it, used to loop when a condition is
false. False, in Perl, is the value 0 (along with undef and the empty
string). What is this the result of this fork()

Depends. In the parent process it is the PID of the child and guaranteed to
be be different from 0. In the child process it is 0.
and it's effect on
the 'unless' statement?

Because only for the child process the condition ever evaluates to false the
body is only executed for the child process.
If the fork() produces a child process, then
doesn't the result of the entire '$pid = fork()' statement produce a
true value, as in the call to fork() executed correctly?

But that is not what fork() returns. Did you check the documentation of
fork().

jue
 
D

Darren Dunham

My second question is still foggy, and I may not have asked it
correctly. Considering the statment 'unless ($pid = fork())', if the
fork is successful, then the entire '$pid = fork()' statement is
evaluated as true and the loop code gets skipped--which is ok in the
parent process. Then, if the perldoc is to be taken at its word, the
child process that gets forked only consists of the code following the
fork() statement, which in this case is the loop code. I see the
benefit of coding exits in child process code.

Nope. Reread it again. fork() returns 0 to the child process. 0 is
not true. The unless gets the result of the assignment (0) in that case
and executes the conditional code.

That code is not testing for a successful fork (it would need to check
for definedness). Instead it is assuming a successful fork and checking
for parent or child.
 
M

Monty

[program using fork snipped]
My second question is still foggy, and I may not have asked it
correctly. Considering the statment 'unless ($pid = fork())', if the
fork is successful, then the entire '$pid = fork()' statement is
evaluated as true and the loop code gets skipped--which is ok in the
parent process. Then, if the perldoc is to be taken at its word, the
child process that gets forked only consists of the code following the
fork() statement, which in this case is the loop code. I see the
benefit of coding exits in child process code.
How am I doing so far?

You seem not to grasp that the call to fork(), if successful, will
return two values: one to the parent and one the child. The parent
value will be non-zero and therefore true, but the child's value will
be zero and false. Therefore, the code in the unless block will be only
executed by the child. Also, "unless" does not start a loop (you may be
thinking of "until"). It is a one-time conditional, equivalent to "if
not".

The "child code" consists of everything inside the "unless" block, plus
everything that follows that block unless and until the child executes
an exit. The "child code" is the _same_ as the "parent code". It is up
to your program to make each of them behave in an appropriate manner,
using the return value from fork() to distinguish between them.


----------------------------------------------------------

----------------------------------------------------------
color]

Believe it or not, I think I'm getting this. Thanks for to all for 1)
correcting me on understanding of 'unless'...it is not a loop
(somehow, I knew that, but I kept seeing a loop structure for some
reason), and 2) pointing me to perldoc (I've got those priorities
backwards, but I hope you get the drift).

I don't mean to belabor the point, but allow me to interpret that
whole 'unless' structure, one last time.

unless ($pid = fork()) {
# Child process code here
exit(0);
}
# Parent process code here

fork() spawns a new process consisting of code following the 'unless'
statement. The $pid variable gets populated with the child PID for
the parent version of $pid and populated with 0 for the child
(spawned) version of $pid (I thought I read that variables get copied
in a forked process). Since the fork was successful (assume success,
failure is a whole different matter), the statement '$pid = fork'
returns a non-zero value and is evaluated by the 'unless' statement as
true and does not execute the child process code. Terrific, that's
what was wanted. The child process executes the snippet of code
follwing the 'unless' statement until it hits the exit() statement. I
presume the child process attempts to return a value to the parent
process, but that's a matter better left for another conversation.

Without too much attention to detail, am I getting it?
 
J

Jürgen Exner

Monty said:
unless ($pid = fork()) {
# Child process code here
exit(0);
}
# Parent process code here

fork() spawns a new process consisting of code following the 'unless'
statement.

No. The spawned process consists of exactly the same code as the parent
process.
The $pid variable gets populated with the child PID for
the parent version of $pid and populated with 0 for the child
(spawned) version of $pid
Correct.

(I thought I read that variables get copied
in a forked process).

That is correct, too. However, those variables in the parent and child
process are totally independant of each other once the fork() is done. And
only at that moment does $pid is being assigned a value and this value
happens to be different for parent and child.
Since the fork was successful (assume success,
failure is a whole different matter), the statement '$pid = fork'
returns a non-zero value

Correct for the parent, wrong for the child
and is evaluated by the 'unless' statement as
true and does not execute the child process code. Terrific, that's
what was wanted. The child process executes the snippet of code
follwing the 'unless' statement until it hits the exit() statement. I

Correct.

presume the child process attempts to return a value to the parent
process, but that's a matter better left for another conversation.

???

jue
 
D

Darren Dunham

Monty said:
I don't mean to belabor the point, but allow me to interpret that
whole 'unless' structure, one last time.
unless ($pid = fork()) {
# Child process code here
exit(0);
}
# Parent process code here
fork() spawns a new process consisting of code following the 'unless'
statement.

I wouldn't say that. It spawns a new process that is identical (except
for the return of the fork call). So all code is duplicated, not just
the unless.
The $pid variable gets populated with the child PID for
the parent version of $pid and populated with 0 for the child
(spawned) version of $pid (I thought I read that variables get copied
in a forked process).

Variables are copied. Prior to the fork, the $pid wasn't assigned, so
it would have been 'undef'. So it is 'undef' in both (in some sense)
when the OS makes the copy. However immediately after the copy starts
running, it assigns the value of the fork() call to $pid.

So it is copied, but immediately overwritten afterward.
Since the fork was successful (assume success,
failure is a whole different matter), the statement '$pid = fork'
returns a non-zero value and is evaluated by the 'unless' statement as
true and does not execute the child process code.

$pid=fork returns a non-zero value if fork returns a non-zero value,
which is true in the parent only. This causes the parent to not execute
the code within the unless block. The child will execute the code in
the unless block.

While I do use unless in many situations in my code, it might be clearer
for you use additional statements and 'if' instead.

$pid = fork();
if ($pid == 0) {
# Child process code here
[...]

That might be clearer when you're trying to remember what fork returns
and reverse the sense of the if because it's an unless...
Terrific, that's
what was wanted. The child process executes the snippet of code
follwing the 'unless' statement until it hits the exit() statement. I
Right.

presume the child process attempts to return a value to the parent
process, but that's a matter better left for another conversation.

The child doesn't attempt to do anything that's not in code.

However, I suggest you examine the 'wait' function if you're interested
in simple messages from the child to the parent.
Without too much attention to detail, am I getting it?

Unfortunately, the computer is all about detail. :)
 
T

Tad McClellan

Darren Dunham said:
The child doesn't attempt to do anything that's not in code.

However, I suggest you examine the 'wait' function if you're interested
in simple messages from the child to the parent.


And see the perlipc.pod manpage if you're interested in
more than "simple" messages.
 
M

Monty

Many thanks to all that posted on this one. I believe I'm pretty
clear on how this works and have enjoyed the varyious methods of
approaching this understanding. I say this not only to express
appreiation, but to ask what I hope is ONE LAST QUESTION on this
matter, but I have the sinking feeling that there is no such thing as
a final question, but I'm going to post it anyway.

Here's where I'm at: consider the code which was presented earlier in
the post:

unless ($pid = fork()) {
# Do something as a child process
exit;
}
# Do something as the parent process

on the execution of the fork, the statement '$pid = fork' evaluates as
true--I'm presuming from having run it--in the parent process (again,
assume success). In the spawned child process...and here's the big
leap for me...the statement '$pid = fork' does not execute (since
another process is not spawned) and the original fork() call merely
provides a false value to the child process at the fork's point of
execution--in this case it also provides a 0 to the child's
$pid variable. The provided false value to the child process allows
the condition of the 'unless' statement to evaluate false and
therefore execute the 'unless' block of code.

Have I finally achieved enlightenment? :)
 
J

Jürgen Exner

Monty said:
unless ($pid = fork()) {
# Do something as a child process
exit;
}
# Do something as the parent process

on the execution of the fork, the statement '$pid = fork' evaluates as
true--I'm presuming from having run it--in the parent process (again,
assume success).

So far, so good.
In the spawned child process...and here's the big
leap for me...the statement '$pid = fork' does not execute

Wrong. You are correct, the fork() itself is executed only once (i.e. in the
parent process).
However the assignment $pid=[whatever] is the very first statement that is
executed in the child process, too.

Maybe you are confused as to the execution sequence. In an assignment
obviously you need to compute the value of the right-hand-side (RHS) first
before assigning the result to the a variable.
Therefore first the fork() is executed (only once), now all of a sudden you
got two individual processes, and in both processes the return value of
fork() is assigned to their respective $pid variable, and then both
processes continue execution.

jue
 
M

Monty

Let me rephrase my question...I don't think I'm asking something
correctly. Or, maybe I am and I'm just not understanding your answers.

My question probably has more to do with the evaluation of the true/
false condition in the 'unless' statement at this point. Assuming
$var = 1 is a successful assignment, then 'if ($var = 1)' would
evaluate as true (as would 'if ($var ==1)', but the point is that
there's an operation in the conditional test, and I'm thinking that
the success of that operation is what determines the truth/falseness
of the expression). If that's wrong, then correct me right here,
otherwise I'm assuming that the statement 'unless($pid = fork())' is
not evaluated on the value stored in $pid, but on whether or not the
operation of forking was successful. This is at the crux of my
understanding why the conditional test is true for the parent and
false for the child. It would be easier from a certain aspect if the
statement was 'unless($pid)', but that's not what's being done here.
 
A

anno4000

Monty said:
Let me rephrase my question...I don't think I'm asking something
correctly. Or, maybe I am and I'm just not understanding your answers.

My question probably has more to do with the evaluation of the true/
false condition in the 'unless' statement at this point. Assuming
$var = 1 is a successful assignment, then 'if ($var = 1)' would
evaluate as true (as would 'if ($var ==1)', but the point is that
there's an operation in the conditional test, and I'm thinking that
the success of that operation is what determines the truth/falseness
of the expression). If that's wrong, then correct me right here,

Well, it *is* wrong. To begin with, your terminology is off the
mark. 'if ($var = 1)' doesn't evaluate to anything, it isn't even
a statement. The complete "if ( ... ) { ... }" statement might
evaluate to something, but it doesn't either. Like loops, if has
no value.

That aside, the success of an operation doesn't necessarily determine
its value. Many system calls, like open(), mkdir(), and so on, do
that. fork(), however, and some others, behaves differently.

You have seen that on success fork() returns different values to
the parent and the child, the child's PID (true) to the parent and
0 (false, but a defined value) to the child. On failure, fork()
returns an undefined value (only to the parent, the child wasn't
created). That is how it is described in the pertinent perldoc,
and only that counts. Conclusions by analogy with "similar"
functions are misleading.
otherwise I'm assuming that the statement 'unless($pid = fork())' is
not evaluated on the value stored in $pid, but on whether or not the
operation of forking was successful.

Again, no. I fail to see how this is a contrast to your first
assumption.
This is at the crux of my
understanding why the conditional test is true for the parent and
false for the child. It would be easier from a certain aspect if the
statement was 'unless($pid)', but that's not what's being done here.

The code

my $pid = fork();
unless ( $pid ) {
# child code
exit;
}
# and so on

would be exactly equivalent.

Anno
 
M

Monty

I see the error in my thinking. Somewhere along the lines, between
teaching myself Perl, C++, Tcl/Tk, Ada, and various flavors of shells,
I ran across a piece of knowledge that said executable statements
could be put in place of conditional tests and the truth/falseness
would be based on the success of that executable statement. I was
erroneous in thinking that Perl did this and that's behind my
confusion over the 'unless' statement. I found this out by running a
bit of test code:

'if ($pid = 0)' evaluates to false and 'if ($pid = 1)' evaluates to
true

'if ({$pid = 0})' evaluates to true, as does 'if ({$pid = 1})'

I assumed the second form of the if statement was Perl behavior.

That clears things up considerably.

I'm going to end this discussion right here. Thanks again to all!
 
U

Uri Guttman

M> 'if ($pid = 0)' evaluates to false and 'if ($pid = 1)' evaluates to
M> true

M> 'if ({$pid = 0})' evaluates to true, as does 'if ({$pid = 1})'

why are the {} in there? those are anon hashes which you don't want
(they will always be true and warnings will point out the odd number of
initializers). where did you get the idea that {} is needed there?

M> I assumed the second form of the if statement was Perl behavior.

huh?

perl has a very simple rule about expressions. all expressions have a
value. simple. that value can be used in many ways and contexts. if()
provides a boolean (which is also scalar) context on the expression
inside (). that is ALL that happens. never assume from other langs what
perl does. perl likely does what you want and in a simpler way.

M> That clears things up considerably.

not for me. you still seem to not understand perl's expressions. fork
is just a function that returns a value so it is an expression. but it
happens to have the most powerful side effect of any perl call as it
creates a new copy of that process for you. and it is not a perl thing
but an OS thing that perl supports directly.

uri
 
J

Jürgen Exner

Monty said:
Let me rephrase my question...I don't think I'm asking something
correctly. Or, maybe I am and I'm just not understanding your answers.

My question probably has more to do with the evaluation of the true/
false condition in the 'unless' statement at this point. Assuming
$var = 1 is a successful assignment, then 'if ($var = 1)' would
evaluate as true (as would 'if ($var ==1)', but the point is that
there's an operation in the conditional test,

Well, right. I assume with operation you mean the assignment?
and I'm thinking that
the success of that operation is what determines the truth/falseness
of the expression).
Wrong.

If that's wrong, then correct me right here,

The operation has a return value. For some operations that return value may
indicate success or failure. For others like e.g. + or = or s/// or many,
many others the return value indicates something totally different.
The assignment operator return the value it was assigning to the variable.
Unfortunately that is not clearly spelled out in perldoc perlop.

jue
otherwise I'm assuming that the statement 'unless($pid = fork())' is
not evaluated on the value stored in $pid, but on whether or not the
operation of forking was successful.

Neither nor. The conditional is evaluated on the return value of the
assignment, which happens to be the return value of fork(), which happens to
be different for parent and child.
For the sake of the conditional your code is equivalent to

unless (fork()) {
# child code
exit;
}

The only reason for the explicit assignment of the return value of fork() to
a variable is that later on the parent process can use that PID to identify
its own children by their process IDs.

jue
 
T

Tad McClellan

Monty said:
I see the error in my thinking. Somewhere along the lines, between
teaching myself Perl, C++, Tcl/Tk, Ada, and various flavors of shells,
I ran across a piece of knowledge that said executable statements
could be put in place of conditional tests and the truth/falseness
would be based on the success of that executable statement. I was
erroneous in thinking that Perl did this and that's behind my
confusion over the 'unless' statement. I found this out by running a
bit of test code:

'if ($pid = 0)' evaluates to false and 'if ($pid = 1)' evaluates to
true


No, no, no. Rather:

'if ($pid = 0)' does not execute its block, and 'if ($pid = 1)'
does execute its block.

(an if statement does not "evaluate" to anything, because it
is not an expression.)

or

'$pid = 0' evaluates to false and '$pid = 1 evaluates to true

(because assignment in scalar context evaluates to whatever is
on the RHS of the assignment.)
'if ({$pid = 0})' evaluates to true, as does 'if ({$pid = 1})'


The curlys return a reference to a hash, and references
are always a true value.

That clears things up considerably.


But it doesn't.

I'm going to end this discussion right here.


Don't stop yet, unless you got the part of Uri's and Michele's
posts about the value of an assignment statement in scalar context.
 
M

Monty

It would appear that I may have stepped on toes with my process of
learning here. I had a particular goal in mind when I first posted
this question and unfortunately, that goal wasn't clear until it was
achieved: 1) a working understanding of what fork() does, and 2)
clarification regarding the evaluation of assignment statements in a
conditional context. Even though that second one may not be complete,
I have a functional understanding, thanks entirely to this group, of
the particulars, pitfalls, and benefits.

I probably committed a breech of protocol by not posting snippets of
discussion that led me to conclusions that I, in turn, posted. My
apologies, but what I've learned has been gleaned over the course of
discussion by osmosis and 'nuance' that I got from the discussion, and
I felt that my focus on the goal would not have been served, in fact
debilitated, by detailed examination of my path though all of this.

I've accomplished what I wanted here, and even if I didn't learn
exactly what you were trying to teach me, I learned enough to write my
own tests for aspects I was unclear on, and I have a better, more
functional knowledge of what's going on.

So please accept my thanks graciously. I realize my knowledge is
incomplete, but for this task, it's what I need to know. And without
the help of this group, I never would have gotten there.
 

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,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top