源码

这段代码是Linux内核中用于创建一个新进程的函数,具体是_do_fork函数。这个函数是内核中处理fork系统调用的核心函数之一。

逐行解释:

  1. 函数签名:_do_fork函数接受多个参数,包括克隆标志clone_flags、栈起始地址stack_start、栈大小stack_size、父进程的线程ID指针parent_tidptr、子进程的线程ID指针child_tidptr以及线程本地存储tls

  2. 定义局部变量:vfork是一个用于vfork操作的完成结构体,pid是新进程的PID,p是指向新创建的进程结构体task_struct的指针。

  3. 检查是否需要报告跟踪事件:如果clone_flags中没有设置CLONE_UNTRACED标志,那么根据克隆类型设置跟踪事件类型trace。如果当前进程被跟踪,并且启用了相应的跟踪事件,则设置trace变量。

  4. 复制进程:调用copy_process函数复制当前进程,创建一个新的进程结构体。如果复制失败,返回错误。

  5. 添加熵:调用add_latent_entropy函数为内核的熵池添加熵,以提高随机数生成器的安全性。

  6. 获取新进程的PID:通过get_task_pid函数获取新进程的PID。

  7. 将子进程的PID写入父进程提供的地址:如果设置了CLONE_PARENT_SETTID标志,将新进程的PID写入父进程提供的地址。

  8. 对于vfork操作:如果设置了CLONE_VFORK标志,初始化一个完成结构体vfork,并增加对新进程结构体的引用计数。

  9. 唤醒新任务:调用wake_up_new_task函数唤醒新创建的进程,使其开始运行。

  10. 如果设置了跟踪事件,通知跟踪器:如果trace变量不为0,则调用ptrace_event_pid函数通知跟踪器。

  11. 对于vfork操作:如果设置了CLONE_VFORK标志,等待父进程调用exec或者退出,然后通知跟踪器vfork操作完成。

  12. 释放PID资源:调用put_pid函数释放PID资源。

  13. 返回新进程的PID:函数返回新创建的进程的PID。

这个函数处理了进程创建的多个方面,包括复制进程、处理跟踪事件、处理vfork特殊操作等。它是Linux内核中进程管理的一个重要组成部分。

/*
 *  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.
 */
long _do_fork(unsigned long clone_flags, // 是否clone
	      unsigned long stack_start, // 桟起始
	      unsigned long stack_size, // 桟大小
	      int __user *parent_tidptr, // 父进程的线程ID指针
	      int __user *child_tidptr, // 子进程的线程ID指针
	      unsigned long tls) // 线程本地存储
{
	struct completion vfork;
	struct pid *pid;
	struct task_struct *p;
	int trace = 0;
	long nr;
 
	/*
	 * Determine whether and which event to report to ptracer.  When
	 * called from kernel_thread or CLONE_UNTRACED is explicitly
	 * requested, no event is reported; otherwise, report if the event
	 * for the type of forking is enabled.
	 */
	if (!(clone_flags & CLONE_UNTRACED)) {
		if (clone_flags & CLONE_VFORK)
			trace = PTRACE_EVENT_VFORK;
		else if ((clone_flags & CSIGNAL) != SIGCHLD)
			trace = PTRACE_EVENT_CLONE;
		else
			trace = PTRACE_EVENT_FORK;
 
		if (likely(!ptrace_event_enabled(current, trace)))
			trace = 0;
	}
 
	p = copy_process(clone_flags, stack_start, stack_size,
			 child_tidptr, NULL, trace, tls, NUMA_NO_NODE);
	add_latent_entropy();
 
	if (IS_ERR(p))
		return PTR_ERR(p);
 
	/*
	 * Do this prior waking up the new thread - the thread pointer
	 * might get invalid after that point, if the thread exits quickly.
	 */
	trace_sched_process_fork(current, p);
 
	pid = get_task_pid(p, PIDTYPE_PID);
	nr = pid_vnr(pid);
 
	if (clone_flags & CLONE_PARENT_SETTID)
		put_user(nr, parent_tidptr);
 
	if (clone_flags & CLONE_VFORK) {
		p->vfork_done = &vfork;
		init_completion(&vfork);
		get_task_struct(p);
	}
 
	wake_up_new_task(p);
 
	/* forking complete and child started to run, tell ptracer */
	if (unlikely(trace))
		ptrace_event_pid(trace, pid);
 
	if (clone_flags & CLONE_VFORK) {
		if (!wait_for_vfork_done(p, &vfork))
			ptrace_event_pid(PTRACE_EVENT_VFORK_DONE, pid);
	}
 
	put_pid(pid);
	return nr;
}