国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Linux > 正文

關(guān)于linux系統(tǒng)如何實(shí)現(xiàn)fork的研究(二)

2024-06-28 13:22:07
字體:
供稿:網(wǎng)友
關(guān)于linux系統(tǒng)如何實(shí)現(xiàn)fork的研究(二)本文為原創(chuàng),轉(zhuǎn)載請注明:http://www.CUOXin.com/tolimit/

引言

  前一篇關(guān)于linux系統(tǒng)如何實(shí)現(xiàn)fork的研究(一)通過代碼已經(jīng)說明了從用戶態(tài)怎么通過軟中斷實(shí)現(xiàn)調(diào)用系統(tǒng)調(diào)用clone函數(shù),而clone函數(shù)的精華copy_PRocess函數(shù)就在此篇文章中進(jìn)行分析。我們知道,在linux系統(tǒng)中,應(yīng)用層可以創(chuàng)建子進(jìn)程和子線程(輕量級進(jìn)程)兩種程序分支結(jié)構(gòu)。而對于linux內(nèi)核而且,并不詳細(xì)區(qū)分子進(jìn)程和子線程(輕量級進(jìn)程)的區(qū)別,他們都使用的是task_struct結(jié)構(gòu)(此結(jié)構(gòu)極其復(fù)雜,包含非常多的數(shù)據(jù)結(jié)構(gòu)),而不同的是子進(jìn)程和子線程的task_struct初始化結(jié)果不同。task_struct結(jié)構(gòu)是一個(gè)進(jìn)程或線程的標(biāo)識和存在的憑證,調(diào)度程序就是通過task_struct結(jié)構(gòu)來區(qū)分不同的進(jìn)程(線程)。里面包含了進(jìn)程(線程)所有需要用到的結(jié)構(gòu)(內(nèi)存描述符,文件描述符,信號描述符,信號處理函數(shù),調(diào)度優(yōu)先級等)。而我們知道,一個(gè)進(jìn)程(線程)不止有自己的task_struck結(jié)構(gòu),還必須有一個(gè)自己的內(nèi)核棧,當(dāng)執(zhí)行進(jìn)程切換時(shí),部分進(jìn)程上下文會保存于其進(jìn)程的內(nèi)核棧中,而中斷發(fā)生時(shí)的中斷上下文也會保存于正在持續(xù)的進(jìn)程內(nèi)核棧中。在copy_process函數(shù)中內(nèi)核棧的初始化導(dǎo)致了fork()的兩次返回值不同(之后會說明)。當(dāng)然,copy_process還涉及到許多操作,比如新進(jìn)程(線程)的安全檢測,pid的分配,關(guān)系調(diào)整(父子進(jìn)程、進(jìn)程組關(guān)系,命名空間關(guān)系等),內(nèi)存結(jié)構(gòu)的初始化等等,這些我們在之后的代碼中慢慢道來。

copy_process
  1 /* 代碼目錄:linux源碼/kernel/Fork.c */  2   3 static struct task_struct *copy_process(unsigned long clone_flags,  4                     unsigned long stack_start,  5                     unsigned long stack_size,  6                     int __user *child_tidptr,  7                     struct pid *pid,  8                     int trace)  9 { 10     int retval; 11     struct task_struct *p; 12  13     /* CLONE_FS 不能與 CLONE_NEWNS 或 CLONE_NEWUSER 同時(shí)設(shè)置 */ 14     if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS)) 15         return ERR_PTR(-EINVAL); 16  17     if ((clone_flags & (CLONE_NEWUSER|CLONE_FS)) == (CLONE_NEWUSER|CLONE_FS)) 18         return ERR_PTR(-EINVAL); 19  20     /* 創(chuàng)建線程時(shí)線程之間要共享信號處理函數(shù) */ 21     if ((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND)) 22         return ERR_PTR(-EINVAL); 23  24     /*  25      * 父子進(jìn)程共享信號處理函數(shù)時(shí)必須共享內(nèi)存地址空間 26      * 這就是為什么書上寫的fork出來的父子進(jìn)程有其獨(dú)立的信號處理函數(shù),因?yàn)樗麄兊膬?nèi)存地址空間不同 27      */ 28     if ((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM)) 29         return ERR_PTR(-EINVAL); 30  31     /* 32      * 防止參數(shù)init進(jìn)程的兄弟進(jìn)程 33      * 只有init進(jìn)程的 signal->flags & SIGNAL_UNKILLABLE 為真 34      * 因?yàn)楫?dāng)進(jìn)程退出時(shí)實(shí)際上是成為了僵尸進(jìn)程(zombie),而要通過init進(jìn)程將它回收,而如果此進(jìn)程為init的兄弟進(jìn)程,則沒辦法將其回收 35      */ 36     if ((clone_flags & CLONE_PARENT) && 37                 current->signal->flags & SIGNAL_UNKILLABLE) 38         return ERR_PTR(-EINVAL); 39  40     /* 如果新的進(jìn)程將會有新的用戶空間或者pid,則不能讓它共享父進(jìn)程的線程組或者信號處理或者父進(jìn)程 */ 41     if (clone_flags & CLONE_SIGHAND) { 42         if ((clone_flags & (CLONE_NEWUSER | CLONE_NEWPID)) || 43             (task_active_pid_ns(current) != 44                 current->nsproxy->pid_ns_for_children)) 45             return ERR_PTR(-EINVAL); 46     } 47  48     /* 附加安全檢查 */ 49     retval = security_task_create(clone_flags); 50     if (retval) 51         goto fork_out; 52  53     retval = -ENOMEM; 54     /* 為新進(jìn)程分配struct task_struct內(nèi)存和內(nèi)核棧內(nèi)存 */ 55     p = dup_task_struct(current); 56     if (!p) 57         goto fork_out; 58  59     /* ftrace是用于內(nèi)核性能分析和跟蹤的 */ 60     ftrace_graph_init_task(p); 61  62     /* futex初始化,其用于SYSTEM V ipC,具體可見 http://blog.chinaunix.net/uid-7295895-id-3011238.html */ 63     rt_mutex_init_task(p); 64  65 #ifdef CONFIG_PROVE_LOCKING 66     DEBUG_LOCKS_WARN_ON(!p->hardirqs_enabled); 67     DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled); 68 #endif 69     retval = -EAGAIN; 70     /* 檢查 tsk->signal->rlim[RLIMIT_NPROC].rlim_cur是否小于等于用戶所擁有的進(jìn)程數(shù),rlim結(jié)構(gòu)體表示相關(guān)資源的最大值 */ 71      if (atomic_read(&p->real_cred->user->processes) >= task_rlimit(p, RLIMIT_NPROC)) { 72         /* INIT_USER是root權(quán)限。檢查父進(jìn)程是否有root權(quán)限 */ 73         if (p->real_cred->user != INIT_USER && !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN)) 74             goto bad_fork_free; 75     } 76     current->flags &= ~PF_NPROC_EXCEEDED; 77  78     /* 將父進(jìn)程的cred復(fù)制到子進(jìn)程的real_cred和cred。struct cred用于安全操作的結(jié)構(gòu) */ 79     retval = copy_creds(p, clone_flags); 80     if (retval < 0) 81         goto bad_fork_free; 82  83     retval = -EAGAIN; 84     /* 進(jìn)程數(shù)量是否超出系統(tǒng)允許最大進(jìn)程數(shù)量,最大進(jìn)程數(shù)量跟內(nèi)存有關(guān),一般原則是所有的進(jìn)程內(nèi)核棧(默認(rèn)8K)加起來不超過總內(nèi)存的1/8,可通過/proc/sys/kernel/threads-max改寫此值 */ 85     if (nr_threads >= max_threads) 86         goto bad_fork_cleanup_count; 87  88     /* 如果實(shí)現(xiàn)新進(jìn)程的執(zhí)行域和可執(zhí)行格式的內(nèi)核函數(shù)都包含在內(nèi)核模塊中,則遞增其使用計(jì)數(shù) */ 89     if (!try_module_get(task_thread_info(p)->exec_domain->module)) 90         goto bad_fork_cleanup_count; 91  92     delayacct_tsk_init(p);    /* Must remain after dup_task_struct() */ 93  94     /* 清除 PF_SUPERPRIV(表示進(jìn)程使用了超級用戶權(quán)限) 和 PF_WQ_WORKER(使用了工作隊(duì)列) */ 95     p->flags &= ~(PF_SUPERPRIV | PF_WQ_WORKER); 96     /* 設(shè)置 PF_FORKNOEXEC 表明此子進(jìn)程還沒有進(jìn)行 execve() 系統(tǒng)調(diào)用 */ 97     p->flags |= PF_FORKNOEXEC; 98  99     /* 初始化子進(jìn)程的子進(jìn)程鏈表和兄弟進(jìn)程鏈表為空 */100     INIT_LIST_HEAD(&p->children);101     INIT_LIST_HEAD(&p->sibling);102     /* 見 http://www.ibm.com/developerworks/cn/linux/l-rcu/ */103     rcu_copy_process(p);104     p->vfork_done = NULL;105     /* 初始化分配鎖,此鎖用于保護(hù)分配內(nèi)存,文件,文件系統(tǒng)等操作 */106     spin_lock_init(&p->alloc_lock);107 108     /* 信號列表初始化,此列表保存被掛起的信號 */109     init_sigpending(&p->pending);110 111     /* 代碼執(zhí)行時(shí)間變量都置為0 */112     p->utime = p->stime = p->gtime = 0;113     p->utimescaled = p->stimescaled = 0;114 #ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE115     p->prev_cputime.utime = p->prev_cputime.stime = 0;116 #endif117 #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN118     seqlock_init(&p->vtime_seqlock);119     p->vtime_snap = 0;120     p->vtime_snap_whence = VTIME_SLEEPING;121 #endif122 123 #if defined(SPLIT_rss_COUNTING)124     memset(&p->rss_stat, 0, sizeof(p->rss_stat));125 #endif126     /* 此變量一般用于epoll和select,從父進(jìn)程復(fù)制過來 */127     p->default_timer_slack_ns = current->timer_slack_ns;128 129     /* 初始化進(jìn)程IO計(jì)數(shù)結(jié)構(gòu) */130     task_io_accounting_init(&p->ioac);131     acct_clear_integrals(p);132 133     /* 初始化cputime_expires結(jié)構(gòu) */134     posix_cpu_timers_init(p);135 136     /* 設(shè)置進(jìn)程創(chuàng)建時(shí)間 */137     p->start_time = ktime_get_ns();138     p->real_start_time = ktime_get_boot_ns();139 140     /* io_context 和 audit_context 置空 */141     p->io_context = NULL;142     p->audit_context = NULL;143     /* 如果創(chuàng)建的是線程,因?yàn)樾枰薷牡疆?dāng)前進(jìn)程的描述符,會先上鎖 */144     if (clone_flags & CLONE_THREAD)145         threadgroup_change_begin(current);146     cgroup_fork(p);147 #ifdef CONFIG_NUMA148     p->mempolicy = mpol_dup(p->mempolicy);149     if (IS_ERR(p->mempolicy)) {150         retval = PTR_ERR(p->mempolicy);151         p->mempolicy = NULL;152         goto bad_fork_cleanup_threadgroup_lock;153     }154 #endif155 #ifdef CONFIG_CPUSETS156     p->cpuset_mem_spread_rotor = NUMA_NO_NODE;157     p->cpuset_slab_spread_rotor = NUMA_NO_NODE;158     seqcount_init(&p->mems_allowed_seq);159 #endif160 #ifdef CONFIG_TRACE_IRQFLAGS161     p->irq_events = 0;162     p->hardirqs_enabled = 0;163     p->hardirq_enable_ip = 0;164     p->hardirq_enable_event = 0;165     p->hardirq_disable_ip = _THIS_IP_;166     p->hardirq_disable_event = 0;167     p->softirqs_enabled = 1;168     p->softirq_enable_ip = _THIS_IP_;169     p->softirq_enable_event = 0;170     p->softirq_disable_ip = 0;171     p->softirq_disable_event = 0;172     p->hardirq_context = 0;173     p->softirq_context = 0;174 #endif175 #ifdef CONFIG_LOCKDEP176     p->lockdep_depth = 0; /* no locks held yet */177     p->curr_chain_key = 0;178     p->lockdep_recursion = 0;179 #endif180 181 #ifdef CONFIG_DEBUG_MUTEXES182     p->blocked_on = NULL; /* not blocked yet */183 #endif184 #ifdef CONFIG_BCACHE185     p->sequential_io    = 0;186     p->sequential_io_avg    = 0;187 #endif188 189 190     /* 初始化子進(jìn)程的調(diào)度優(yōu)先級和策略,在此并沒有將此進(jìn)程加入到運(yùn)行隊(duì)列,在copy_process返回之后加入 */    191     retval = sched_fork(clone_flags, p);192     if (retval)193         goto bad_fork_cleanup_policy;194 195     /* perf event是一個(gè)性能調(diào)優(yōu)工具,具體見 http://blog.sina.com.cn/s/blog_98822316010122ex.html */196     retval = perf_event_init_task(p);197     if (retval)198         goto bad_fork_cleanup_policy;199     retval = audit_alloc(p);200     if (retval)201         goto bad_fork_cleanup_perf;202     /* 初始化 p->sysvshm.shm_clist 鏈表頭 */203     shm_init_task(p);204 205     /* copy_semundo, copy_files, copy_fs, copy_sighand, copy_signal, copy_mm, copy_namespaces, copy_io都是根據(jù)clone_flags從父進(jìn)程做相應(yīng)的復(fù)制 */206     retval = copy_semundo(clone_flags, p);207     if (retval)208         goto bad_fork_cleanup_audit;209     retval = copy_files(clone_flags, p);210     if (retval)211         goto bad_fork_cleanup_semundo;212     retval = copy_fs(clone_flags, p);213     if (retval)214         goto bad_fork_cleanup_files;215     /* 判斷是否設(shè)置 CLONE_SIGHAND ,如果是(線程必須為是),增加父進(jìn)行的sighand引用計(jì)數(shù),如果否(創(chuàng)建的必定是子進(jìn)程),將父線程的sighand_struct復(fù)制到子進(jìn)程中 */216     retval = copy_sighand(clone_flags, p);217     if (retval)218         goto bad_fork_cleanup_fs;219     /* 如果創(chuàng)建的是線程,直接返回0,如果創(chuàng)建的是進(jìn)程,則會將父進(jìn)程的信號屏蔽和安排復(fù)制到子進(jìn)程中 */220     retval = copy_signal(clone_flags, p);221     if (retval)222         goto bad_fork_cleanup_sighand;223     /* 224      * 如果是進(jìn)程,則將父進(jìn)程的mm_struct結(jié)構(gòu)復(fù)制到子進(jìn)程中,然后修改當(dāng)中屬于子進(jìn)程有別于父進(jìn)程的信息(如頁目錄)225      * 如果是線程,則將子線程的mm指針和active_mm指針都指向父進(jìn)程的mm指針?biāo)附Y(jié)構(gòu)。226      */227     retval = copy_mm(clone_flags, p);228     if (retval)229         goto bad_fork_cleanup_signal;230     retval = copy_namespaces(clone_flags, p);231     if (retval)232         goto bad_fork_cleanup_mm;233     retval = copy_io(clone_flags, p);234     if (retval)235         goto bad_fork_cleanup_namespaces;236     237     /* 238      * 初始化子進(jìn)程內(nèi)核棧和thread_struct結(jié)構(gòu)體239      * 當(dāng)進(jìn)程切換時(shí),進(jìn)程的硬件上下文一般保存于三個(gè)地方: tss_struct(保存進(jìn)程內(nèi)核棧地址,I/O許可權(quán)限位),thread_struct(大部分非通用寄存器),進(jìn)程內(nèi)核棧(通用寄存器)240      * copy_thread函數(shù)會將父進(jìn)程的thread_struct和內(nèi)核棧數(shù)據(jù)復(fù)制到子進(jìn)程中,并將子進(jìn)程的返回值置為0(x86返回值保存在eax中,arm保存在r0中,即把eax或者r0所在的內(nèi)核棧數(shù)據(jù)置為0)241      * copy_thread函數(shù)還會將子進(jìn)程的eip寄存器值設(shè)置為ret_from_fork()的地址,即當(dāng)子進(jìn)程首次被調(diào)用就立即執(zhí)行系統(tǒng)調(diào)用clone返回。242      * 所以應(yīng)用層調(diào)用fork()函數(shù)后,子進(jìn)程返回0,父進(jìn)程返回子進(jìn)程ID(返回子進(jìn)程ID在之后代碼中會實(shí)現(xiàn))243      */244     retval = copy_thread(clone_flags, stack_start, stack_size, p);245     if (retval)246         goto bad_fork_cleanup_io;247 248     /* 判斷是不是init進(jìn)程 */249     if (pid != &init_struct_pid) {250         retval = -ENOMEM;251         /* 分配pid */252         pid = alloc_pid(p->nsproxy->pid_ns_for_children);253         if (!pid)254             goto bad_fork_cleanup_io;255     }256 257     /* 如果設(shè)置了CLONE_CHILD_SETTID則將task_struct中的set_child_tid指向用戶空間的child_tidptr,否則置空 */258     p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL;259     /* 如果設(shè)置了CLONE_CHILD_CLEARTID則將task_struct中的clear_child_tid指向用戶空間的child_tidptr,否則置空 */260     p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr : NULL;261     262 #ifdef CONFIG_BLOCK263     p->plug = NULL;264 #endif265 #ifdef CONFIG_FUTEX266     p->robust_list = NULL;267 #ifdef CONFIG_COMPAT268     p->compat_robust_list = NULL;269 #endif270     INIT_LIST_HEAD(&p->pi_state_list);271     p->pi_state_cache = NULL;272 #endif273     /*274      * 如果共享VM或者vfork創(chuàng)建,信號棧清空275      */276     if ((clone_flags & (CLONE_VM|CLONE_VFORK)) == CLONE_VM)277         p->sas_ss_sp = p->sas_ss_size = 0;278 279     /*280      * 系統(tǒng)調(diào)用跟蹤時(shí)應(yīng)該禁止單步執(zhí)行281      */282     user_disable_single_step(p);283     clear_tsk_thread_flag(p, TIF_SYSCALL_TRACE);284 #ifdef TIF_SYSCALL_EMU285     clear_tsk_thread_flag(p, TIF_SYSCALL_EMU);286 #endif287     clear_all_latency_tracing(p);288 289 290     /* 將子進(jìn)程的PID設(shè)置為分配的PID在全局namespace中分配的值,在不同namespace中進(jìn)程的PID不同,而p->pid保存的是全局的namespace中所分配的PID */291     p->pid = pid_nr(pid);292     if (clone_flags & CLONE_THREAD) {293         /* 創(chuàng)建的是線程 */294         p->exit_signal = -1;295         /* 線程組的所有線程的group_leader都一致 */296         p->group_leader = current->group_leader;297         /* 線程組的所有線程的tgid都一致,使用getpid返回的就是tgid */298         p->tgid = current->tgid;299     } else {300         /* 創(chuàng)建的是子進(jìn)程 */301         if (clone_flags & CLONE_PARENT)302             p->exit_signal = current->group_leader->exit_signal;303         else304             p->exit_signal = (clone_flags & CSIGNAL);305         p->group_leader = p;306         /* tgid與pid一致,所以當(dāng)創(chuàng)建子線程時(shí),tgid與主線程的一致 */307         p->tgid = p->pid;308     }309 310     /* 初始化頁框中臟頁數(shù)量為0 */311     p->nr_dirtied = 0;312     /* 初始化臟頁數(shù)量臨界值,當(dāng)臟頁數(shù)量到達(dá)臨界值時(shí),會調(diào)用balance_dirty_pages()將臟頁寫入磁盤 */313     p->nr_dirtied_pause = 128 >> (PAGE_SHIFT - 10);314     /* 將臟頁寫入磁盤的開始時(shí)間 */315     p->dirty_paused_when = 0;316 317     p->pdeath_signal = 0;318     /* 初始化線程組鏈表為空 */319     INIT_LIST_HEAD(&p->thread_group);320     p->task_works = NULL;321 322 323     /* 到此系統(tǒng)中已經(jīng)存在此進(jìn)程(線程),但是它還不能夠執(zhí)行,需要等待父進(jìn)程對其處理,這里會上鎖 */324     write_lock_irq(&tasklist_lock);325 326     if (clone_flags & (CLONE_PARENT|CLONE_THREAD)) {327         /* 創(chuàng)建的是兄弟進(jìn)程或者相同線程組線程 */328         /* 其父進(jìn)程為父進(jìn)程的父進(jìn)程 */329         p->real_parent = current->real_parent;330         /* 其父進(jìn)程執(zhí)行域?yàn)楦高M(jìn)程的父進(jìn)程執(zhí)行域 */331         p->parent_exec_id = current->parent_exec_id;332     } else {333         /* 創(chuàng)建的是子進(jìn)程 */334         /* 父進(jìn)程為父進(jìn)程 */335         p->real_parent = current;336         /* 父進(jìn)程的執(zhí)行域?yàn)楦高M(jìn)程的執(zhí)行域 */337         p->parent_exec_id = current->self_exec_id;338     }339 340     /* 當(dāng)前進(jìn)程信號處理上鎖,這里應(yīng)該是禁止了信號處理 */341     spin_lock(&current->sighand->siglock);342 343     /*344      * seccomp與系統(tǒng)安全有關(guān),具體見 http://note.sdo.com/u/634687868481358385/NoteContent/M5cEN~kkf9BFnM4og00239345      */346     copy_seccomp(p);347 348     /*349      * 在fork之前,進(jìn)程組和會話信號都需要送到父親結(jié)點(diǎn),而在fork之后,這些信號需要送到父親和孩子結(jié)點(diǎn)。350      * 如果我們在將新進(jìn)程添加到進(jìn)程組的過程中出現(xiàn)一個(gè)信號,而這個(gè)掛起信號會導(dǎo)致當(dāng)前進(jìn)程退出(current),我們的子進(jìn)程就不能夠被kill或者退出了351      * 所以這里要檢測父進(jìn)程有沒有信號被掛起。352      */353     recalc_sigpending();354     if (signal_pending(current)) {355         /* 包含有掛起進(jìn)程,錯(cuò)誤 */356         spin_unlock(&current->sighand->siglock);357         write_unlock_irq(&tasklist_lock);358         retval = -ERESTARTNOINTR;359         goto bad_fork_free_pid;360     }361 362     if (likely(p->pid)) {363         /* 如果子進(jìn)程需要跟蹤,就將 current->parent 賦值給 tsk->parent ,并將子進(jìn)程插入調(diào)試程序的跟蹤鏈表中 */364         ptrace_init_task(p, (clone_flags & CLONE_PTRACE) || trace);365 366         /* p->pids[PIDTYPE_PID].pid = pid; */367         init_task_pid(p, PIDTYPE_PID, pid);368 369         /* 如果是子進(jìn)程(其實(shí)就是判斷 p->exit_signal 是否大于等于0,創(chuàng)建的是線程的話,exit_signal的值為-1) */370         if (thread_group_leader(p)) {371             /* p->pids[PIDTYPE_PGID].pid = current->group_leader->pids[PIDTYPE_PGID].pid; PGID為進(jìn)程組ID,所以直接復(fù)制父進(jìn)程的pgid */372             init_task_pid(p, PIDTYPE_PGID, task_pgrp(current));373             /* p->pids[PIDTYPE_SID].pid = current->group_leader->pids[PIDTYPE_SID].pid; SID為會話組ID,當(dāng)沒有使用setsid()時(shí),子進(jìn)程的sid與父進(jìn)程一致 */374             init_task_pid(p, PIDTYPE_SID, task_session(current));375 376             /* return pid->numbers[pid->level].nr == 1; 判斷新進(jìn)程是否處于一個(gè)新創(chuàng)建的namespace中(新進(jìn)程所在的新namespace中的pid會為1,以此判斷) */377             if (is_child_reaper(pid)) {378                 /* 將當(dāng)前namespace的init進(jìn)程設(shè)置為此新進(jìn)程 */379                 ns_of_pid(pid)->child_reaper = p;380                 p->signal->flags |= SIGNAL_UNKILLABLE;381             }382 383             p->signal->leader_pid = pid;384             p->signal->tty = tty_kref_get(current->signal->tty);385 386             /* 將此進(jìn)程添加到父進(jìn)程的子進(jìn)程鏈表 */387             list_add_tail(&p->sibling, &p->real_parent->children);388             /* 將此進(jìn)程task_struct加入到task鏈表中 */389             list_add_tail_rcu(&p->tasks, &init_task.tasks);390             /* 將新進(jìn)程描述符的pgid結(jié)構(gòu)插入pgid_hash */391             attach_pid(p, PIDTYPE_PGID);392             /* 將新進(jìn)程描述符的sid結(jié)構(gòu)插入sid_hash */393             attach_pid(p, PIDTYPE_SID);394             /* 當(dāng)前cpu進(jìn)程數(shù)量加1 */395             __this_cpu_inc(process_counts);396         } else {397             /* 創(chuàng)建的是線程,這里的處理導(dǎo)致了線程會共享信號 */398             current->signal->nr_threads++;399             atomic_inc(&current->signal->live);400             atomic_inc(&current->signal->sigcnt);401             /* 將新線程的thread_group結(jié)點(diǎn)加入到線程組的領(lǐng)頭線程的thread_group鏈表中 */402             list_add_tail_rcu(&p->thread_group,403                       &p->group_leader->thread_group);404             /* 將新線程的thread_node結(jié)點(diǎn)加入的新線程的signal->thread_head中 */405             list_add_tail_rcu(&p->thread_node,406                       &p->signal->thread_head);407         }408         /* 將新進(jìn)程描述符的pid結(jié)構(gòu)插入pid_hash */409         attach_pid(p, PIDTYPE_PID);410         /* 當(dāng)前系統(tǒng)進(jìn)程數(shù)加1 */411         nr_threads++;412     }413 414     /* 已創(chuàng)建的進(jìn)程數(shù)量加1 */415     total_forks++;416     /* 釋放當(dāng)前進(jìn)程信號處理鎖 */417     spin_unlock(&current->sighand->siglock);418     syscall_tracepoint_update(p);419     /* 釋放tasklist_lock鎖 */420     write_unlock_irq(&tasklist_lock);421 422     /* 將新進(jìn)程與proc文件系統(tǒng)進(jìn)行關(guān)聯(lián) */423     proc_fork_connector(p);424     cgroup_post_fork(p);425     /* 如果創(chuàng)建的是線程,釋放此鎖 */426     if (clone_flags & CLONE_THREAD)427         threadgroup_change_end(current);428     perf_event_fork(p);429 430     trace_task_newtask(p, clone_flags);431     uprobe_copy_process(p, clone_flags);432 433     /* 返回新進(jìn)程的task_struct結(jié)構(gòu) */434     return p;435 436     /* 以下為執(zhí)行期間的錯(cuò)誤處理 */437 bad_fork_free_pid:438     if (pid != &init_struct_pid)439         free_pid(pid);440 bad_fork_cleanup_io:441     if (p->io_context)442         exit_io_context(p);443 bad_fork_cleanup_namespaces:444     exit_task_namespaces(p);445 bad_fork_cleanup_mm:446     if (p->mm)447         mmput(p->mm);448 bad_fork_cleanup_signal:449     if (!(clone_flags & CLONE_THREAD))450         free_signal_struct(p->signal);451 bad_fork_cleanup_sighand:452     __cleanup_sighand(p->sighand);453 bad_fork_cleanup_fs:454     exit_fs(p); /* blocking */455 bad_fork_cleanup_files:456     exit_files(p); /* blocking */457 bad_fork_cleanup_semundo:458     exit_sem(p);459 bad_fork_cleanup_audit:460     audit_free(p);461 bad_fork_cleanup_perf:462     perf_event_free_task(p);463 bad_fork_cleanup_policy:464 #ifdef CONFIG_NUMA465     mpol_put(p->mempolicy);466 bad_fork_cleanup_threadgroup_lock:467 #endif468     if (clone_flags & CLONE_THREAD)469         threadgroup_change_end(current);470     delayacct_tsk_free(p);471     module_put(task_thread_info(p)->exec_domain->module);472 bad_fork_cleanup_count:473     atomic_dec(&p->cred->user->processes);474     exit_creds(p);475 bad_fork_free:476     free_task(p);477 fork_out:478     return ERR_PTR(retval);479 }

流程圖

小結(jié)

  copy_process作為do_fork的主心骨,其流程并不復(fù)雜,只是每一步調(diào)用的初始化函數(shù)都非常精妙,涉及到大量的內(nèi)知識和代碼,這里為了篇幅著想就不繼續(xù)往細(xì)節(jié)分析了,會在之后的文章中慢慢補(bǔ)全其中的知識和自己的理解。整篇文章讀下來,其實(shí)copy_process的核心就是初始化task_struct結(jié)構(gòu)體供新進(jìn)程(線程)使用,并為其分配獨(dú)有的pid,最后將其加入到運(yùn)行隊(duì)列中。而至于為什么應(yīng)用層調(diào)用fork()會進(jìn)行兩次返回,原理就是在內(nèi)核棧中,在copy_thread函數(shù)中父進(jìn)程將其內(nèi)核棧復(fù)制到子進(jìn)程中,把子進(jìn)程被調(diào)度后執(zhí)行的第一條語句設(shè)置為do_fork()返回,并把保存返回值的寄存器值(一般返回值保存在eax(ARM是r0),而這些通用寄存器值保存在內(nèi)核棧中,當(dāng)調(diào)用后會進(jìn)行進(jìn)程切換,會把這些保存于內(nèi)核棧的寄存器值還原到寄存器中)置為0,所以子進(jìn)程的返回值為0,而父進(jìn)程會繼續(xù)執(zhí)行copy_thread函數(shù)之后的初始化,最后返回子進(jìn)程的pid(實(shí)際上是tgid)。


發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 恩平市| 清镇市| 汕头市| 娄烦县| 博客| 浪卡子县| 洛阳市| 黄冈市| 错那县| 文成县| 曲松县| 错那县| 宜都市| 贵港市| 宜州市| 陆川县| 牙克石市| 沈阳市| 和田县| 旌德县| 张掖市| 尚志市| 油尖旺区| 文登市| 永善县| 惠东县| 江口县| 保山市| 三河市| 旬阳县| 宁德市| 潍坊市| 沅陵县| 浦北县| 海安县| 深圳市| 英德市| 修武县| 蒲江县| 灵川县| 高密市|