码迷,mamicode.com
首页 > 系统相关 > 详细

小何讲进程: Linux进程控制编程 (fork、vfork)

时间:2016-04-30 06:41:55      阅读:325      评论:0      收藏:0      [点我收藏+]

标签:

所谓进程控制,就是系统使用一些具有特定功能的程序段来创建进程、撤消进程以及完成进程在各种状态之间的转换,

从而达到多进程高效率并发执行和协调资源共享的目的。进程控制是进程管理和处理机管理的一个重要任务。


1. fork()创建进程
在Linux系统中,除了系统启动之后的第一个进程(根进程)由系统来创建外,

其余所有进程都必须由已存在的进程来创建新创建的进程叫子进程,而创建子进程的进程叫父进程,

具有相同父进程的进程叫兄弟进程。

在Linux中创建一个新进程的方法是使用fork()函数。 
fork()函数用于根据已存在的进程以“复制”的方式创建一个子进程。


调用fork()时,fork()将父进程所有的资源通过数据结构的"复制"传给子进程:
让子进程与父进程使用同一代码段
将父进程的数据段和堆栈段复制一份给子进程,这样父进程的所有数据都可以留给子进程,

但父子进程的地址空间已经分开,不再共享任何数据,因此,相互之间不再有影响。

子进程的执行独立于父进程,父子进程的数据共享需要通过专门的通信机制。
采用“复制”的方法创建子进程控制块
把复制这个词加上引号,是因为这个复制并不是完全复制。因为父进程控制块中某些项的内容必须按照子进程的特点来修改,

如:进程的标识、状态等。另外,子进程控制块还必须有表示自己父进程的域及私有空间,如数据空间、用户堆栈等。


使用fork()函数得到的子进程是父进程的一个“复制品”,它从父进程处继承了整个进程的地址空间:
包括进程上下文、代码段、进程堆栈、内存信息、打开的文件描述符、信号控制设定、进程优先级、进程组号、

当前工作目录、根目录、资源限制和控制终端等,而子进程所独有的只有它的进程号、资源使用和计时器等少量信息。


技术分享



父、子两个进程运行同一个程序:

因为子进程几乎是父进程的完全复制,所以父子两个进程会运行同一个程序。

因此需要用一种方式来区分它们,否则,这两个进程不可能做不同的事。


子进程中,fork()返回0
父进程中,fork()返回子进程的ID(>0)

实际上是在父进程中执行fork()函数时,父进程会复制出一个子进程,而且父子进程的代码从fork()函数的返回开始分别在两个地址空间中同时运行。

从而两个进程分别获得其所属fork()的返回值,其中在父进程中fork()的返回值是子进程的进程号,而在子进程中fork()返回0。

因此,可通过返回值来判定该进程是父进程还是子进程。
若fork()创建进程失败,则返回-1。

技术分享


fork()系统调用的工作流程示意图:

技术分享


fork()函数的不足:
使用fork()函数时,它“复制”了父进程中的代码段、数据段和堆栈段里的大部分内容,使得fork()函数的系统开销比较大。
当创建一个子进程后,子进程接下来调用exec()执行另一个程序,那么前面的复制工作将是多余的
如果调用fork()函数的程序的数据段和堆栈段都很大,那么复制工作的开销会严重影响系统性能。
解决办法:采用copy-on-write(写时复制)技术。
复制时只是“逻辑”上的,并非“物理”上的:实际执行fork()时,物理空间上两个进程的数据段和堆栈段都还是共享着的,

当有一个进程写了某个数据时,这时两个进程之间的数据才有了区别,此时系统就将有区别的“页”从物理上分离,

这样系统在空间上的开销就可以达到最小。

fork()函数举例:

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

int main()
{
	pid_t result;
	result=fork();
	if(result==-1)
	{
		printf("FORK ERROR!\n");
	}
	else if(result==0)
	{
		printf("In child process PID is %d \n The returned value is %d \n\n",getpid(),result);
	}
	else
	{
		printf("In father process PID is %d \n The return value is %d \n\n",getpid(),result);	
	}

	return result;
}


运行截图如下:

技术分享


2.  vfork():
vfork是linux提供的另一个用来生成一个子进程的系统调用。
与fork的区别:vfork并不把父进程全部复制到子进程中,而只是用复制指针的方法使子进程和父进程的资源实现共享。

因为通常创建新进程的目的是 exec一个新程序,所以vfork不产生父进程的副本,可提高效率。
vfork与fork一样都创建一个子进程,但是它并不将父进程的地址空间完全复制到子进程中,因为子进程会立即调用 exec (或exit ),

不过在子进程调用 exec或exit之前,它在父进程的空间中运行。
vfork保证子进程先于父进程运行,只有当子进程调用exec或exit后,父进程才能被调度运行。
用vfork()创建的子进程不能用return返回,只能用exit()或_exit()退出。而用fork()创建的子进程可以用return返回。

技术分享


3. 对比fork()和vfork()函数,观察之间的区别

(1)调用fork()函数:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{    
	pid_t result;   
	int  data=5;  
	result = fork(); 	/*调用fork()函数*/    
	if(result ==  -1)	/*首先进行出错处理*/    
	    	  printf("Fork error\n");  
	else if (result == 0) 	/*返回值为0代表子进程*/	
	{
		data++;          
	 	printf("data=%d,child\'s PID=%d\n",data,getpid());
	}   
	else			 /*返回值大于0代表父进程*/       
	{ 
		data++;         
	        printf("data=%d,father\'s PID=%d\n",data,getpid());
	}   
	exit(1);  
}

运行截图:

技术分享

(2)调用vfork()函数:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{    
	pid_t result;   
	int  data=5;  
	result = vfork(); 	/*调用fork()函数*/    
	if(result ==  -1)	/*首先进行出错处理*/    
	    	  printf("Fork error\n");  
	else if (result == 0) 	/*返回值为0代表子进程*/	
	{
		  data++;          
	 	  printf("data=%d,child\'s PID=%d\n",data,getpid());
	}   
	else			 /*返回值大于0代表父进程*/       
	{ 
		  data++;         
	          printf("data=%d,father\'s PID=%d\n",data,getpid());
	}   
	exit(1);  
}
运行截图:

技术分享

小何讲进程: Linux进程控制编程 (fork、vfork)

标签:

原文地址:http://blog.csdn.net/rl529014/article/details/51282425

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!