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. 非主函数中调用return和exit效果很明显,但是在main函数中调用return和exit的现象就很模糊,多数情况下现象都是一致的。
为什么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的理解的主要内容,如果未能解决你的问题,请参考以下文章