有意思的进程创建函数fork()的问题

时间:2022-11-20 14:21:34

在做某个公司的笔试题的时候遇到了这么一个问题,描述如下:

如下代码会输出多少个"-"字符?

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>

int main()
{
	int i;
	for(i=0;i<2;i++)
	{
		fork();
		printf("-\n");
	}
	return 0;
}

在这里只做一个引子,下面稍微介绍一下fork()然后再解决这个问题。


fork()函数

#include<unistd.h>

/*返回值:子进程中返回0,父进程中返回子进程ID,出错返回-1*/
pid_t fork(void);
由fork创建的新进程被称为子进程(child process)。fork函数被调用一次,但返回两次。子进程的返回值是0,而父进程的返回值则是新进程的进程ID。 将子进程ID返回给父进程的理由是:因为一个进程的子进程可以有多个,并且没有一个函数使一个进程可以获得其所有子进程的进程ID。 fork使子进程得到返回值0的理由是:一个进程只会有一个父进程,所以子进程总是可以调用getpid以获得其父进程的进程ID。

使fork失败的两个主要原因是:系统中已经有了太多的进程,或者该实际用户ID的进程总数超过了系统限制。

fork有下面两种用法:

(1)一个父进程希望复制自己,使父、子进程同时执行不同的代码段。这在网络服务进程中是常见的--父进程等待客户端的服务请求。当这种请求到达时,父进程调用fork,使子进程处理此请求。父进程则继续等待下一个服务请求到达。

(2)一个进程要执行一个不同的程序。这对shell是常见的情况。子进程从fork返回后立即调用exec。

归结起来说就是是实现多线程。C语言多线程实现需要自己控制来实现,这个比Java要复杂。


注意:fork确实创建了一个子进程并完全复制父进程,但是子进程是从fork后面那个指令开始执行的。对于原因也很合乎逻辑,如果子进程也从main开头到尾执行所有指令,那么它执行到fork指令时也必定会创建一个子子进程,子子孙孙无穷尽也,如此下去,这个小小的程序就可以创建无数多个进程可以把你的电脑搞瘫痪,所以fork作者肯定不会这么做。

在此,我们还是举一个最经典的fork的代码示例:

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>

int main()
{
	pid_t pid = fork();//程序从这里开始分岔,父子进程都从这一句开始执行一次

	if(pid > 0)
	{
		sleep(2);
		printf( "this is parent process:process id is %d\n",getpid() );
	}
	else if(pid==0)
	{
		printf( "this is child process:process id is %d\n",getpid() );
	}
	else
	{
		printf("error!\n");
	}

	return 0;
}

结果:

有意思的进程创建函数fork()的问题


现在回到开头的程序上,到底输出多少个"-"?原来的代码将循环解析一下成为如下格式:

int main()
{
	fork();
	printf("-\n");
	fork();
        printf("-\n");
	return 0;
}

分析:首先父进程A执行第一个fork()函数,然后生成了一个子进程B,此时A和B同时也就是并发执行,都执行printf("-");先输出了两个"-",然后进程A和进程B由都执行fork()函数,因此A又创建了子进程C,B创建了子进程D,现在一共有四个进程ABCD,然后同时执行下面的printf("-");,打印四次,输出四个"-",一共输出6个"-"。

如下图:

有意思的进程创建函数fork()的问题

可见,一共执行了6次printf("-")。


OK,我们知道了这个之后再来看一道有意思的问题:

#include<stdio.h>
int main()
{
  fork();
  fork()&&fork()||fork();
  fork();
}
请问有多少个进程?

分析:解决这个问题主要注意三点:1.子进程是从fork后面那个指令开始执行的;2.fork父子进程的不同返回值;3对于"a&&b"如果a为0,那么不会再执行b。

用语言来描述实在是太复杂,上一张,希望读者能够看懂:

有意思的进程创建函数fork()的问题


最后的进程总数看有多少个节子点就行了。很明显一共是20个。关键点就是在树的第三层,C和D都是子进程,那么它俩返回的值都是0,因此"&&"后面的表达式就不会执行,之后执行最后一个fork()函数。但是A和B两个父进程返回值大于0,因此会执行后面的"fork()||fork()"。分析结束,如果有问题,可以留言。


参考资料:

《UNIX高级环境编程》


转载请注明出处:http://blog.csdn.net/lavorange/article/details/38961247