进程创建

        Linux中关于进程创建的主要函数有fork,vfork,clone,他们的实现都是有do_fork来完成的,只是传入的参数有差别,do_fork中调用函数copy_process从父进程中复制相关内容到子进程,其中这个复制量的确定是根据传入参数flag来确定是否需要重新申请内存还是共享父进程的资源,下面对具体的代码进行分析。

	/*唯一使用的标志是SIGCHLD。这意味着在子进程终止后
	发送SIGCHLD信号通知父进程。
	*/
	int sys_fork(struct pt_regs *regs)
	{
		return do_fork(SIGCHLD, regs->sp, regs, 0, NULL, NULL);
	}
	
	/*
	 * This is trivial, and on the face of it looks like it
	 * could equally well be done in user mode.
	 *
	 * Not so, for quite unobvious reasons - register pressure.
	 * In user mode vfork() cannot have a stack frame, and if
	 * done by calling the "clone()" system call directly, you
	 * do not have enough call-clobbered registers to hold all
	 * the information you need.
	 */
	int sys_vfork(struct pt_regs *regs)
	{
		return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->sp, regs, 0,
			       NULL, NULL);
	}
	/*可以看到,参数的设置取决于用户的设置*/
	int sys_clone(struct pt_regs *regs)
	{
		unsigned long clone_flags;
		unsigned long newsp;
		int __user *parent_tidptr, *child_tidptr;
	
		clone_flags = regs->bx;
		newsp = regs->cx;
		parent_tidptr = (int __user *)regs->dx;
		child_tidptr = (int __user *)regs->di;
		if (!newsp)
			newsp = regs->sp;
		return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr);
	}

下面是具体的do_fork函数

	/*
	 *  Ok, this is the main fork-routine.
	 *
	 * It copies the process, and if successful kick-starts
	 * it and waits for it to finish using the VM if required.
	 */
	 /*clone_flags是一个标志集合,用来指定控制复制过程的一些属性。
	 最低字节指定了在子进程终止时被发给父进程的信号号码。其余的高位
	 字节保存了各种常数,下文会分别讨论。
	
	start_stack是用户状态下栈的起始地址。
	
	regs是一个指向寄存器集合的指针,其中以原始形式保存了调用参数。
	该参数使用的数据类型是特定于体系结构的struct pt_regs,其中按照
	系统调用执行时寄存器在内核栈上的存储顺序,保存了所有的寄存器
	(更详细的信息,请参考附录A)。
	
	stack_size是用户状态下栈的大小。该参数通常是不必要的,设置为0。
	
	parent_tidptr和child_tidptr是指向用户空间中地址的两个指针
	,分别指向父子进程的TID。NPTL(Native Posix Threads Library)
	库的线程实现需要这两个参数。
	*/
	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)
	{
		struct task_struct *p;
		int trace = 0;
		long nr;
	
		/*
		 * Do some preliminary argument and permissions checking before we
		 * actually start allocating stuff
		 */
		 /*下面是标志组合的正确性检查*/
		if (clone_flags & CLONE_NEWUSER) {
			if (clone_flags & CLONE_THREAD)
				return -EINVAL;
			/* hopefully this check will go away when userns support is
			 * complete
			 *//*权限*/
			if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SETUID) ||
					!capable(CAP_SETGID))
				return -EPERM;
		}
	
		/*
		 * We hope to recycle these flags after 2.6.26
		 */
		 /*如果设置了CLONE_STOPPED标志,进程内核打印
		 输出创建标志信息*/
		if (unlikely(clone_flags & CLONE_STOPPED)) {
			static int __read_mostly count = 100;
			/*函数printk_ratelimit为打印控制,具体会在后面做分析*/
			if (count > 0 && printk_ratelimit()) {
				char comm[TASK_COMM_LEN];
	
				count--;
				printk(KERN_INFO "fork(): process `%s' used deprecated "
						"clone flags 0x%lx\n",
					get_task_comm(comm, current),
					clone_flags & CLONE_STOPPED);
			}
		}
	
		/*
		 * When called from kernel_thread, don't do user tracing stuff.
		 */
		 /*whether a register set came from user mode.*/
		if (likely(user_mode(regs)))
			trace = tracehook_prepare_clone(clone_flags);/*Its return value will be passed to tracehook_finish_clone().*/
		/*复制一个进程,并对相应标志位进行设定,参见后面的详细介绍
		*/
		p = copy_process(clone_flags, stack_start, regs, stack_size,
				 child_tidptr, NULL, trace);
		/*
		 * Do this prior waking up the new thread - the thread pointer
		 * might get invalid after that point, if the thread exits quickly.
		 */
		if (!IS_ERR(p)) {/*判断p的有效性*/
			struct completion vfork;
			/*???*/
			trace_sched_process_fork(current, p);
	
			nr = task_pid_vnr(p);/*返回进程p的命名空间中的pid*/
			/*Write a simple value into user space*/
			if (clone_flags & CLONE_PARENT_SETTID)/* if set the TID in the parent */
				put_user(nr, parent_tidptr);/*将nr放到parent_tidptr地址中
										此地址为用户空间中的*/
			/*如果设置了这个标志,将初始化进程中的
			completion结构*/
			if (clone_flags & CLONE_VFORK) {
				p->vfork_done = &vfork;
				init_completion(&vfork);
			}
			 /*audit context内容的复制,需要该变量空间已经存在*/
			audit_finish_fork(p);
			 /*如果进子进程被跟踪则发送SIGSTOP信号.
			由于子进程现在还没有运行,信号不能被处理
	  		所以设置TIF_SIGPENDING标志*/
			tracehook_report_clone(regs, clone_flags, nr, p);
	
			/*
			 * We set PF_STARTING at creation in case tracing wants to
			 * use this to distinguish a fully live task from one that
			 * hasn't gotten to tracehook_report_clone() yet.  Now we
			 * clear it and set the child going.
			 */
			p->flags &= ~PF_STARTING;
			/*如果子进程初始化成STOP状态
	   		则发送SIGSTOP信号.由于子进程现在还没有运行,信号不能被处理
	   		所以设置TIF_SIGPENDING标志*/
			if (unlikely(clone_flags & CLONE_STOPPED)) {
				/*
				 * We'll start up with an immediate SIGSTOP.
				 */
				sigaddset(&p->pending.signal, SIGSTOP);
				set_tsk_thread_flag(p, TIF_SIGPENDING);
				__set_task_state(p, TASK_STOPPED);
			} else {/*进入运行队列,调用调度类的唤醒函数,后面详细介绍*/
				wake_up_new_task(p, clone_flags);
			}
	
			tracehook_report_clone_complete(trace, regs,
							clone_flags, nr, p);
			/*如果定义了CLONE_VFORK标志.则将当前进程投入睡眠*/
			if (clone_flags & CLONE_VFORK) {
				freezer_do_not_count();
				wait_for_completion(&vfork);
				freezer_count();
				tracehook_report_vfork_done(p, nr);
			}
		} else {
			nr = PTR_ERR(p);
		}
		return nr;
	}
对于设置了CLONE_VFORK标志的,调用下面函数

/**
 * init_completion: - Initialize a dynamically allocated completion
 * @x:  completion structure that is to be initialized
 *
 * This inline function will initialize a dynamically created completion
 * structure.
 */
static inline void init_completion(struct completion *x)
{	/*done标志为0。表示子进程还没有将父进程唤醒*/
	x->done = 0;
	/*初始化一个等待队列*/
	init_waitqueue_head(&x->wait);
}
和下面的代码呼应

/*如果定义了CLONE_VFORK标志.则将当前进程投入睡眠*/
		if (clone_flags & CLONE_VFORK) {
			freezer_do_not_count();
			wait_for_completion(&vfork);
			freezer_count();
			tracehook_report_vfork_done(p, nr);
		}
唤醒函数,将进程入运行队列

/*
 * wake_up_new_task - wake up a newly created task for the first time.
 *
 * This function will do some initial scheduler statistics housekeeping
 * that must be done for every newly created context, then puts the task
 * on the runqueue and wakes it.
 */
void wake_up_new_task(struct task_struct *p, unsigned long clone_flags)
{
	unsigned long flags;
	struct rq *rq;

	rq = task_rq_lock(p, &flags);/*获得指定task的rq*/
	BUG_ON(p->state != TASK_RUNNING);
	update_rq_clock(rq);/*更新rq的时钟计时*/

	if (!p->sched_class->task_new || !current->se.on_rq) {
		activate_task(rq, p, 0);/*进入运行队列*/
	} else {
		/*
		 * Let the scheduling class do new task startup
		 * management (if any):
		 */
		p->sched_class->task_new(rq, p);
		inc_nr_running(rq);
	}
	trace_sched_wakeup_new(rq, p, 1);
	/*用以決定一個Task是否可以中斷目前正在
	運作的Task,取得執行權.*/
	check_preempt_curr(rq, p, WF_FORK);
#ifdef CONFIG_SMP
	if (p->sched_class->task_wake_up)
		p->sched_class->task_wake_up(rq, p);
#endif
	task_rq_unlock(rq, &flags);
}

对于copy_process函数比较繁琐,也是do_fork主要执行函数,完成进程资源的复制,根据相关的标志位,有的资源需要和父进程共享,具体下一篇我们会看到。




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值