首先我们看以下的程序段1:
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <unistd.h> 4 5 int main(int argc, char* argv[]) { 6 int i = 0; 7 for (i = 0; i < 2; i++) { 8 fork(); 9 printf("-"); 10 } 11 }
这段程序会输出多少个“-”呢?
(一) 这里首先列出关于Linux中进程的必备知识:
1.进程可以看做是程序的一次执行,在Linux下,每个进程有唯一的PID标识。PID是从1到32768的整数。
其中PID=1的进程是init,其他进程从2依次编号,当用完32768后,从2重新编号。
2.Linux中有一个叫做进程表的结构存储当前正在运行的进程,可以使用命令"ps aux"查看当前系统的进程。
进程表的每一个表项,记录的是当前操作系统中一个进程的情况。对于单CPU而言,每个时刻只有一个进程
占用CPU,但是系统中可能存在多个等待执行的进程和阻塞进程。
当分给某个进程的CPU时间用完,操作系统将该进程相关寄存器的值保存在该进程在进程表中的相应表项
里面,并同时要把下一个占用CPU的进程的上下文从进程表中读出,更新相应的寄存器,此过程称为上下文
转换。此外还有一点就是程序计数器(Program Counter),它将指出当前进程已经执行到那个指令,是进程
上下文的主要内容,换出CPU的进程要保存该寄存器的值,换入CPU的进程,要从进程表中保存的进程上下文
信息中读出该寄存器的值,并更新PC的值。
3.进程在Linux中呈现出树状结构,init为根节点,其他进程均有父进程,某进程的父进程就是创建这个进程
的进程,被创建的进程成为该父进程的子进程。
4.fork()函数的有趣之处在于,它被调用一次,却能返回两次,并且有一下三种可能的返回值:
1) 在父进程中,fork()返回新创建进程的ID.
2)在子进程中,fork()返回0.
3)如果调用fork()出现错误则返回一个负值.
操作系统创建一个新的进程,并在进程表中为其新建一个表项。子进程是父进程的副本,它将获得
父进程数据空间、堆、栈等资源的拷贝。父子进程之间不共享这些数据,但共享代码空间。此时程序计数器PC在父子
进程中都声称。
(二) 几个关于fork()函数的示例
进程创建成功后,系统中出现两个基本完全相同的进程,这两个进程执行没有固定的先后顺序,
那个进程先执行,要看系统的进程调度策略。每个进程都有一个互不相同的进程标示符(Process ID),
可以通过函数getpid()获得,还有一个能返回父进程进程标示符(Process ID)的函数getppid()。
1 #include <iostream> 2 #include <unistd.h> 3 4 int main(int argc, char* argv[]) { 5 pid_t id = 0; 6 int count = 0; 7 id = fork(); 8 if (id < 0) { 9 std::cout << "Error: return code from fork() is: " << id << std::endl; 10 } else if (id == 0) { 11 std::cout << "I am a child process, my process id is: " << 12 getpid() << std::endl; 13 std::cout << "I am a child process, but my parent process id is: " << getppid() << std::endl; 14 count++; 15 } else { 16 std::cout << "I am a parent process, my process id is: " << getpid() << std::endl; 17 std:;cout << "I am a parent process, but my parent process is: " << getppid() << std::endl; 18 count++; 19 } 20 std::cout << "Tha count's value is: " << count << std::endl; 21 return 0; 22 }
该程序的执行结果为:
10:42 386% ./test
I am a parent process, my process id is: 2916
I am a parent process, my parent process id is: 2839
The count value is: 1
I am a child process, my process id is: 2917
I am a child process, my parent process is: 1
The count value is: 1
从上述结果我们可以看出有两个进程在执行,父进程的pid为2916,父进程的父进程pid为2839;父进程创建的子进程pid为2917,但是
这里子进程的父进程pid却为1,而不是2916,甚是奇怪?如果pid为1的话,那应该是init创建了该子进程?
但是有一点可以看出,变量count的值为1,因为这两个进程有独立的数据栈,相互之间不会干扰。
再看一个关于fork()函数的示例:
1 #include <iostream> 2 #include <unistd.h> 3 4 int main(int argc, char* argv[]) { 5 pid_t id = 0; 6 for (int i = 0; i < 2; i++) { 7 id = fork(); 8 if (id == 0) { 9 std::cout << "The " << i << " Iteration...." << std::endl; 10 std::cout << "I am a child,my process id is: " << getpid() << std::endl; 11 std::cout << "I am a child,but my parent id is: " << getppid() << std::endl; 12 } else { 13 std::cout << "The " << i << " Iteration...." << std::endl; 14 std::cout << "I am a parent process, my process id is: " << getpid() << std::endl; 15 std::cout << "I am a parent process, my parent process id is: " << getppid() << std::endl; 16 } 17 } 18 return 0; 19 }
该程序的执行结果为:
12:18 394% ./test
The 0 Iteration....
I am a parent process, my process id is: 2990
I am a parent process, my parent process id is: 2839
The 0 Iteration....
I am a child,my process id is: 2991
I am a child,but my parent id is: 2990
The 1 Iteration....
I am a parent process, my process id is: 2990
I am a parent process, my parent process id is: 2839
The 1 Iteration....
I am a child,my process id is: 2992
I am a child,but my parent id is: 2990
The 1 Iteration....
I am a parent process, my process id is: 2991
I am a parent process, my parent process id is: 2990
The 1 Iteration....
I am a child,my process id is: 2993
I am a child,but my parent id is: 2991
分析上述代码的执行流程:
i = 0 i = 1
parent
parent
child2
parent(事实上为child1)
child1
child3
1)第一次迭代的情况(i=0)
父进程调用fork()创建了一个子进程,其中父进程的Process ID为:2990,子进程的PID为:2991,并且
我们可以从以下的执行结果:
The 0 Iteration....
I am a child,my process id is: 2991
I am a child,but my parent id is: 2990
看出确实该子进程为PID:2990所创建的。因此有这样的关系图谱:PID:2990——>PID:2991
2)第二次迭代的情况(i=1)
此时PID:2990的进程继续执行此次迭代,在遇到fork()函数时再次创建一个子进程,有如下的执行结果:
The 1 Iteration....
I am a child,my process id is: 2992
I am a child,but my parent id is: 2990
从中我们可以看出,由PID:2990的进程所创建子进程的PID:2992.图谱关系:PID:2990——>PID:2992.
同时在第一次迭代过程中创建的子进程PID:2991也要执行此次循环,当它遇到fork()函数时,也要创建一个
子进程,从执行结果可以看出:
The 1 Iteration....
I am a child,my process id is: 2993
I am a child,but my parent id is: 2991
从中可以看出,PID:2991所创建的子进程PID:2993。图谱关系为:PID:2991——>PID:2993。
综上所述,可以有两条很清晰的进程关系:
PID:2990——>PID:2991——>PID:2993
PID:2990——>PID:2992
关于本文开头的那个示例,我会在下篇接着分析。