ps allows you to see
this relationship: the PID column is the process
ID, and PPID is the process's parent's ID.
The system call
pid_t getppid(void);
gets the process id of a process's parent.
A process creates a child process (thereby becoming a
parent) via the system call fork.
NAME
fork - create a new process
SYNOPSIS
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
Try man -s2 fork for gory details.
The concept behind fork is perhaps not what you expect.
First of all, if the call to fork fails, -1 is returned.
With that out of the way, here's the weirdness.
When a process calls fork, the kernel creates a new process
that is a clone of the original, and both processes
simply continue executing at the instruction following the
call to fork. The child process is an exact duplicate of the
clone save for one thing: the value returned by fork in the
child process is zero, in the parent process it is the process
id of the child.
#include <stdio.h>
#include <unistd.h>
int main()
{
pid_t t = fork();
if (t == -1) { printf("fork failed!\n"); exit(1); }
if (t == 0)
printf("I am the child. My pid is %i.\n",getpid());
else
printf("I am the parent. My pid is %i.\n",getpid());
return 0;
}
This simple example gives the clue to how this strange concept
of fork really works. After forking, both processes check the
return value, determine whether they are child or parent, and
work accordingly. The fact that both processes are identical
copies of one another (except for fork's return value) has
interesting consequences. One consequence is that their file
descriptors refer to exactly the same system open file table
entries. That means that they share not just the same files,
but the same connections ... so when one reads a bit,
it changes the offset so that when the next reads, it starts
where the first left off.
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char **argv)
{
int fd = open(argv[1],O_RDONLY);
pid_t cid;
if ((cid = fork()) == -1) { fprintf(stderr,"Fork failed!\n"); return 1; }
if (cid == 0) { sleep(1); } // forces alternation, without it both proc's
// may read same char, b/c read is not atomic
char c;
while(read(fd,&c,1) > 0)
{
if (cid == 0)
printf("%c <-- child\n",c);
else
printf(" %c <-- parent\n",c);
sleep(2);
}
return 0;
}
Running this program, we see that the child and parent
processes are not just reading from the same file, but
actually from the same connection to that file. This means
that it isn't just the vnode that's the same, but the system
open file table entry.
bash$ gcc ssofte.c
bash$ ./a.out data.txt
H <-- parent
e <-- child
l <-- parent
l <-- child
o <-- parent
<-- child
W <-- parent
o <-- child
r <-- parent
l <-- child
d <-- parent
. <-- child
<-- parent
It's interesting to modify this program and see what happens.
For example, what happens if we make the call to fopen
after the call to fork? Can you explain
what happens?
wait does that for us.
Interesting uses of this will be easier to find when we have a
few more tools at our disposal (notably exec,
which is covered next class). However, we can see wait get
used in a simple example.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main( int argc, char *argv[] )
{
pid_t pid;
pid = fork();
if( pid == -1 ) exit( 1 );
if( pid == 0 )
{
printf("I am a child.\n");
exit(2);
}
else
{
int status;
wait(&status);
printf("My child exited with (%i).\n", WEXITSTATUS(status));
exit(0);
}
}
Below is a very simple program to compute discrete logarithms, that tests all numbers in the range 1..n to find the one (if it exists) that is logby mod n. Next to it is another version that forks and, following the fork, lets the parent search in the range 1..n/2 and the child search in the range n/2..n. On a multi-core machine, the two processes may well be assigned different CPUs so that they can actually run in parallel. Indeed, if you test the two programs on input b = 3811, y = 7279 and n = 29917 on one of the lab machines, you should see the forking version taking about half the time!
| mlog1.c | mlog2.c |
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
// Find log[x](y) modulo n, i.e. k such that x^k mod n = y
// Try: mlog1 3811 7279 29917
// x y n ---> 19863
int main(int argc, char **argv)
{
int x = atoi(argv[1]), y = atoi(argv[2]), n = atoi(argv[3]);
int k, a, b, i, z;
a = 1; b = n;
for(k = a; k < b; ++k)
{
// compute z, where z = x^k mod n
z = 1;
for(i = 0; i < k; ++i)
z = z*x % n;
// if z == y, we've found log[x](y) mod n.
if (z == y) { printf ("%i\n",k); return 0; }
}
}
|
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
// Find log[x](y) modulo n, i.e. k such that x^k mod n = y
// Try: mlog2 3811 7279 29917
// x y n ---> 19863
int main(int argc, char **argv)
{
int x = atoi(argv[1]), y = atoi(argv[2]), n = atoi(argv[3]);
int k, a, b, i, z;
// Parent and child split range 1...n into two
if (fork()){ a = 1; b = n/2; }
else { a = n/2; b = n; }
for(k = a; k < b; ++k)
{
// compute z, where z = x^k mod n
z = 1;
for(i = 0; i < k; ++i)
z = z*x % n;
// if z == y, we've found log[x](y) mod n.
if (z == y) { printf ("%i\n",k); return 0; }
}
}
|