首先,fork() 函数的主要功能通俗的说是创建一个子进程。这2个进程是相互独立的两个进制,它们共享的内容只有代码段。这时候就要提到我们之前学到 的一个知识点——内存四区了,在栈,堆,数据段和代码段这四个大块中,它们父子俩的前三个是完全区分开的,像这样一个函数:
int main()
{
int count = 0;
pid_t pid = fork();
count++;
printf ("count = %d\n", count);
return 0;
}
它的答案就是两个1,而不是1和2 。
另外就是父子进程的运行先后问题,它们的执行先后是不确定的(虽然笔者在red hat实验了很多次,都是子进程先执行)。
关于这个函数的返回值问题,因为它有2个进程,自然而然的它有也2个返回值,在父进程中,返回值为子进程的ID,在子进程中,返回值为0;我们可以使用getpid()函数来获取它们各自的ID号,如下:
int main()
{
pid_t pid = fork();
if (pid == -1)
{
perror ("fork");
return -1;
}
if (pid > 0) // 父进程
{
printf ("我是父进程,pid = %d\n", getpid());
}
else if (pid == 0) // 子进程
{
printf ("我是子进程,pid = %d\n", getpid());
}
return 0;
}
因为每调用一次fork() 函数就会产生一个相应的子进程,所以,很显然的,连续调用三次fork() 就能产生总共8个进程,这不难理解。但,不要觉得这很容易理解,我们来看一下下面这道题,判断一下会有 多少个a被打印出来:
int main()
{
fork();
fork() && fork() || fork();
fork();
printf("a\n");
return 0;
}
首先,我们很容易理解的是,第3行和第5行代码,就是进程的翻倍,主要就是看第四行代码的意思,我们假设主进程为1,在第三行代码执行结束时,它产生了子进程2,我们分开讨论,判断了1的执行结果翻倍就是总的进程数。
在第四行代码中:2进程在执行完第一个fork() 函数后产生了它的子进程3,2进程的返回值为3进程的ID(一个大于0的正整数),所以就执行&&操作的后半部分第二个fork() 函数,产生子进程4,返回值为4的ID,所以就不执行 || 后面的操作了,到此,2进程结束; 再回来看它的子进程3,它作为子进程,返回值为0,所以&&的后半部分是执行不了了,但&&完之后的结果就是0,可以进行接下来的 || 操作,于是它又产生了一个子进程5;同样,2进程产生的另一个 子进程4和3进程是一个道理,也要执行||操作的后半部分,产生一个子进程6。
到此,第四行代码全部执行完毕,产生了包括2在内总共5个进程,于是最终的结果就是2*5*2 = 20个进程。
另外,与fork() 函数相类似的还有一个vfork() 函数, 它有这样一些特点:
1) vfork() 函数的子进程必须显式调用;
2)vfork() 函数的父子进程共享数据段;
3)vfork() 函数的子进程要先于父进程执行,即子进程运行结束,CPU才能调度到父进程。