fork和vfork,return和exit的理解

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了fork和vfork,return和exit的理解相关的知识,希望对你有一定的参考价值。

fork和vfork的差别:

1、fork是创建一个子进程,并把父进程的内存数据copy到子进程中。

vfork是创建一个子进程,并和父进程的内存数据share一起。

2、vfork是这样的工作的:

(1)、保证子进程先执行。

(2)、当子进程调用exit()或exec()后,父进程往下执行。

3、fork后来采用的优化技术,这样,对于fork后并不是马上拷贝内存,而是只有你在需要改变的时候,才会从父进程中拷贝到子进程中,这样fork后立马执行exec的成本就非常小了。而vfork因为共享内存所以比较危险。

 

为什么会出现vfork

原因是: 起初只有fork,但是很多程序在fork一个子进程后就exec一个外部程序,于是fork需要copy父进程的数据这个动作就变得毫无意了,而且还很重,所以,搞出了个父子进程共享的vfork。所以,vfork本就是为了exec而生。

exit与return的区别:

exit函数在头文件stdlib.h中。

exit(0):正常运行程序并退出程序;

exit(1):非正常运行导致退出程序;

return():返回函数,若在main主函数中,则会退出函数并返回一值,可以写为return(0),或return 0

详细说:

1. return返回函数值,是关键字;exit是一个函数。

2. return是语言级别的,它表示了调用堆栈的返回;而exit是系统调用级别的,它表示了一个进程的结束。
3. return是函数的退出(返回);exit是进程的退出。

4. return是C语言提供的,exit是操作系统提供的(或者函数库中给出的)。

5. return用于结束一个函数的执行,将函数的执行信息传出个其他调用函数使用;exit函数是退出应用程序,删除进程使用的内存空间,并将应用程序的一 个状态返回给OS,这个状态标识了应用程序的一些运行信息,这个信息和机器和操作系统有关,一般是 0 为正常退出,非0 为非正常退出。

6. 非主函数中调用returnexit效果很明显,但是在main函数中调用returnexit的现象就很模糊,多数情况下现象都是一致的。

 

为什么return会挂掉,exit()不会?

从上面我们知道,结束子进程的调用是exit()而不是return,如果你在vfork中return了,那么,这就意味main()函数return了,注意因为函数栈父子进程共享,所以整个程序的栈就出现问题了。如果你在子进程中return,那么基本是下面的过程:

(1)、首先子进程的main() 函数 return了。

(2)、而main()函数return后,通常会调用 exit()或相似的函数(如:exitgroup())。

(3)、这时,父进程收到子进程exit(),开始从vfork返回,但是父进程的栈都被子进程干废掉了,你让我怎么执行?(注:栈会返回一个诡异一个栈地址,对于某些内核版本的实现,直接报“栈错误”,然而,对于某些内核版本的实现,于是有可能会再次调用main(),于是进入了一个无限循环的结果,直到vfork 调用返回 error)

再回到 return 和 exit,return会释放局部变量,并弹栈,回到上级函数执行。exit直接退掉。如果你用c++ 你就知道,return会调用局部对象的析构函数,exit不会。(注:exit不是系统调用,是glibc对系统调用 _exit()或_exitgroup()的封装)。

可见,子进程调用exit() 没有修改函数栈,所以,父进程得以顺利执行。

 

内核代码分析:

linux创建子进程实际是一个复制父进程的过程。所以更贴切的说法是clone。linux一开始使用fork的原因是当时clone这个词还没有流行。 实际存在fork,clone,vfork 三个系统调用。fork是完全复制,clone则是有选择的复制,vfork则完全使用父进程的资源。可以理解vfork是创建的线程。 vfork的出现主要是为了立即就执行exec的程序考虑的。但是后来的kernel都支持copy_on_write ,所以vfork提高效率的机制也没有那么明显了。

内核中三个系统调用最后都是调用do_fork:

fork:return do_fork(SIGCHLD, regs.esp, s, 0);

clone:return do_fork(clone_flags, newsp, s, 0);

vfork: return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, s, 0);

 

#define CLONE_VFORK 0x00004000  /* set if the parent wants the child to wake it up on mm_release*/

#define CLONE_VM 0x00000100  /* set if VM shared between processes */

上面两个宏指出:vfork 要求子进程执行mm_release 后唤醒 父进程, 并且共享虚拟内存

 

为什么要求子进程先行呢?

拿虚拟内存做比方。 进程需要有结构管理自己的虚拟内存空间, 该结构在进程 结构体 task_struct 中就是一个mm_struct 类型的指针。fork的时候内核会新建结构体,将该mm_struct 本身以及下级结构都复制一份,并设置子进程的mm_struct 指向新的内存。而vfork则只是复制了task_struct 本身,并没有递归下去。简单说就是:fork复制了内存,vfork复制了指针。

 

相关例子:

fork

技术分享

 vfork

技术分享

运行结果为:

fork运行结果:

技术分享

vfork运行结果:

1、return返回时出错:

技术分享

2、exit返回结果:

技术分享


运行结果说明:vfrok时父、子进程共享数据段,fork时是进行拷贝。如果,vfork子进程中,使用return返回时,出现段错误。

 


以上是关于fork和vfork,return和exit的理解的主要内容,如果未能解决你的问题,请参考以下文章

fork和vfork

fork和vfork

fork与vfork的区别与联系

(疑问)进程控制---vfork 函数

细究fork()和vfork()

return和exit函数的区别