【归纳总结】Unix/linux下的进程管理(二):创建进程的函数及其应用、对比
本次内容主要介绍Unix/linux下2个创建进程的函数fork和vfork以及它们的差别。
1.fork函数
(1)函数的格式
#include <unistd.h>
pid_t fork(void);
函数功能:
主要用于以复制正在运行进程的方式来创建新的进程,其中新进程叫做子进程,正在运行的进程叫做父进程。
返回值:
函数调用成功时,父进程返回子进程的PID,子进程返回0,函数调用出错时,父进程返回-1,子进程没有被创建。
注意:
此函数的返回值有2个,主要原因是因为在fovk执行结束前,运行到x行 就已经创建出了一个子进程,所以父子进程都会会执行x行到return语句的代码。为了能找到自己的儿子,所以父进程会返回子进程的pid,子进程能通过getpid()找到父进程,因此子进程返回0即可。
(2)fork创建子进程的工作方式
1)fork函数之前的代码,父进程执行依次。
2)fork函数之后的代码,父子进程各自执行一次。
3)fork函数的返回值,父子进程各自返回一次。
(3)fork创建的父子进程之间的关系
1)父进程启动子进程,父子进程同时启动,如果子进程先结束,会给父进程发信号,让父进程帮助子进程回收资源。
2)如果父进程现在结束,子进程变成孤儿进程,子进程会变更父进程(重新指定init进程为新的父进程),init进程也叫做孤儿院。
3)如果子进程先结束,但是父进程由于各种原因并没有收到子进程发来的信号,也就是没有帮子进程回收资源,那么子进程会变成僵尸进程。
(4)fork函数创建的父子进程之间的内存资源关系
进程的内存区域划分:代码区、全局区、堆区、栈区
子进程是父进程的副本,子进程获得父进程数据段和堆栈段(包括I/O流缓冲区)的拷贝,但子进程共享父进程的代码段。 父进程的文件描述符表也会被复制到子进程中,二者共享同一个文件表。所以如果在父子进程中,有不需要的描述符的话,最好要close。
------------------------------------------------------------------------------------------------------------------------
2.vfork和execl函数
(1)vforK函数
#include <sys/types.h>
#include <unistd.h>
pid_t vfork(void);
函数功能:
该函数的功能与fork函数类似,返回值和出错原因等参数fork函数即可,但是所不同的是,该函数创建的子进程不会复制父进程中的内存区域,而是直接占用,导致父进程进入阻塞状态,直到子进程结束或者调用exec系列函数为止,而子进程的结束应该去调用_exit()函数。
注意:
vfork函数保证创建的子进程先执行。
(2)exec系列函数
int execl(const char *path, const char *arg, ...);
第一个参数:执行的文件路径
第二个参数:执行的参数,一般给文件名即可
第三个参数:可变长参数,最后使用NULL结尾
函数功能:
主要用于按照参数指定的路径和方式区执行文件。
如:
使用execl函数执行ls -l 命令时:
execl("/bin/ls", "ls", "-l", NULL);
注意:
vfork函数本身没有太大的实际意思,一般与exec系列函数搭配使用,而vfork函数负责创建子进程,exec系列函数负责执行新的代码。
fork函数也可以和exec系列函数搭配使用,一般很少使用。
------------------------------------------------------------------------------------------------------------------------
fork与vfork的差别及其应用
从概念上说来,可以将fork()认为对父进程的副本,除了文本区是共享之外,其它都是创建拷贝的。这是早期fork函数的实现,这种做法大大的耗费了内存的资源,相比之下vfork()创建的子进程则是共享了父进程的内存,效率远比fork函数高。在fork()采用写时复制前,vfork()可以说完全取代fork()。
为了解决fork函数造成不必要的浪费,内核对父进程的数据区、BBS段、堆区和栈区采用了写时复制(copy-on-write)技术来处理。这种技术大致可以理解为,父子进程先共享内存,如果整个程序结束前,父子进程都没有对内存进行写操作,那么子进程就没必要对父进程的内存进行拷贝。最初,内核做了一些设置,令这些段的页表项指向父进程相同的物理内存业,并将这些页面自身标为只读。调用fork()后,内核会捕捉所有父进程或子进程针对这些页面的修改企图,并为将要修改的页面创建拷贝。系统将心的页面拷贝分配给遭内核捕获的进程,还会对子进程的相应页表项做适当调整。从这一刻起,父、子进程可以分别修改各自的页拷贝,不再相互影响。
相比与fork(),vfork()是为子进程立即执行exec()系列的程序而专门设计的。主要原因如下
1:vfork()无需为子进程复制虚拟内存或页表。相反,子进程共享父进程内存,直至其成功执行了exec()或者调用-exit()退出。
2:vfork()创建的子进程在调用exec()或_exit()之前,将暂停执行父进程。
fork与vfork的运用场景
一般fork与vfork、exec配合到进程终止的函数一起使用的,关于进程的函数这里就不多讨论。下面我举个模拟银行的例子,通俗的对这2个函数的基本使用进行一个大致的描绘。
假设这模拟个银行只有取款与存款这么两个功能,要求是程序可以同时执行取款和存款这2个程序,并且能实现多个客户同时存款和取款。
同时执行存款和取款的话,使用vfork()+exec()函数最适合不过,父进程创建2个子进程,子进程A负责存款业务,子进程B负责取款业务。如此类推,有多少个业务,父进程就使用vfork()+execl()创建多少个子进程。
在存款进程中,A会等待存款消息,当捕获了一个存款消息后,进程A就fovk()一个子进程,子进程就负责处理这个消息,那么在存款进程中,父进程负责的是捕获消息,具体的处理工作都交给了子进程,这样就能实现多个进程的并发处理,加快业务处理的效率。
取款进程同上。
总结:fovk()+execl()是创建多个功能不一样的进程处理不同的业务,fovk则是创建多个功能一样的进程去处理相同的业务!
(最后关于这个模拟银行的练习代码,不久再整理出来好了)
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。