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);
}

 

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