系统调用
创建进程的系统调用为:fork,vfork,clone,其相对应的系统调用函数为sys_fork,sys_vfork,
sys_clone,这些函数都与体系相关,将用户空间的参数传递给内核空间,然后调用系统独立函数do_fork:
- long do_fork(unsigned long clone_flags,unsigned long stack_start,
-
struct pt_regs *regs,unsigned long stack_size,
-
int __user *parent_tidptr,int __user *child_tidptr)
其中
clone_flags:指明复制的属性,低字节表示当子进程退出时,发送给父进程的信号数量,高字节表示复制的各项参数
stack_start:复制的用户栈起始地址
regs:原始形式的调用参数,这里使用了与体系相关的结构参数,也就是用户传递的参数
stack_size:用户态栈的大小,一般设置为0
parent_tidptr,child_tidptr:
指向用户态,保存父子进程的线程id(tid),是线程库NPTL必不可少的部分。
不同的fork变体,采用不同的标志位,如:
- asmlinkage long sys_fork(struct pt_regs *reg)
-
{
-
return do_fork(SIGCHLD,regs->rsp,regs,0,NULL,NULL);
-
}
-
asmlinkage long sys_vfork(struct pt_regs *regs)
-
{
-
return do_fork(CLONE_VFORK|CLONE_VM|SIGCHLD,regs->rsp,reg,0,
-
NULL,NULL);
-
}
而sys_clone如下
:
- //regs:接收来自用户传递的参数
-
asmlinkage long sys_clone(struct pt_regs regs)
-
{
-
unsigned long clone_flags;
-
unsigned long newsp;
-
int __user *parent_tidptr,*child_tidptr;
-
/*将参数转化为具体的系统调用参数,传递给do_fork*/
-
clone_flags = regs.ebx;
-
newsp = regs.ecx;
-
parent_tidptr = (int __user *)regs.edx;
-
child_tidptr = (int __user*)regs.edi;
-
if(!newsp)
-
newsp = regs.esp;
-
return do_fork(clone_flags,newsp,®s,0,parent_tidptr,child_tidptr);
-
}
do_fork的实现
调用关系图如下:
do_fork主要执行下面的步骤:
1.判断clone_flags,调用copy_process,产生子进程,返回子进程之后,开始判断与进程属性相关的各种标志,然后进行相对应的处理。
2.产生新的pid,如果设置了CLONE_NEWPID,则在父进程的命名空间中分配新的pid,否则生成新的命名空间,返回pid。
- nr = (clone_flags & CLONE_NEWPID)?
-
//直接从当前进程的命名空间中分配一个pid
-
task_pid_nr_ns(p,current->nsproxy->pid_ns):
-
task_pid_vnr(p);//
3.是否是创建线程?如果设置了CLONE_PARENT_SETTID,则需要将刚刚获取的nr写到用户的行参中.
- if(clone_flags & CLONE_PARENT_SETTID)
-
put_user(nr,parent_tidptr);
4.是否设置了CLONE_VFORK标志,如果设置了该标志,则需要初始化相关数据结构
- if(clone_flags & CLONE_VFORK) {
-
p->vfork_done = &vfork;
-
/*vfork类型定义如下:
-
struct completion {
-
unsigned int done;
-
wait_queue_head_t wait;
-
}
-
*/
-
init_completion(&vfork);
-
}
5.判断CLONE_STOPPED,开始启动任务
- if(!(clone_flags & CLONE_STOPPED))
-
//放入调度队列,等待运行,防止一直fork,消耗cpu时间
-
wake_up_new_task(p,clone_flags);
-
else
-
p->state = TASK_STOPPED;
6.如果是vfork机制,则父进程开始睡眠,等待子进程退出,这样就能保证两个进程不能使用相同的进程地址空间。
- if(clone_flags & CLONE_VFORK) {
-
//设置内存访问标志,防止当前进程访问
-
freezer_do_not_count();
-
wait_for_completion(&vfork);
-
//解除访问标志
-
freezer_count();
-
…...
-
}
阅读(1624) | 评论(0) | 转发(0) |