Linux内核分析

Linux创建进程的坎坷之路

2017-04-02  本文已影响78人  athorn

陈松 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

需要打上这么多断点,do_fork、copy_process、sys_clone、copy_thread、dup_task_struct等。

跟踪dup_task_struct进去看

143         if (unlikely(p->flags & PF_KTHREAD)) {
144                 /* kernel thread */
145                 memset(childregs, 0, sizeof(struct pt_regs));
146                 p->thread.ip = (unsigned long) ret_from_kernel_thread;
147                 task_user_gs(p) = __KERNEL_STACK_CANARY;
148                 childregs->ds = __USER_DS;
149                 childregs->es = __USER_DS;
150                 childregs->fs = __KERNEL_PERCPU;
151                 childregs->bx = sp;     /* function */
152                 childregs->bp = arg;
153                 childregs->orig_ax = -1;
154                 childregs->cs = __KERNEL_CS | get_kernel_rpl();
155                 childregs->flags = X86_EFLAGS_IF | X86_EFLAGS_FIXED;
156                 p->thread.io_bitmap_ptr = NULL;
157                 return 0;
158         }

当然,如果上述语句块未执行到,那么会执行下面的:

159         *childregs = *current_pt_regs();
160         childregs->ax = 0;
161         if (sp)
162                 childregs->sp = sp;
163 
164         p->thread.ip = (unsigned long) ret_from_fork;
165         task_user_gs(p) = get_user_gs(current_pt_regs());

最后是设置子进程的返回值为0,thread的ip为ret_from_fork,这也就是我们子进程返回的执行点。
process_32.c中有如下定义,显然是嵌入式汇编,入口在entry_32.S中。

 58 asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
 59 asmlinkage void ret_from_kernel_thread(void) __asm__("ret_from_kernel_thread");
290 ENTRY(ret_from_fork)
291         CFI_STARTPROC
292         pushl_cfi %eax
293         call schedule_tail
294         GET_THREAD_INFO(%ebp)
295         popl_cfi %eax
296         pushl_cfi $0x0202               # Reset kernel eflags
297         popfl_cfi
298         jmp syscall_exit
299         CFI_ENDPROC
300 END(ret_from_fork)
301 
302 ENTRY(ret_from_kernel_thread)
303         CFI_STARTPROC
304         pushl_cfi %eax
305         call schedule_tail
306         GET_THREAD_INFO(%ebp)
307         popl_cfi %eax
308         pushl_cfi $0x0202               # Reset kernel eflags
309         popfl_cfi
310         movl PT_EBP(%esp),%eax
311         call *PT_EBX(%esp)
312         movl $0,PT_EAX(%esp)
313         jmp syscall_exit
314         CFI_ENDPROC
315 ENDPROC(ret_from_kernel_thread)
2305 /**
2306  * schedule_tail - first thing a freshly forked thread must call.
2307  * @prev: the thread we just switched away from.
2308  */
2309 asmlinkage __visible void schedule_tail(struct task_struct *prev)
2310         __releases(rq->lock)
2311 {
2312         struct rq *rq = this_rq();
2313 
2314         finish_task_switch(rq, prev);
2315 
2316         /*
2317          * FIXME: do we need to worry about rq being invalidated by the
2318          * task_switch?
2319          */
2320         post_schedule(rq);
2321 
2322         if (current->set_child_tid)
2323                 put_user(task_pid_vnr(current), current->set_child_tid);
2324 }
1657         if (!IS_ERR(p)) {
1658                 struct completion vfork;
1659                 struct pid *pid;
1660 
1661                 trace_sched_process_fork(current, p);
1662 
1663                 pid = get_task_pid(p, PIDTYPE_PID);
1664                 nr = pid_vnr(pid);
1665 
1666                 if (clone_flags & CLONE_PARENT_SETTID)
1667                         put_user(nr, parent_tidptr);
1668 
1669                 if (clone_flags & CLONE_VFORK) {
1670                         p->vfork_done = &vfork;
1671                         init_completion(&vfork);
1672                         get_task_struct(p);
1673                 }
1674 
1675                 wake_up_new_task(p);
1676 
1677                 /* forking complete and child started to run, tell ptracer */
1678                 if (unlikely(trace))
1679                         ptrace_event_pid(trace, pid);
1680 
1681                 if (clone_flags & CLONE_VFORK) {
1682                         if (!wait_for_vfork_done(p, &vfork))
1683                                 ptrace_event_pid(PTRACE_EVENT_VFORK_DONE, pid);
1684                 }
1685 
1686                 put_pid(pid);
1687         } else {
1688                 nr = PTR_ERR(p);
1689         }
1690         return nr;

你看,这样,这样,然后再这样,Linux就把进程创建出来了。

那么问题来了,为什么子进程的syscall_exit之后,就能返回到fork的下面一句代码执行呢?

思考中。。。

上一篇下一篇

猜你喜欢

热点阅读