fork之后发生了什么(基于3.16-rc4)

在linux c编程中,我们可以使用fork,vfork,clone三个系统调用来创建子进程。下面我们先分析下fork系统调用的实现原理。代码如下(kernel/fork.c):

 1 #ifdef __ARCH_WANT_SYS_FORK
 2 SYSCALL_DEFINE0(fork)
 3 {
 4 #ifdef CONFIG_MMU
 5     return do_fork(SIGCHLD, 0, 0, NULL, NULL);
 6 #else
 7     /* can not support in nommu mode */
 8     return -EINVAL;
 9 #endif
10 }
11 #endif

看见了么,linux中系统调用就是这样定义的。可以看见fork系统调用是do_fork函数的一个封装,它会通过软中断(系统门)的形式跳到内核函数do_fork中。下面我们来分析下do_fork函数(kernel/fork.c):

 1 long do_fork(unsigned long clone_flags,
 2           unsigned long stack_start,
 3           unsigned long stack_size,
 4           int __user *parent_tidptr,
 5           int __user *child_tidptr)
 6 {
 7     struct task_struct *p;
 8     int trace = 0;
 9     long nr;
10
11     /*
12      * Determine whether and which event to report to ptracer.  When
13      * called from kernel_thread or CLONE_UNTRACED is explicitly
14      * requested, no event is reported; otherwise, report if the event
15      * for the type of forking is enabled.
16      */
17     if (!(clone_flags & CLONE_UNTRACED)) {
18         if (clone_flags & CLONE_VFORK)
19             trace = PTRACE_EVENT_VFORK;
20         else if ((clone_flags & CSIGNAL) != SIGCHLD)
21             trace = PTRACE_EVENT_CLONE;
22         else
23             trace = PTRACE_EVENT_FORK;
24
25         if (likely(!ptrace_event_enabled(current, trace)))
26             trace = 0;
27     }
28
29     p = copy_process(clone_flags, stack_start, stack_size,
30              child_tidptr, NULL, trace);
31     /*
32      * Do this prior waking up the new thread - the thread pointer
33      * might get invalid after that point, if the thread exits quickly.
34      */
35     if (!IS_ERR(p)) {
36         struct completion vfork;
37         struct pid *pid;
38
39         trace_sched_process_fork(current, p);
40
41         pid = get_task_pid(p, PIDTYPE_PID);
42         nr = pid_vnr(pid);
43
44         if (clone_flags & CLONE_PARENT_SETTID)
45             put_user(nr, parent_tidptr);
46
47         if (clone_flags & CLONE_VFORK) {
48             p->vfork_done = &vfork;
49             init_completion(&vfork);
50             get_task_struct(p);
51         }
52
53         wake_up_new_task(p);
54
55         /* forking complete and child started to run, tell ptracer */
56         if (unlikely(trace))
57             ptrace_event_pid(trace, pid);
58
59         if (clone_flags & CLONE_VFORK) {
60             if (!wait_for_vfork_done(p, &vfork))
61                 ptrace_event_pid(PTRACE_EVENT_VFORK_DONE, pid);
62         }
63
64         put_pid(pid);
65     } else {
66         nr = PTR_ERR(p);
67     }
68     return nr;
69 }

第17-27行对函数接收到的标志进行判断,如果设置了追踪标志,则依据创建进程所使用的系统调用,在trace变量中放入不同的类型值。第29行创建子进程的描述符以及其他的数据结构,返回值为子进程的描述符指针,保存在变量p中,该函数可以说最核心的函数,待会来分析它。第35行,如果p指针没有出错,则进入if体。第41行获取子进程的pid号。第42行更新子进程的pid命名空间,并获得进程的pid号,存放在nr变量中。第44-45行,如果设置了CLONE_PARENT_SETTID标志,则将子进程的命名空间保存到父进程的一个用户态变量中,该变量的指针就是传进来的参数parent_tidptr。该函数中大部分是跟子进程追踪有关,我们暂且不分析这部分,主要分析下copy_process函数,代码如下(kernel/fork.c):

  1 static struct task_struct *copy_process(unsigned long clone_flags,
  2                     unsigned long stack_start,
  3                     unsigned long stack_size,
  4                     int __user *child_tidptr,
  5                     struct pid *pid,
  6                     int trace)
  7 {
  8     int retval;
  9     struct task_struct *p;
 10
 11     if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
 12         return ERR_PTR(-EINVAL);
 13
 14     if ((clone_flags & (CLONE_NEWUSER|CLONE_FS)) == (CLONE_NEWUSER|CLONE_FS))
 15         return ERR_PTR(-EINVAL);
 16
 17     /*
 18      * Thread groups must share signals as well, and detached threads
 19      * can only be started up within the thread group.
 20      */
 21     if ((clone_flags & CLONE_THREAD) && !(clone_flags & CLONE_SIGHAND))
 22         return ERR_PTR(-EINVAL);
 23
 24     /*
 25      * Shared signal handlers imply shared VM. By way of the above,
 26      * thread groups also imply shared VM. Blocking this case allows
 27      * for various simplifications in other code.
 28      */
 29     if ((clone_flags & CLONE_SIGHAND) && !(clone_flags & CLONE_VM))
 30         return ERR_PTR(-EINVAL);
 31
 32     /*
 33      * Siblings of global init remain as zombies on exit since they are
 34      * not reaped by their parent (swapper). To solve this and to avoid
 35      * multi-rooted process trees, prevent global and container-inits
 36      * from creating siblings.
 37      */
 38     if ((clone_flags & CLONE_PARENT) &&
 39                 current->signal->flags & SIGNAL_UNKILLABLE)
 40         return ERR_PTR(-EINVAL);
 41
 42     /*
 43      * If the new process will be in a different pid or user namespace
 44      * do not allow it to share a thread group or signal handlers or
 45      * parent with the forking task.
 46      */
 47     if (clone_flags & CLONE_SIGHAND) {
 48         if ((clone_flags & (CLONE_NEWUSER | CLONE_NEWPID)) ||
 49             (task_active_pid_ns(current) !=
 50                 current->nsproxy->pid_ns_for_children))
 51             return ERR_PTR(-EINVAL);
 52     }
 53
 54     retval = security_task_create(clone_flags);
 55     if (retval)
 56         goto fork_out;
 57
 58     retval = -ENOMEM;
 59     p = dup_task_struct(current);
 60     if (!p)
 61         goto fork_out;
 62
 63     ftrace_graph_init_task(p);
 64     get_seccomp_filter(p);
 65
 66     rt_mutex_init_task(p);
 67
 68 #ifdef CONFIG_PROVE_LOCKING
 69     DEBUG_LOCKS_WARN_ON(!p->hardirqs_enabled);
 70     DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled);
 71 #endif
 72     retval = -EAGAIN;
 73     if (atomic_read(&p->real_cred->user->processes) >=
 74             task_rlimit(p, RLIMIT_NPROC)) {
 75         if (p->real_cred->user != INIT_USER &&
 76             !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN))
 77             goto bad_fork_free;
 78     }
 79     current->flags &= ~PF_NPROC_EXCEEDED;
 80
 81     retval = copy_creds(p, clone_flags);
 82     if (retval < 0)
 83         goto bad_fork_free;
 84
 85     /*
 86      * If multiple threads are within copy_process(), then this check
 87      * triggers too late. This doesn‘t hurt, the check is only there
 88      * to stop root fork bombs.
 89      */
 90     retval = -EAGAIN;
 91     if (nr_threads >= max_threads)
 92         goto bad_fork_cleanup_count;
 93
 94     if (!try_module_get(task_thread_info(p)->exec_domain->module))
 95         goto bad_fork_cleanup_count;
 96
 97     delayacct_tsk_init(p);    /* Must remain after dup_task_struct() */
 98     p->flags &= ~(PF_SUPERPRIV | PF_WQ_WORKER);
 99     p->flags |= PF_FORKNOEXEC;
100     INIT_LIST_HEAD(&p->children);
101     INIT_LIST_HEAD(&p->sibling);
102     rcu_copy_process(p);
103     p->vfork_done = NULL;
104     spin_lock_init(&p->alloc_lock);
105
106     init_sigpending(&p->pending);
107
108     p->utime = p->stime = p->gtime = 0;
109     p->utimescaled = p->stimescaled = 0;
110 #ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
111     p->prev_cputime.utime = p->prev_cputime.stime = 0;
112 #endif
113 #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
114     seqlock_init(&p->vtime_seqlock);
115     p->vtime_snap = 0;
116     p->vtime_snap_whence = VTIME_SLEEPING;
117 #endif
118
119 #if defined(SPLIT_RSS_COUNTING)
120     memset(&p->rss_stat, 0, sizeof(p->rss_stat));
121 #endif
122
123     p->default_timer_slack_ns = current->timer_slack_ns;
124
125     task_io_accounting_init(&p->ioac);
126     acct_clear_integrals(p);
127
128     posix_cpu_timers_init(p);
129
130     do_posix_clock_monotonic_gettime(&p->start_time);
131     p->real_start_time = p->start_time;
132     monotonic_to_bootbased(&p->real_start_time);
133     p->io_context = NULL;
134     p->audit_context = NULL;
135     if (clone_flags & CLONE_THREAD)
136         threadgroup_change_begin(current);
137     cgroup_fork(p);
138 #ifdef CONFIG_NUMA
139     p->mempolicy = mpol_dup(p->mempolicy);
140     if (IS_ERR(p->mempolicy)) {
141         retval = PTR_ERR(p->mempolicy);
142         p->mempolicy = NULL;
143         goto bad_fork_cleanup_threadgroup_lock;
144     }
145 #endif
146 #ifdef CONFIG_CPUSETS
147     p->cpuset_mem_spread_rotor = NUMA_NO_NODE;
148     p->cpuset_slab_spread_rotor = NUMA_NO_NODE;
149     seqcount_init(&p->mems_allowed_seq);
150 #endif
151 #ifdef CONFIG_TRACE_IRQFLAGS
152     p->irq_events = 0;
153     p->hardirqs_enabled = 0;
154     p->hardirq_enable_ip = 0;
155     p->hardirq_enable_event = 0;
156     p->hardirq_disable_ip = _THIS_IP_;
157     p->hardirq_disable_event = 0;
158     p->softirqs_enabled = 1;
159     p->softirq_enable_ip = _THIS_IP_;
160     p->softirq_enable_event = 0;
161     p->softirq_disable_ip = 0;
162     p->softirq_disable_event = 0;
163     p->hardirq_context = 0;
164     p->softirq_context = 0;
165 #endif
166 #ifdef CONFIG_LOCKDEP
167     p->lockdep_depth = 0; /* no locks held yet */
168     p->curr_chain_key = 0;
169     p->lockdep_recursion = 0;
170 #endif
171
172 #ifdef CONFIG_DEBUG_MUTEXES
173     p->blocked_on = NULL; /* not blocked yet */
174 #endif
175 #ifdef CONFIG_MEMCG
176     p->memcg_batch.do_batch = 0;
177     p->memcg_batch.memcg = NULL;
178 #endif
179 #ifdef CONFIG_BCACHE
180     p->sequential_io    = 0;
181     p->sequential_io_avg    = 0;
182 #endif
183
184     /* Perform scheduler related setup. Assign this task to a CPU. */
185     retval = sched_fork(clone_flags, p);
186     if (retval)
187         goto bad_fork_cleanup_policy;
188
189     retval = perf_event_init_task(p);
190     if (retval)
191         goto bad_fork_cleanup_policy;
192     retval = audit_alloc(p);
193     if (retval)
194         goto bad_fork_cleanup_policy;
195     /* copy all the process information */
196     retval = copy_semundo(clone_flags, p);
197     if (retval)
198         goto bad_fork_cleanup_audit;
199     retval = copy_files(clone_flags, p);
200     if (retval)
201         goto bad_fork_cleanup_semundo;
202     retval = copy_fs(clone_flags, p);
203     if (retval)
204         goto bad_fork_cleanup_files;
205     retval = copy_sighand(clone_flags, p);
206     if (retval)
207         goto bad_fork_cleanup_fs;
208     retval = copy_signal(clone_flags, p);
209     if (retval)
210         goto bad_fork_cleanup_sighand;
211     retval = copy_mm(clone_flags, p);
212     if (retval)
213         goto bad_fork_cleanup_signal;
214     retval = copy_namespaces(clone_flags, p);
215     if (retval)
216         goto bad_fork_cleanup_mm;
217     retval = copy_io(clone_flags, p);
218     if (retval)
219         goto bad_fork_cleanup_namespaces;
220     retval = copy_thread(clone_flags, stack_start, stack_size, p);
221     if (retval)
222         goto bad_fork_cleanup_io;
223
224     if (pid != &init_struct_pid) {
225         retval = -ENOMEM;
226         pid = alloc_pid(p->nsproxy->pid_ns_for_children);
227         if (!pid)
228             goto bad_fork_cleanup_io;
229     }
230
231     p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL;
232     /*
233      * Clear TID on mm_release()?
234      */
235     p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr : NULL;
236 #ifdef CONFIG_BLOCK
237     p->plug = NULL;
238 #endif
239 #ifdef CONFIG_FUTEX
240     p->robust_list = NULL;
241 #ifdef CONFIG_COMPAT
242     p->compat_robust_list = NULL;
243 #endif
244     INIT_LIST_HEAD(&p->pi_state_list);
245     p->pi_state_cache = NULL;
246 #endif
247     /*
248      * sigaltstack should be cleared when sharing the same VM
249      */
250     if ((clone_flags & (CLONE_VM|CLONE_VFORK)) == CLONE_VM)
251         p->sas_ss_sp = p->sas_ss_size = 0;
252
253     /*
254      * Syscall tracing and stepping should be turned off in the
255      * child regardless of CLONE_PTRACE.
256      */
257     user_disable_single_step(p);
258     clear_tsk_thread_flag(p, TIF_SYSCALL_TRACE);
259 #ifdef TIF_SYSCALL_EMU
260     clear_tsk_thread_flag(p, TIF_SYSCALL_EMU);
261 #endif
262     clear_all_latency_tracing(p);
263
264     /* ok, now we should be set up.. */
265     p->pid = pid_nr(pid);
266     if (clone_flags & CLONE_THREAD) {
267         p->exit_signal = -1;
268         p->group_leader = current->group_leader;
269         p->tgid = current->tgid;
270     } else {
271         if (clone_flags & CLONE_PARENT)
272             p->exit_signal = current->group_leader->exit_signal;
273         else
274             p->exit_signal = (clone_flags & CSIGNAL);
275         p->group_leader = p;
276         p->tgid = p->pid;
277     }
278
279     p->nr_dirtied = 0;
280     p->nr_dirtied_pause = 128 >> (PAGE_SHIFT - 10);
281     p->dirty_paused_when = 0;
282
283     p->pdeath_signal = 0;
284     INIT_LIST_HEAD(&p->thread_group);
285     p->task_works = NULL;
286
287     /*
288      * Make it visible to the rest of the system, but dont wake it up yet.
289      * Need tasklist lock for parent etc handling!
290      */
291     write_lock_irq(&tasklist_lock);
292
293     /* CLONE_PARENT re-uses the old parent */
294     if (clone_flags & (CLONE_PARENT|CLONE_THREAD)) {
295         p->real_parent = current->real_parent;
296         p->parent_exec_id = current->parent_exec_id;
297     } else {
298         p->real_parent = current;
299         p->parent_exec_id = current->self_exec_id;
300     }
301
302     spin_lock(&current->sighand->siglock);
303
304     /*
305      * Process group and session signals need to be delivered to just the
306      * parent before the fork or both the parent and the child after the
307      * fork. Restart if a signal comes in before we add the new process to
308      * it‘s process group.
309      * A fatal signal pending means that current will exit, so the new
310      * thread can‘t slip out of an OOM kill (or normal SIGKILL).
311     */
312     recalc_sigpending();
313     if (signal_pending(current)) {
314         spin_unlock(&current->sighand->siglock);
315         write_unlock_irq(&tasklist_lock);
316         retval = -ERESTARTNOINTR;
317         goto bad_fork_free_pid;
318     }
319
320     if (likely(p->pid)) {
321         ptrace_init_task(p, (clone_flags & CLONE_PTRACE) || trace);
322
323         init_task_pid(p, PIDTYPE_PID, pid);
324         if (thread_group_leader(p)) {
325             init_task_pid(p, PIDTYPE_PGID, task_pgrp(current));
326             init_task_pid(p, PIDTYPE_SID, task_session(current));
327
328             if (is_child_reaper(pid)) {
329                 ns_of_pid(pid)->child_reaper = p;
330                 p->signal->flags |= SIGNAL_UNKILLABLE;
331             }
332
333             p->signal->leader_pid = pid;
334             p->signal->tty = tty_kref_get(current->signal->tty);
335             list_add_tail(&p->sibling, &p->real_parent->children);
336             list_add_tail_rcu(&p->tasks, &init_task.tasks);
337             attach_pid(p, PIDTYPE_PGID);
338             attach_pid(p, PIDTYPE_SID);
339             __this_cpu_inc(process_counts);
340         } else {
341             current->signal->nr_threads++;
342             atomic_inc(&current->signal->live);
343             atomic_inc(&current->signal->sigcnt);
344             list_add_tail_rcu(&p->thread_group,
345                       &p->group_leader->thread_group);
346             list_add_tail_rcu(&p->thread_node,
347                       &p->signal->thread_head);
348         }
349         attach_pid(p, PIDTYPE_PID);
350         nr_threads++;
351     }
352
353     total_forks++;
354     spin_unlock(&current->sighand->siglock);
355     syscall_tracepoint_update(p);
356     write_unlock_irq(&tasklist_lock);
357
358     proc_fork_connector(p);
359     cgroup_post_fork(p);
360     if (clone_flags & CLONE_THREAD)
361         threadgroup_change_end(current);
362     perf_event_fork(p);
363
364     trace_task_newtask(p, clone_flags);
365     uprobe_copy_process(p, clone_flags);
366
367     return p;
368
369 bad_fork_free_pid:
370     if (pid != &init_struct_pid)
371         free_pid(pid);
372 bad_fork_cleanup_io:
373     if (p->io_context)
374         exit_io_context(p);
375 bad_fork_cleanup_namespaces:
376     exit_task_namespaces(p);
377 bad_fork_cleanup_mm:
378     if (p->mm)
379         mmput(p->mm);
380 bad_fork_cleanup_signal:
381     if (!(clone_flags & CLONE_THREAD))
382         free_signal_struct(p->signal);
383 bad_fork_cleanup_sighand:
384     __cleanup_sighand(p->sighand);
385 bad_fork_cleanup_fs:
386     exit_fs(p); /* blocking */
387 bad_fork_cleanup_files:
388     exit_files(p); /* blocking */
389 bad_fork_cleanup_semundo:
390     exit_sem(p);
391 bad_fork_cleanup_audit:
392     audit_free(p);
393 bad_fork_cleanup_policy:
394     perf_event_free_task(p);
395 #ifdef CONFIG_NUMA
396     mpol_put(p->mempolicy);
397 bad_fork_cleanup_threadgroup_lock:
398 #endif
399     if (clone_flags & CLONE_THREAD)
400         threadgroup_change_end(current);
401     delayacct_tsk_free(p);
402     module_put(task_thread_info(p)->exec_domain->module);
403 bad_fork_cleanup_count:
404     atomic_dec(&p->cred->user->processes);
405     exit_creds(p);
406 bad_fork_free:
407     free_task(p);
408 fork_out:
409     return ERR_PTR(retval);
410 }

copy_process

第11-52行对传递进来的clone_flags标志进行一致性检查,如果有错误,返回错误代码。第54行对clone_flags进行附加性安全检查。第59行为子进程创建进程描述符。第63行,这个版本的linux内核源码中该函数为空函数,不研究它。第64行增加子进程p的seccomp.filter结构体成员的引用数量。第66行初始化子进程中的互斥锁。第73-77行比较子进程的拥有者所拥有的进程数量是否超过限制,如果超过了限制并且该拥有者不是root用户,就会跳到错误处理代码。第81行将父进程的拥有者信息拷贝给子进程。第91行检查系统中的进程数量是否超过了限制,nr_threads是内核中的全局变量,存放着进程的总数。第94行检查子进程的执行域(要执行的代码)所在的模块是否已经加载进了内核,如果没有的话就进行出错处理。第98-99行对子进程flags进程初始化。第100-101对子进程的孩子链表和兄弟链表进行初始化。第102行初始化子进程的rcu(一种锁机制)。第104行初始化子进程的alloc_lock自旋锁。第106行初始化进程的挂起信号集。第108-116对进行的各种时间进程初始化。第125行初始化子进程的io计数。第126行对子进程的多个计数域进行清零。第128行初始化子进程的定时器。第185-222行,使用了多个copy_***函数创建了子进程要用的各种数据结构,并将父进程的相应结构体中的内容拷贝进来,或者根据flags指定的内容来初始化子进程的这些数据结构。第224-228行,如果传进来的pid指针和全局结构体变量init_struct_pid的地址不相同的话,就要为子进程分配pid结构体。第265行从pid结构体中获取到子进程的pid号,保存到子进程描述符的pid域。第266-269行,如果设置了CLONE_THREAD标志,说明子进程和父进程在同一个线程组,那么子进程将继承父进程的tgid,否则第275-276行,子进程的组领导者就是它自己,组号tgpid是它自己的pid号。第279-281行设置一些和进程脏页限制有关的成员。下面291-356行的代码需要关掉中断并且获取写锁。第294-296行对进程亲子关系初始化,如果设置了CLONE_PARENT和CLONE_THREAD标志,子进程的真实父进程设置为它的父进程的真实父进程(它的爷爷进程),否则第298-299行,子进程的真实父进程设置为它的父进程。第302行-354行需要获取自旋锁。第321行初始化子进程的ptraced字段(和进程追踪有关)。第323行,对子进程的pid结构体进行初始化。第324-340行,如果子进程是线程组的组长,则对子进程进行相应处理,其中,第325-326行将子进程的进程组和会话组的组长分别设置为父进程的组领导的进程组和会话组组长,第337-338行,将子进程加入它所在组的哈希链表中。否则,子进程不是线程组组长的话,第341-347行不对子进程做pid相关的操作。第349行,同样要将子进程加入到pid哈希表中。第350行全局变量nr_threads自加1,说明系统中的进程又多了一个。如果整个函数内部没有出错的话,第367行返回创建好的子进程描述符指针p。第369行到结束,均为错误处理的代码,如果函数中有出错的地方,就会跳到这些代码中。
下面我们接着分析第59行的dup_task_struct函数,代码如下(kernel/fork.c):

 1 static struct task_struct *dup_task_struct(struct task_struct *orig)
 2 {
 3     struct task_struct *tsk;
 4     struct thread_info *ti;
 5     unsigned long *stackend;
 6     int node = tsk_fork_get_node(orig);
 7     int err;
 8
 9     tsk = alloc_task_struct_node(node);
10     if (!tsk)
11         return NULL;
12
13     ti = alloc_thread_info_node(tsk, node);
14     if (!ti)
15         goto free_tsk;
16
17     err = arch_dup_task_struct(tsk, orig);
18     if (err)
19         goto free_ti;
20
21     tsk->stack = ti;
22
23     setup_thread_stack(tsk, orig);
24     clear_user_return_notifier(tsk);
25     clear_tsk_need_resched(tsk);
26     stackend = end_of_stack(tsk);
27     *stackend = STACK_END_MAGIC;    /* for overflow detection */
28
29 #ifdef CONFIG_CC_STACKPROTECTOR
30     tsk->stack_canary = get_random_int();
31 #endif
32
33     /*
34      * One for us, one for whoever does the "release_task()" (usually
35      * parent)
36      */
37     atomic_set(&tsk->usage, 2);
38 #ifdef CONFIG_BLK_DEV_IO_TRACE
39     tsk->btrace_seq = 0;
40 #endif
41     tsk->splice_pipe = NULL;
42     tsk->task_frag.page = NULL;
43
44     account_kernel_stack(ti, 1);
45
46     return tsk;
47
48 free_ti:
49     free_thread_info(ti);
50 free_tsk:
51     free_task_struct(tsk);
52     return NULL;
53 }

在该函数中创建了子进程的进程描述符,并将描述符指针返回。第6行,如果使用到了NUMA技术的话,函数返回父进程描述符的pref_node_fork字段值,否则,返回-1。第9行为子进程创建进程描述符,将描述符指针保存在tsk变量中。第13行为子进程创建thread_info结构体(也就是创建内核栈),并返回结构体指针(该指针实际上也是子进程堆栈的内核栈指针)。第17行将父进程描述符的各个成员值全部复制给子进程描述符的成员。第21行将子进程内核栈地址ti赋给stack成员。第23行对子进程的thread_info结构体进行初始化,将父进程的该结构体成员值复制给子进程,然后将子进程的描述符指针存入thread_info结构体的task域(从thread_info就可找到进程描述符了)。第25行清除子进程thread_info结构体中的flag标志(具体而言,就是need_resched标志,取消内核抢占)。第26行将自进程内核栈顶指针保存到stackend变量中。第27行向子进程内核堆栈中存入STACK_END_MAGIC,来进行堆栈溢出检测。第23行将子进程描述符使用计数置为2(一个表示父进程的引用,一个表示自己)。第44行将子进程的内核栈所在的zone结构体使用计数置为1。第46行返回子进程描述符指针。接下来,我们回到do_fork函数中,第53行wake_up_new_task函数会将刚创建好的子进程加入到运行队列中并且唤醒它,下面看看该函数(kernel/sched/core.c)。

 1 void wake_up_new_task(struct task_struct *p)
 2 {
 3     unsigned long flags;
 4     struct rq *rq;
 5
 6     raw_spin_lock_irqsave(&p->pi_lock, flags);
 7 #ifdef CONFIG_SMP
 8     /*
 9      * Fork balancing, do it here and not earlier because:
10      *  - cpus_allowed can change in the fork path
11      *  - any previously selected cpu might disappear through hotplug
12      */
13     set_task_cpu(p, select_task_rq(p, task_cpu(p), SD_BALANCE_FORK, 0));
14 #endif
15
16     /* Initialize new task‘s runnable average */
17     init_task_runnable_average(p);
18     rq = __task_rq_lock(p);
19     activate_task(rq, p, 0);
20     p->on_rq = 1;
21     trace_sched_wakeup_new(p, true);
22     check_preempt_curr(rq, p, WF_FORK);
23 #ifdef CONFIG_SMP
24     if (p->sched_class->task_woken)
25         p->sched_class->task_woken(rq, p);
26 #endif
27     task_rq_unlock(rq, p, &flags);
28 }

该函数暂且放到这里,带我把进程调度这块的博文完成之后,再完善这里。该函数执行完后,第68行父进程返回子进程的pid号,回到do_fork中,子进程创建的工作就完成了。此后父子进程公平的参与进程调度。

fork之后发生了什么(基于3.16-rc4)

时间: 2024-08-30 15:29:37

fork之后发生了什么(基于3.16-rc4)的相关文章

fork开源代码后如何基于某个tag建立自己的branch

应用场景: 在github上fork一个自己想看的开源项目,想基于某个tag来写一些测试demo,然后可以做到版本控制. 方法: //克隆 git clone xxxxx.git //查看tag git tag //基于tag建立自己的分支 git co -b brachname tagname 这样即可以看到项目最新的动态,又可以将自己的代码基于某个稳定的版本写的测试代码管理起来.

执行 $ Gulp 时发生了什么 —— 基于 Gulp 的前端集成解决方案(二)

前言 文章 在windows下安装gulp —— 基于 Gulp 的前端集成解决方案(一) 中,已经完成对 gulp 的安装,由于是window环境,文中特意提到了可以通过安装 gitbash 来代替 window 系统的 CMD 命令行工具.本节主要围绕 如何使用 Gulp 完成一个预编译 SASS 文件的任务 来逐步熟悉 Gulp . 本文地址:http://www.cnblogs.com/leonkao/p/4611102.html Gulp 概述 Gulp 是一个构建工具,通过自动化处理

EJBCA的安装(基于Ubuntu 16.04 LTS + wildfly8 + ejbca6.3.11 + jdk7)

前一段时间折腾了一下PKI,用EJBCA在研究院内网搭建了一个CA,目前是提供给手机端(安卓和IOS)来和服务器端(nginx + Java应用)做安全连接的(客户端和服务器端双向认证) 由于EJBCA官方的安装文档太不靠谱了,按照官方的文档来做的话会遇到各种坑, 所以记录一下我安装的过程.希望能给用到的人有一点帮助. 这里也贴一下官方的文档地址:https://www.ejbca.org/docs/installation.html 环境和所依赖的软件包: Ubuntu 16.04 LTS,

关于Pycharm同步git,基于xubuntu 16.04环境

查看git config配置,在终端中输入以下命令:git config --list然后把自己的git账户添加上git config --global user.name 你的git账户名git config --global user.email 你注册git的邮箱添加完成可以 git config –list 查看信息是否匹配然后打开pycharm进行配置,依次点击file--setting--version control-git  这里是检测到本地安装的git应用程序file--set

ChaletOS 16.04 发布,肖似Windows 10

有一款名为 ChaletOS 的发行版不久前发布了 16.04 LTS ,它是一款基于 Ubuntu 16.04 LTS 的发行版.这个发行版的主要目标是为那些前 Windows 用户们提供一个进入开源世界的便捷之路.它提供了一个名为起点(Start Point)的应用,设计用来帮助那些 Windows 用户们快速熟悉该系统,这个应用里面包括视频指引.教程和推荐应用等. 不过,对于有些 Windows 用户,可能希望看到熟悉的 Windows 界面,因此,ChaletOS 的开发者 Dejan

mariadb install on Ubuntu 16.04.1 LTS

最近在尝试基于Ubuntu 16.04.1 LTS server 部署openstack ,在部署的过程中遇到mariadb 无法正常链接. 问题 参照openstack 官方文档安装mariadb,安装后遇到无法在本地登录的问题 $mysql -u root -p $mysql -h localhost -u root -p 报错如下 mysql (mariadb) ERROR 1698 (28000): Access denied for user 'root'@'localhost' 但使

system()、exec()、fork()三个与进程有关的函数的比较

启动新进程(system函数) system()函数可以启动一个新的进程. int system (const char *string ) 这个函数的效果就相当于执行sh –c string. 一般来说,使用system函数远非启动其他进程的理想手段,因为它必须用一个shell来启动需要的程序.这样对shell的安装情况,以及shell的版本依赖性很大. system函数的特点: 建立独立进程,拥有独立的代码空间,内存空间 等待新的进程执行完毕,system才返回.(阻塞) 替换进程映像(ex

linuxC中fork()函数详解

一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事.    一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间.然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同.相当于克隆了一个自己. 我们来看一个例子: 1 /* 2 * fork_test.c 3 * versi

构建一个基本的前端自动化开发环境 —— 基于 Gulp 的前端集成解决方案(四)

通过前面几节的准备工作,对于 npm / node / gulp 应该已经有了基本的认识,本节主要介绍如何构建一个基本的前端自动化开发环境. 下面将逐步构建一个可以自动编译 sass 文件.压缩 javascript 文件.多终端多浏览器同步测试的开发环境,并且还可以通过 piblish 命令对项目下的文件进行打包操作. 相关连接导航 在windows下安装gulp —— 基于 Gulp 的前端集成解决方案(一) 执行 $Gulp 时发生了什么 —— 基于 Gulp 的前端集成解决方案(二) 常