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

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

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

#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;
}

结果:

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

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个"-"。

如下图:

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

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

#include<stdio.h>
int main()
{
  fork();
  fork()&&fork()||fork();
  fork();
}

请问有多少个进程?

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

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

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

参考资料:

《UNIX高级环境编程》

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

时间: 2024-11-06 14:52:52

有意思的进程创建函数fork()的问题的相关文章

进程创建函数fork

fork fork() 调用一次返回两次:父进程返回子进程pid,子进程返回0. 进程是树形结构,子进程可随时通过getppid()获取父进程: 而父进程只能在创建时获取子进程号. 子进程使用父进程堆栈及数据空间副本等存储空间.代码正文段及文件共享,例如文件偏移量. 存储空间多采用写时复制的方式Copy-On-Write.一开始共享存储空间,直到子进程试图修改某个区域时,进行复制.复制单位一般为页大小. 常见用法: 1.父进程希望子进程复制自己,后处理不同的代码段.如网络请求,父进程等待请求,后

Linux进程的创建函数fork()及其fork内核实现解析

进程的创建之fork() Linux系统下,进程可以调用fork函数来创建新的进程.调用进程为父进程,被创建的进程为子进程. fork函数的接口定义如下: #include <unistd.h> pid_t fork(void); 与普通函数不同,fork函数会返回两次.一般说来,创建两个完全相同的进程并没有太多的价值.大部分情况下,父子进程会执行不同的代码分支.fork函数的返回值就成了区分父子进程的关键.fork函数向子进程返回0,并将子进程的进程ID返给父进程.当然了,如果fork失败,

【归纳总结】Unix/linux下的进程管理(二):创建进程的函数及其应用、对比

创建进程的函数fork().vfork()和execl() 本次内容主要介绍Unix/linux下2个创建进程的函数fork和vfork以及它们的差别. 1.fork函数 (1)函数的格式 #include <unistd.h> pid_t fork(void); 函数功能: 主要用于以复制正在运行进程的方式来创建新的进程,其中新进程叫做子进程,正在运行的进程叫做父进程. 返回值: 函数调用成功时,父进程返回子进程的PID,子进程返回0,函数调用出错时,父进程返回-1,子进程没有被创建. 注意

进程创建fork函数的调试[1]

1 #include<stdio.h> 2 #include<sys/types.h> 3 #include<unistd.h> 4 #include<stdlib.h> 5 int main() 6 { 7 int count=0; 8 pid_t pid;//此时只有一个进程 9 pid=fork();//此时创建了两个进程 10 if(pid<0) 11 { 12 printf("error in fork!"); 13 ex

linux c学习笔记----进程创建(fork,wait,waitpid)

1.pid_t fork(); (1)当一个进程调用了fork 以后,系统会创建一个子进程.这个子进程和父进程不同的地方只有他的进程ID 和父进程ID,其他的都是一样.就象符进程克隆(clone)自己一样. (2)为了区分父进程和子进程,我们必须跟踪fork 的返回值. 当fork 掉用失败的时候(内存不足或者是用户的最大进程数已到)fork 返回-1,否则fork 的返回值有重要的作用.对于父进程fork 返回子进程的ID,而对于fork 子进程返回0.我 们就是根据这个返回值来区分父子进程的

UNIX环境编程学习笔记(19)——进程管理之fork 函数的深入学习

lienhua342014-10-07 在“进程控制三部曲”中,我们学习到了 fork 是三部曲的第一部,用于创建一个新进程.但是关于 fork 的更深入的一些的东西我们还没有涉及到,例如,fork 创建的新进程与调用进程之间的关系.父子进程的数据共享问题等.fork 是否可以无限制的调用?如果不行的话,最大限制是多少?另外,我们还将学习一个 fork 的变体 vfork. 1 fork 创建的新进程与调用进程之间的关系 UNIX 操作系统中的所有进程之间的关系呈现一个树形结构.除了进程 ID

模拟shell ( 进程函数:fork(),execvp(),wait() )

shell是一个管理进程和运行进程的程序,下面我们就通过模拟一个shell程序这个实例来更好地认识认识在Linux/Unix系统中,进程的创建和结束,以及父子进程之间的一些关系.接下来先贴上源代码的中命令的读取部分: numargs=0; while(numargs<MAXARGS){ printf("Arg[%d]?",numargs); if(fgets(argbuf,ARGLEN,stdin)&&*argbuf!='\n') arglist[numargs+

进程标识符与fork函数

ID为0的进程通常是调度进程,常被称为交换进程(swapper),是内核中的系统进程. ID为1的进程叫做init进程,是一个普通用户进程,不属于内核,由内核调用. 一个现有进程可以调用fork函数创建一个新进程(子进程).fork函数被调用一次,返回两次.子进程返回值为0,父进程返回值为子进程的进程ID. 当fork出一个子进程后,子进程便拥有独立的数据段.堆.栈的副本,但父.子进程共享正文段(关于程序分布见文章"C程序的存储空间布局").但现在很多实现并不完全复制数据段.堆.栈,开

【Linux编程】进程标识符与fork函数

ID为0的进程一般是调度进程.常被称为交换进程(swapper),是内核中的系统进程. ID为1的进程叫做init进程,是一个普通用户进程,不属于内核,由内核调用. 一个现有进程能够调用fork函数创建一个新进程(子进程).fork函数被调用一次.返回两次. 子进程返回值为0.父进程返回值为子进程的进程ID. 当fork出一个子进程后,子进程便拥有独立的数据段.堆.栈的副本,但父.子进程共享正文段(关于程序分布见文章"C程序的存储空间布局").但如今非常多实现并不全然复制数据段.堆.栈