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

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


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);  
}
运行截图:


GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐