linux内核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 (as per the clone
 * flags). The actual kick-off is left to the caller.
 */
static __latent_entropy struct task_struct *copy_process(
					unsigned long clone_flags,
					unsigned long stack_start,
					unsigned long stack_size,
					int __user *child_tidptr,
					struct pid *pid,
					int trace,
					unsigned long tls,
					int node)
{
	int retval;
	struct task_struct *p;
	struct multiprocess_signals delayed;
	/*检查参数clone_flags所传递的一致性.
	如果
	1.CLONE_NEWNS和CLONE_FS都被设。
	2.CLONE_THREAD被置位,但是CLONE_SIGHAND标志被清0
	(也就是说同一线程组的轻量级进程必须共享信号)
	3.CLONE_SIGHAND标志被设置,但CLONE_VM被清0
	(也就是要求共享信//号处理程序的轻量级进程必须共享内存描述符)。
	以上的三种情况会返回错误代码

	int clone(int (*fn)(void *), void *child_stack, int flags, void *arg);
	这里fn是函数指针,我们知道进程的4要素,这个就是指向程序的指针,就是所谓的“剧本", 
	child_stack明显是为子进程分配系统堆栈空间(在linux下系统堆栈空间是2页面,就是8K的内存,
	其中在这块内存中,低地址上放入了值,这个值就是进程控制块task_struct的值),
	flags就是标志用来描述你需要从父进程继承那些资源, arg就是传给子进程的参数)。下面是flags可以取的值:
	
	CLONE_PARENT  创建的子进程的父进程是调用者的父进程,新进程与创建它的进程成了“兄弟”而不是“父子”
 	CLONE_FS      子进程与父进程共享相同的文件系统,包括root、当前目录、umask
 	CLONE_FILES   子进程与父进程共享相同的文件描述符(file descriptor)表
 	CLONE_NEWNS   在新的namespace启动子进程,namespace描述了进程的文件hierarchy(等级制度)
 	CLONE_SIGHAND 子进程与父进程共享相同的信号处理(signal handler)表
 	CLONE_PTRACE  若父进程被trace,子进程也被trace
 	CLONE_VFORK   父进程被挂起,直至子进程释放虚拟内存资源
 	CLONE_VM      子进程与父进程运行于相同的内存空间	
 	CLONE_PID     子进程在创建时PID与父进程一致
 	CLONE_THREAD  Linux 2.4中增加以支持POSIX线程标准,子进程与父进程共享相同的线程群
	*/
	/*
	 * Don't allow sharing the root directory with processes in a different
	 * namespace
	 */
	/* CLONE_FS 不能与 CLONE_NEWNS 或 CLONE_NEWUSER 同时设置 */
	if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
		return ERR_PTR(-EINVAL);

	if ((clone_flags & (CLONE_NEWUSER|CLONE_FS)) == (CLONE_NEWUSER|CLONE_FS))
		return ERR_PTR(-EINVAL);

	/*
	 * Thread groups must share signals as well, and detached threads
	 * can only be started up within the thread group.
	 */
	/* 创建线程时线程之间要共享信号处理函数 */
	if ((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND))
		return ERR_PTR(-EINVAL);

	/*
	 * Shared signal handlers imply shared VM. By way of the above,
	 * thread groups also imply shared VM. Blocking this case allows
	 * for various simplifications in other code.
	 */
	/* 
	 * 父子进程共享信号处理函数时必须共享内存地址空间
	 * 这就是为什么书上写的fork出来的父子进程有其独立的信号处理函数,因为他们的内存地址空间不同
	 */
	if ((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM))
		return ERR_PTR(-EINVAL);

	/*
	 * Siblings of global init remain as zombies on exit since they are
	 * not reaped by their parent (swapper). To solve this and to avoid
	 * multi-rooted process trees, prevent global and container-inits
	 * from creating siblings.
	 */
	/*
 	 * 防止参数init进程的兄弟进程
 	 * 只有init进程的 signal->flags & SIGNAL_UNKILLABLE 为真
 	 * 因为当进程退出时实际上是成为了僵尸进程(zombie),而要通过init进程将它回收,而如果此进程为init的兄弟进程,则没办法将其回收
 	 */
	if ((clone_flags & CLONE_PARENT) &&
				current->signal->flags & SIGNAL_UNKILLABLE)
		return ERR_PTR(-EINVAL);

	/*
	 * If the new process will be in a different pid or user namespace
	 * do not allow it to share a thread group with the forking task.
	 */
	/* 如果新的进程将会有新的用户空间或者pid,则不能让它共享父进程的线程组或者信号处理或者父进程 */
	if (clone_flags & CLONE_THREAD) {
		if ((clone_flags & (CLONE_NEWUSER | CLONE_NEWPID)) ||
		    (task_active_pid_ns(current) !=
				current->nsproxy->pid_ns_for_children))
			return ERR_PTR(-EINVAL);
	}
	
	/* 附加安全检查 */
	/*
	 * Force any signals received before this point to be delivered
	 * before the fork happens.  Collect up signals sent to multiple
	 * processes that happen during the fork and delay them so that
	 * they appear to happen after the fork.
	 */
	sigemptyset(&delayed.signal);
	INIT_HLIST_NODE(&delayed.node);

	spin_lock_irq(&current->sighand->siglock);
	if (!(clone_flags & CLONE_THREAD))
		hlist_add_head(&delayed.node, &current->signal->multiprocess);
	recalc_sigpending();
	spin_unlock_irq(&current->sighand->siglock);
	retval = -ERESTARTNOINTR;
	if (signal_pending(current))
		goto fork_out;
	
	/* 为新进程分配struct task_struct内存和内核栈内存 */
	retval = -ENOMEM;
	p = dup_task_struct(current, node);
	if (!p)
		goto fork_out;

	/*
	 * This _must_ happen before we call free_task(), i.e. before we jump
	 * to any of the bad_fork_* labels. This is to avoid freeing
	 * p->set_child_tid which is (ab)used as a kthread's data pointer for
	 * kernel threads (PF_KTHREAD).
	 */
	p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL;
	/*
	 * Clear TID on mm_release()?
	 */
	p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr : NULL;
    /* ftrace是用于内核性能分析和跟踪的 */
	ftrace_graph_init_task(p);
    /* mutex初始化,其用于SYSTEM V IPC,具体可见 http://blog.chinaunix.net/uid-7295895-id-3011238.html */
	rt_mutex_init_task(p);

#ifdef CONFIG_PROVE_LOCKING
	DEBUG_LOCKS_WARN_ON(!p->hardirqs_enabled);
	DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled);
#endif
	retval = -EAGAIN;
	/* 检查 tsk->signal->rlim[RLIMIT_NPROC].rlim_cur是否小于等于用户所拥有的进程数,rlim结构体表示相关资源的最大值 */
	if (atomic_read(&p->real_cred->user->processes) >=
			task_rlimit(p, RLIMIT_NPROC)) {
        /* INIT_USER是root权限。检查父进程是否有root权限 */
		if (p->real_cred->user != INIT_USER &&
		    !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN))
			goto bad_fork_free;
	}
	current->flags &= ~PF_NPROC_EXCEEDED;
    /* 将父进程的cred复制到子进程的real_cred和cred。struct cred用于安全操作的结构 */
	retval = copy_creds(p, clone_flags);
	if (retval < 0)
		goto bad_fork_free;

	/*
	 * If multiple threads are within copy_process(), then this check
	 * triggers too late. This doesn't hurt, the check is only there
	 * to stop root fork bombs.
	 */
	/* 
	 * 进程数量是否超出系统允许最大进程数量,最大进程数量跟内存有关,
	 * 一般原则是所有的进程内核栈(默认8K)加起来不超过总内存的1/8,
	 * 可通过/proc/sys/kernel/threads-max改写此值 
	 */
	retval = -EAGAIN;
	if (nr_threads >= max_threads)
		goto bad_fork_cleanup_count;

	delayacct_tsk_init(p);	/* Must remain after dup_task_struct() */
	/* 清除 PF_SUPERPRIV(表示进程使用了超级用户权限) 和 PF_WQ_WORKER(使用了工作队列) */
	p->flags &= ~(PF_SUPERPRIV | PF_WQ_WORKER | PF_IDLE);
	/* 设置 PF_FORKNOEXEC 表明此子进程还没有进行 execve() 系统调用 */
	p->flags |= PF_FORKNOEXEC;

	/* 初始化子进程的子进程链表和兄弟进程链表为空 */
	INIT_LIST_HEAD(&p->children);
	INIT_LIST_HEAD(&p->sibling);

	/* 见 http://www.ibm.com/developerworks/cn/linux/l-rcu/ */
	rcu_copy_process(p);
	p->vfork_done = NULL;

	/* 初始化分配锁,此锁用于保护分配内存,文件,文件系统等操作 */
	spin_lock_init(&p->alloc_lock);

	/* 信号列表初始化,此列表保存被挂起的信号 */
	init_sigpending(&p->pending);

	/* 代码执行时间变量都置为0 */
	p->utime = p->stime = p->gtime = 0;
#ifdef CONFIG_ARCH_HAS_SCALED_CPUTIME
	p->utimescaled = p->stimescaled = 0;
#endif
	prev_cputime_init(&p->prev_cputime);

#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
	seqcount_init(&p->vtime.seqcount);
	p->vtime.starttime = 0;
	p->vtime.state = VTIME_INACTIVE;
#endif

#if defined(SPLIT_RSS_COUNTING)
	memset(&p->rss_stat, 0, sizeof(p->rss_stat));
#endif
	/* 此变量一般用于epoll和select,从父进程复制过来 */
	p->default_timer_slack_ns = current->timer_slack_ns;
	
	/* 初始化进程IO计数结构 */
	task_io_accounting_init(&p->ioac);
	acct_clear_integrals(p);
	
	/* 初始化cputime_expires结构 */
	posix_cpu_timers_init(p);

	/* 设置进程创建时间 */
	p->start_time = ktime_get_ns();
	p->real_start_time = ktime_get_boot_ns();

	/* io_context 和 audit_context 置空 */
	p->io_context = NULL;
	audit_set_context(p, NULL);

	/*
	 * cgroup和namespace类似,也是将进程进行分组,但它的目的和namespace不一样,
	 * namespace是为了隔离进程组之间的资源,而cgroup是为了对一组进程进行统一的资源监控和限制。
	 */
	cgroup_fork(p);
#ifdef CONFIG_NUMA
	p->mempolicy = mpol_dup(p->mempolicy);
	if (IS_ERR(p->mempolicy)) {
		retval = PTR_ERR(p->mempolicy);
		p->mempolicy = NULL;
		goto bad_fork_cleanup_threadgroup_lock;
	}
#endif
#ifdef CONFIG_CPUSETS
	p->cpuset_mem_spread_rotor = NUMA_NO_NODE;
	p->cpuset_slab_spread_rotor = NUMA_NO_NODE;
	seqcount_init(&p->mems_allowed_seq);
#endif
#ifdef CONFIG_TRACE_IRQFLAGS
	p->irq_events = 0;
	p->hardirqs_enabled = 0;
	p->hardirq_enable_ip = 0;
	p->hardirq_enable_event = 0;
	p->hardirq_disable_ip = _THIS_IP_;
	p->hardirq_disable_event = 0;
	p->softirqs_enabled = 1;
	p->softirq_enable_ip = _THIS_IP_;
	p->softirq_enable_event = 0;
	p->softirq_disable_ip = 0;
	p->softirq_disable_event = 0;
	p->hardirq_context = 0;
	p->softirq_context = 0;
#endif

	p->pagefault_disabled = 0;

#ifdef CONFIG_LOCKDEP
	p->lockdep_depth = 0; /* no locks held yet */
	p->curr_chain_key = 0;
	p->lockdep_recursion = 0;
	lockdep_init_task(p);
#endif

#ifdef CONFIG_DEBUG_MUTEXES
	p->blocked_on = NULL; /* not blocked yet */
#endif
#ifdef CONFIG_BCACHE
	p->sequential_io	= 0;
	p->sequential_io_avg	= 0;
#endif
	/* 初始化子进程的调度优先级和策略,在此并没有将此进程加入到运行队列,在copy_process返回之后加入 */ 
	/* Perform scheduler related setup. Assign this task to a CPU. */
	retval = sched_fork(clone_flags, p);
	if (retval)
		goto bad_fork_cleanup_policy;
	/* perf event是一个性能调优工具,具体见 http://blog.sina.com.cn/s/blog_98822316010122ex.html */
	retval = perf_event_init_task(p);
	if (retval)
		goto bad_fork_cleanup_policy;
	retval = audit_alloc(p);
	if (retval)
		goto bad_fork_cleanup_perf;
	/* copy all the process information */
	/* 初始化 p->sysvshm.shm_clist 链表头 */
	shm_init_task(p);
	retval = security_task_alloc(p, clone_flags);
	if (retval)
		goto bad_fork_cleanup_audit;

	/* copy_semundo, copy_files, copy_fs, copy_sighand, copy_signal, copy_mm, copy_namespaces, copy_io
	 * 都是根据clone_flags从父进程做相应的复制 
	 * */
	retval = copy_semundo(clone_flags, p);
	if (retval)
		goto bad_fork_cleanup_security;
	retval = copy_files(clone_flags, p);
	if (retval)
		goto bad_fork_cleanup_semundo;
	retval = copy_fs(clone_flags, p);
	if (retval)
		goto bad_fork_cleanup_files;
	retval = copy_sighand(clone_flags, p);
	if (retval)
		goto bad_fork_cleanup_fs;
		/* 如果创建的是线程,直接返回0,如果创建的是进程,则会将父进程的信号屏蔽和安排复制到子进程中 */
	retval = copy_signal(clone_flags, p);
	if (retval)
		goto bad_fork_cleanup_sighand;
	/* 
	 * 如果是进程,则将父进程的mm_struct结构复制到子进程中,然后修改当中属于子进程有别于父进程的信息(如页目录)
	 * 如果是线程,则将子线程的mm指针和active_mm指针都指向父进程的mm指针所指结构。
	 */
	retval = copy_mm(clone_flags, p);
	if (retval)
		goto bad_fork_cleanup_signal;
	retval = copy_namespaces(clone_flags, p);
	if (retval)
		goto bad_fork_cleanup_mm;
	retval = copy_io(clone_flags, p);
	if (retval)
		goto bad_fork_cleanup_namespaces;
	/* 
	 * 初始化子进程内核栈和thread_struct结构体
	 * 当进程切换时,进程的硬件上下文一般保存于三个地方: tss_struct(保存进程内核栈地址,I/O许可权限位),thread_struct(大部分非通用寄存器),进程内核栈(通用寄存器)
	 * copy_thread函数会将父进程的thread_struct和内核栈数据复制到子进程中,并将子进程的返回值置为0(x86返回值保存在eax中,arm保存在r0中,即把eax或者r0所在的内核栈数据置为0)
	 * copy_thread函数还会将子进程的eip寄存器值设置为ret_from_fork()的地址,即当子进程首次被调用就立即执行系统调用clone返回。
	 * 所以应用层调用fork()函数后,子进程返回0,父进程返回子进程ID(返回子进程ID在之后代码中会实现)
	 */
	retval = copy_thread_tls(clone_flags, stack_start, stack_size, p, tls);
	if (retval)
		goto bad_fork_cleanup_io;
	/* 判断是不是init进程 */
	if (pid != &init_struct_pid) {
		pid = alloc_pid(p->nsproxy->pid_ns_for_children);/* 分配pid */
		if (IS_ERR(pid)) {
			retval = PTR_ERR(pid);
			goto bad_fork_cleanup_thread;
		}
	}

#ifdef CONFIG_BLOCK
	p->plug = NULL;
#endif
#ifdef CONFIG_FUTEX
	p->robust_list = NULL;
#ifdef CONFIG_COMPAT
	p->compat_robust_list = NULL;
#endif
	INIT_LIST_HEAD(&p->pi_state_list);
	p->pi_state_cache = NULL;
#endif
	/*
	 * sigaltstack should be cleared when sharing the same VM
	 */
	/*
	 * 如果共享VM或者vfork创建,信号栈清空
	 */
	if ((clone_flags & (CLONE_VM|CLONE_VFORK)) == CLONE_VM)
		sas_ss_reset(p);

	/*
	 * Syscall tracing and stepping should be turned off in the
	 * child regardless of CLONE_PTRACE.
	 */
	/*
	 * 系统调用跟踪时应该禁止单步执行
	 */
	user_disable_single_step(p);
	clear_tsk_thread_flag(p, TIF_SYSCALL_TRACE);
#ifdef TIF_SYSCALL_EMU
	clear_tsk_thread_flag(p, TIF_SYSCALL_EMU);
#endif
	clear_all_latency_tracing(p);

	/* ok, now we should be set up.. */
	/* 将子进程的PID设置为分配的PID在全局namespace中分配的值,在不同namespace中进程的PID不同,而p->pid保存的是全局的namespace中所分配的PID */
	p->pid = pid_nr(pid);
	if (clone_flags & CLONE_THREAD) {/* 创建的是线程 */
		p->exit_signal = -1;
		p->group_leader = current->group_leader;/* 线程组的所有线程的group_leader都一致 */
		p->tgid = current->tgid;/* 线程组的所有线程的tgid都一致,使用getpid返回的就是tgid */
	} else {
		/* 创建的是子进程 */
		if (clone_flags & CLONE_PARENT)
			p->exit_signal = current->group_leader->exit_signal;
		else
			p->exit_signal = (clone_flags & CSIGNAL);
		p->group_leader = p;
		p->tgid = p->pid;/* tgid与pid一致,所以当创建子线程时,tgid与主线程的一致 */
	}

	p->nr_dirtied = 0;/* 初始化页框中脏页数量为0 */
	p->nr_dirtied_pause = 128 >> (PAGE_SHIFT - 10);/* 初始化脏页数量临界值,当脏页数量到达临界值时,会调用balance_dirty_pages()将脏页写入磁盘 */
	p->dirty_paused_when = 0;/* 将脏页写入磁盘的开始时间 */

	p->pdeath_signal = 0;
	INIT_LIST_HEAD(&p->thread_group);/* 初始化线程组链表为空 */
	p->task_works = NULL;

	cgroup_threadgroup_change_begin(current);
	/*
	 * Ensure that the cgroup subsystem policies allow the new process to be
	 * forked. It should be noted the the new process's css_set can be changed
	 * between here and cgroup_post_fork() if an organisation operation is in
	 * progress.
	 */
	retval = cgroup_can_fork(p);
	if (retval)
		goto bad_fork_free_pid;

	/*
	 * Make it visible to the rest of the system, but dont wake it up yet.
	 * Need tasklist lock for parent etc handling!
	 */
	/* 到此系统中已经存在此进程(线程),但是它还不能够执行,需要等待父进程对其处理,这里会上锁 */
	write_lock_irq(&tasklist_lock);

	/* CLONE_PARENT re-uses the old parent */
	if (clone_flags & (CLONE_PARENT|CLONE_THREAD)) {
		/* 创建的是兄弟进程或者相同线程组线程 */
		/* 其父进程为父进程的父进程 */
		p->real_parent = current->real_parent;
		/* 其父进程执行域为父进程的父进程执行域 */
		p->parent_exec_id = current->parent_exec_id;
	} else {
		/* 创建的是子进程 */
		/* 父进程为父进程 */
		p->real_parent = current;
		/* 父进程的执行域为父进程的执行域 */
		p->parent_exec_id = current->self_exec_id;
	}

	klp_copy_process(p);
	/* 当前进程信号处理上锁,这里应该是禁止了信号处理 */
	spin_lock(&current->sighand->siglock);

	/*
	 * Copy seccomp details explicitly here, in case they were changed
	 * before holding sighand lock.
	 */
	/*
	 * seccomp与系统安全有关,具体见 http://note.sdo.com/u/634687868481358385/NoteContent/M5cEN~kkf9BFnM4og00239
	 */
	copy_seccomp(p);
	/*
	 * 在fork之前,进程组和会话信号都需要送到父亲结点,而在fork之后,这些信号需要送到父亲和孩子结点。
	 * 如果我们在将新进程添加到进程组的过程中出现一个信号,而这个挂起信号会导致当前进程退出(current),我们的子进程就不能够被kill或者退出了
	 * 所以这里要检测父进程有没有信号被挂起。
	 */
	rseq_fork(p, clone_flags);

	/* Don't start children in a dying pid namespace */
	if (unlikely(!(ns_of_pid(pid)->pid_allocated & PIDNS_ADDING))) {
		retval = -ENOMEM;
		goto bad_fork_cancel_cgroup;
	}

	/* Let kill terminate clone/fork in the middle */
	if (fatal_signal_pending(current)) {
		retval = -EINTR;
		goto bad_fork_cancel_cgroup;
	}


	init_task_pid_links(p);
	if (likely(p->pid)) {
		/* 如果子进程需要跟踪,就将 current->parent 赋值给 tsk->parent ,并将子进程插入调试程序的跟踪链表中 */
		ptrace_init_task(p, (clone_flags & CLONE_PTRACE) || trace);

		init_task_pid(p, PIDTYPE_PID, pid);
		/* 如果是子进程(其实就是判断 p->exit_signal 是否大于等于0,创建的是线程的话,exit_signal的值为-1) */
		if (thread_group_leader(p)) {
			/* p->pids[PIDTYPE_PGID].pid = current->group_leader->pids[PIDTYPE_PGID].pid; PGID为进程组ID,所以直接复制父进程的pgid */
			init_task_pid(p, PIDTYPE_TGID, pid);
			/* p->pids[PIDTYPE_SID].pid = current->group_leader->pids[PIDTYPE_SID].pid; SID为会话组ID,当没有使用setsid()时,子进程的sid与父进程一致 */
			init_task_pid(p, PIDTYPE_PGID, task_pgrp(current));
			init_task_pid(p, PIDTYPE_SID, task_session(current));
			/* return pid->numbers[pid->level].nr == 1; 判断新进程是否处于一个新创建的namespace中(新进程所在的新namespace中的pid会为1,以此判断) */
			if (is_child_reaper(pid)) {
				/* 将当前namespace的init进程设置为此新进程 */
				ns_of_pid(pid)->child_reaper = p;
				p->signal->flags |= SIGNAL_UNKILLABLE;
			}
			p->signal->shared_pending.signal = delayed.signal;
			p->signal->tty = tty_kref_get(current->signal->tty);
			/*
			 * Inherit has_child_subreaper flag under the same
			 * tasklist_lock with adding child to the process tree
			 * for propagate_has_child_subreaper optimization.
			 */
			p->signal->has_child_subreaper = p->real_parent->signal->has_child_subreaper ||
							 p->real_parent->signal->is_child_subreaper;
			/* 将此进程添加到父进程的子进程链表 */
			list_add_tail(&p->sibling, &p->real_parent->children);
			/* 将此进程task_struct加入到task链表中 */
			list_add_tail_rcu(&p->tasks, &init_task.tasks);
			/* 将新进程描述符的pgid结构插入pgid_hash */
			attach_pid(p, PIDTYPE_TGID);
			/* 将新进程描述符的sid结构插入sid_hash */
			attach_pid(p, PIDTYPE_PGID);
			attach_pid(p, PIDTYPE_SID);
			/* 当前cpu进程数量加1 */
			__this_cpu_inc(process_counts);
		} else {
			/* 创建的是线程,这里的处理导致了线程会共享信号 */
			current->signal->nr_threads++;
			atomic_inc(&current->signal->live);
			atomic_inc(&current->signal->sigcnt);
			task_join_group_stop(p);
			/* 将新线程的thread_group结点加入到线程组的领头线程的thread_group链表中 */
			list_add_tail_rcu(&p->thread_group,
					  &p->group_leader->thread_group);
			/* 将新线程的thread_node结点加入的新线程的signal->thread_head中 */
			list_add_tail_rcu(&p->thread_node,
					  &p->signal->thread_head);
		}
		/* 将新进程描述符的pid结构插入pid_hash */
		attach_pid(p, PIDTYPE_PID);
		nr_threads++;/* 当前系统进程数加1 */
	}
	total_forks++;/* 已创建的进程数量加1 */
	hlist_del_init(&delayed.node);
	spin_unlock(&current->sighand->siglock);/* 释放当前进程信号处理锁 */
	syscall_tracepoint_update(p);
	write_unlock_irq(&tasklist_lock);/* 释放tasklist_lock锁 */

	proc_fork_connector(p);/* 将新进程与proc文件系统进行关联 */
	cgroup_post_fork(p);
	cgroup_threadgroup_change_end(current);/* 如果创建的是线程,释放此锁 */
	perf_event_fork(p);

	trace_task_newtask(p, clone_flags);
	uprobe_copy_process(p, clone_flags);
	/* 返回新进程的task_struct结构 */
	return p;

bad_fork_cancel_cgroup:
	spin_unlock(&current->sighand->siglock);
	write_unlock_irq(&tasklist_lock);
	cgroup_cancel_fork(p);
bad_fork_free_pid:
	cgroup_threadgroup_change_end(current);
	if (pid != &init_struct_pid)
		free_pid(pid);
bad_fork_cleanup_thread:
	exit_thread(p);
bad_fork_cleanup_io:
	if (p->io_context)
		exit_io_context(p);
bad_fork_cleanup_namespaces:
	exit_task_namespaces(p);
bad_fork_cleanup_mm:
	if (p->mm)
		mmput(p->mm);
bad_fork_cleanup_signal:
	if (!(clone_flags & CLONE_THREAD))
		free_signal_struct(p->signal);
bad_fork_cleanup_sighand:
	__cleanup_sighand(p->sighand);
bad_fork_cleanup_fs:
	exit_fs(p); /* blocking */
bad_fork_cleanup_files:
	exit_files(p); /* blocking */
bad_fork_cleanup_semundo:
	exit_sem(p);
bad_fork_cleanup_security:
	security_task_free(p);
bad_fork_cleanup_audit:
	audit_free(p);
bad_fork_cleanup_perf:
	perf_event_free_task(p);
bad_fork_cleanup_policy:
	lockdep_free_task(p);
#ifdef CONFIG_NUMA
	mpol_put(p->mempolicy);
bad_fork_cleanup_threadgroup_lock:
#endif
	delayacct_tsk_free(p);
bad_fork_cleanup_count:
	atomic_dec(&p->cred->user->processes);
	exit_creds(p);
bad_fork_free:
	p->state = TASK_DEAD;
	put_task_stack(p);
	free_task(p);
fork_out:
	spin_lock_irq(&current->sighand->siglock);
	hlist_del_init(&delayed.node);
	spin_unlock_irq(&current->sighand->siglock);
	return ERR_PTR(retval);
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章