Linux fork(),vfork(),clone()底层浅谈

来源:互联网 发布:java互联网开发框架 编辑:程序博客网 时间:2024/06/08 06:09

Linux  fork(),vfork(),clone()底层浅谈

 

我们平常进程会用到fork,但是fork只是创建了一个与父进程一模一样的子进程,并且复制了父进程所有的资源,包括打开的文件,父进程的数据结构、映射的物理页面,以及各种堆栈等等。如果我们不需要这么多资源,那我们该怎么操作呢?

 

vfork会共享父进程的堆栈,同时也会先于父进程运行(将父进程挂起),只有在子进程结束后父进程才会运行。vfork除了task_struct结构和系统空间堆栈以外的资源全部通过数据结构指针的复制来“遗传”。vfork不能够使用return返回。只能使用exit(1)退出。

 

clone可以有选择性的继承父进程的资源,可以像vfork一样完全与父进程共享,也可以像fork一样完全拷贝。

 

下面来看看forkclone的不同:

Pid_t fork(void);

Int clone(int (*fn)(void *arg), void *child_stack, int flags, void *arg);

 

fork会调用sys_forkvfork会调用sys_vforkclone会调用sys_clone。但是他们的底层是调用do_fork()。不过在调用do_fork的时候,他们三个给的参数不同。

 

Int do_fork(

unsigned long clone_flags,//这个参数包含两部分,一个是子进程去世时给父进程发的信号,另一个是表示一些资源和标志位  

unsigned long stack_start,//栈顶指针esp

struct pt_regs *regs,//通常给的父进程的regs

unsigned long stack_size//通常给0

)

 

上面是do_fork函数,在调用do_fork的时候,fork,vfork,clone三个给的参数却不一样

 

fork: do_fork(SIGCHILD,regs.esp,®s,0);

vfork: do_fork(CLONE_VFORK|CLONE_VM|SIGCHILD,regs.esp,®s,0);

clone: do_fork(clone_flags,newsp,®s,0);//newsp实际上是regs.ecxregs.esp中的一个

 

先看fork,fork在子进程结束的时候会给父进程发送SIGCHILD信号;从regs.esp获得父进程的栈顶地址,复制了父进程的系统堆栈。把父进程的regs放在系统堆栈的顶部,为什么要把父进程的regs放到系统堆栈的顶部呢?do_fork是一个系统调用,进程因为系统调用或者中断进入内核的时候,其系统空间的顶部保存着CPU进入内核前夕个个寄存器的内容,并且形成了一个pt_regs的数据结构。

 

接下来看vfork,在看参数前,先了解一下一些有关的宏

 

#define CSIGNAL 0x000000ff /* signal mask to be sent at exit */
#define CLONE_VM 0x00000100 /* set if VM shared between processes */
#define CLONE_FS 0x00000200 /* set if fs info shared between processes */
#define CLONE_FILES 0x00000400 /* set if open files shared between processes */
#define CLONE_SIGHAND 0x00000800 /* set if signal handlers and blocked signals shared */
#define CLONE_PID 0x00001000 /* set if pid shared */
#define CLONE_PTRACE 0x00002000 /* set if we want to let tracing continue on the child too */
#define CLONE_VFORK 0x00004000 /* set if the parent wants the child to wake it up on mm_release*/ 

 

vfork会在子进程结束后给父进程发送SIGCHILD信号,并且共享了父进程的用户虚存空间,并且当子进程释放后会唤醒父进程,进程调度的时候把regs放在系统堆栈的顶部。

 

最后来看看cloneclone的参数给的比较随意,没有指定需要给的参数,设立了一个新的堆栈,剩下的和fork没什么区别。

 

do_fork先复制task_struct的数据结构,接着根据第一个参数来复制文件系统,接下来处理信号,处理物理页面,最后是处理系统堆栈。这样,一个新的进程(线程)就出来了。

 

 

我们既然谈到了vfork的退出,就得谈谈exitexit的底层是do_exit,在执行exit的时候,会把一些父进程的资源释放,并且取消内核的定时器队列,消除该进程的临界区等等资源。那么我们会问:有一些时候我们没有fork,也使用了exit,那么他的父进程从哪来的?

 

实际上是这样的,linux0号进程引导创建1号进程init,之后所有的进程都可以视作是被“复制出来的”。所以,虽然该进程没有显式调用fork,但实际上这些进程都是存在父进程的。

原创粉丝点击