fork, vfork, clone,pthread_create,kernel_thread
fork,vfork,clone,都是系统调用,以前还以为是前面两个是clone的封装,实际上前三个都是系统调用,pthread_create是对clone的封装,kernel_thread用于创建内核线程
fork 在内核中调用
do_fork(SIGCHLD, regs.esp, ®s, 0, NULL, NULL)
vfork:
do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.esp, ®s, 0, NULL, NULL)
clone:
do_fork(clone_flags, newsp, ®s, 0, parent_tidptr, child_tidptr)
其中
clone_flags = regs.ebx;
newsp = regs.ecx;
parent_tidptr = (int __user *)regs.edx;
child_tidptr = (int __user *)regs.edi;
pthread_create:
pid = __clone(__pthread_manager_event,
(void **) __pthread_manager_thread_tos,
CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND,
(void *)(long)manager_pipe[0]);
kernel_thread:
do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, ®s, 0, NULL, NULL)
在执行这个函数之前,在栈中对regs作了初始化,把自己的参数塞进起,并设regs.eip=kernel_thread_helper,具体流程参见前一篇文章。
可见他们的不同主要在于flag的不同,为什么前两个都有SIGCHLD,把保护用户态寄存器的地址传递是为什么?进程的到底是怎么切换的?
先看do_fork,首先分配一个PID,调用copy_process()进行具体的拷贝,如果flag里有CLONE_VFORK,将父进程放入一个等待队列,所以子进程先运行。
copy_process()
This creates a new process as a copy of the old one, but does not actually start it yet.It copies the registers, and all the appropriate parts of the process environment.
p = dup_task_struct(current); 为子进程分配task_struct和内核stack.
然后copy_files(),copy_fs()进行拷贝。
copy_thread,把用户传进来的通用寄存器进行拷贝给子进程并设置,这儿很关键
childregs = task_pt_regs(p);
*childregs = *regs;
childregs->eax = 0;
childregs->esp = esp;
p->thread.esp = (unsigned long) childregs;
p->thread.esp0 = (unsigned long) (childregs+1);
p->thread.eip = (unsigned long) ret_from_fork;
可见传入的用户态通用寄存器附给了子进程,并置eax=0所以返回的pid是0,估计是pt_regs里没有esp,所以单独赋值了。task_struct的thread字段记录了进程特定于cpu的信息,但切换到子进程的时候,就把thread中esp,eip弹出,所以子进程就可以通过ret_from_fork返回运行了。
而ret_from_fork
asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
不明白。
至于前面说道的SIGCHLD,估计是设置子进程挂了的时候给父进程发个SIGCHLD信号。也没看到在哪儿设。
所谓创建一个进程,就是创建task_struct和内核stack,并把stack里的pt_regs设置下,返回的时候好恢复到调用点,并选择性的继承父进程的资源。