1 | #include <unistd.h>; |
2 | #include <sys/types.h>; |
3 | |
4 | main () |
5 | { |
6 | pid_t pid; |
7 | pid=fork(); |
8 | |
9 | if (pid < 0) |
10 | printf("error in fork!"); |
11 | else if (pid == 0) |
12 | printf("i am the child process, my process id is %d\n",getpid()); |
13 | else |
14 | printf("i am the parent process, my process id is %d\n",getpid()); |
15 | } |
16 | |
17 |
linux中的fork的网上的讲解都是用这个程序作为例子,自己也就转过来了,然后半抄袭半体会的写点自己的认识。
-
一个可执行的程序
-
进程相关的全部数据(包括变量、内存变量、缓冲区等等)
-
程序的执行上下文(exection text)
一个进程可以理解为一个可执行程序的一次执行过程中的一个状态。操作系统对进程的管理是通过进程表完成的。进程表中的每一个表项,记录的是当前系统中的一个进程的相关信息。对于单个CPU的情况,每一个特定的时刻只有一个进程可以占用CPU,但是系统中可能同时存在多个活动的进程,CPU通过不同的调度算法实现不同进程的调用,分时占用CPU执行。
CPU通过一个称为程序计数器(Program counter,pc)的寄存器,指出当前占用CPU的进程要执行的下一条指令的位置。
当分配给某个进程的CPU时间已经用完,操作系统将该进程相关的寄存器的值保存到该进程在进程表中对应的表项中;把将要接替这个进程占用CPU的那个进程的上下文从进程表中读出,并更新相应的寄存器,这个过程称为上下文交换(Process context switch)。
而对于fork进程,当执行到pid=fork();
操作系统创建一个新的进程(子进程),并且在进程表中相应的为他创建一个新的表项。新进程和原有的进程拥有相同的可执行程序,上下文和相关数据,但是他们是两个相互独立的进程。此时程序寄存器PC在父子进程中都指向fork返回,但是如何返回在父子进程中就发生分歧。
在子进程的fork中,copy父进程的相关信息时,eax寄存器没有copy,而是设置成0,而eax就是子进程的返回值。因此子进程中fork返回0;
在父进程的fork中,eax是子进程的进程号,所以,父进程中的fork返回值就是子进程的进程号。
因此,父进程继续执行,pid满足>0,输出i am the parent process
子进程根据调度算法在某个时候得到调用(不同于vfork,vfork指定先执行子进程),他的上下文被换入,占据CPU。而在子进程中fork返回0,在这个进程继续执行的过程中满足pid=0,输出i am the child process。
这时候,子进程虽然与父进程拥有相同的可执行程序,但是这是程序的另外一次执行,从执行的角度和父进程相互独立。
1 | [root@localhost c]# ./a.out |
2 | i am the child process, my process id is 4286 |
3 | i am the parent process, my process id is 4285 |
最终我们看到两次输出,程序中互斥的两个分支都被执行了,在一个程序的一次执行中,这是不可能的,但是我们看到的两行输出时来自于父子两个进程,这两个进程来自于一个程序的两次执行。