- 作者:handi
- 星期四, 十月 28, 2004
- 读核日记fork上篇
- 中间会参考到一些资料和文章,我也会在日记头一一列出
- 1. lion's unix 源代码分析
- 2. kernel 2.6.8 /kernel/fork.c
- 3. kernel 2.4.18 /kernel/fork.c
- 4. linux情景分析(上、下)
- 基本上没有找到以fork命名的函数,而且还符合
- match case and match whold word only
- 于是sys_fork --> do_fork,就像socket一样只有sys_socket
- 至于为什么系统调用的时候是fork()和socket(),我想可能是函数map的
- 原因吧
- 先说大体流程,之后再解说代码
- first,检查一些状态,获取一些必要的信息
- --> 进入copy_process实体,复制进程(中间有一个很重要的一点,就是
- 为什么子进程返回值是0)
- --> 设置一些信息
- --> 让进程开始run
- 比较于unix的实现,我只能说linux真复杂和罗嗦,lion's unix
- 中的实现很简单清晰,如果只是要了解原理的,那份fork就足够了,而且
- 很老实的,没有sys_fork, v_fork, do_fork之类的嵌套,就一个fork
- 当然不清楚目前unix的版本中这是怎样实现的,所以过些天会找些minix
- 或bsd中实现的代码来参考,然后补充到日记中来
- 前面罗嗦了这么多,还是来分析一下源代码吧.
- /kernel/fork.c
- kernel_thread(),sys_fork(),sys_clone(),sys_vfork()
- 这几个函数都是调用do_fork()函数,主要参数区别都是集中在
- clone_flags上面,在/arch/i386/kernel/process.c
- /*
- * Ok, this is the main fork-routine.
- *
- * It copies the process, and if successful kick-starts
- * it and waits for it to finish using the VM if required.
- */
- long do_fork(unsigned long clone_flags, /* 克隆标识 */
- unsigned long stack_start, /* 栈起始位置 - 怎么不用指针? */
- struct pt_regs *regs, /* 寄存器 */
- unsigned long stack_size, /* 栈大小 */
- int __user *parent_tidptr, /* 父进程的什么指针 */
- int __user *child_tidptr) /* 子进程的什么指针, 这两个指针用来指什么? */
- {
- struct task_struct *p; /* PCB结构 */
- int trace = 0;
- long pid;
- /* CLONE_PTRACE 这个宏表示什么意思[set if we want to let tracing continue on the child too]
- * 翻译过来的意思是如果想跟踪子进程的信息,就设置这一位
- * 是否跟踪由do_fork的参数clone_flags确定的
- * 也就是说是否跟踪子进程的信息是由我们在调用的时候确定 */
- /* current这个是指当前的父进程吗?
- * unlikely原型:
- * #include <linux/compiler.h> */
- if (unlikely(current->ptrace))
- {/* unlikely(x),对x两次取反,具体实现取决于gcc的版本,gcc 2.9.6是界点,其实就是 取x的值 */
- trace = fork_traceflag (clone_flags);/* fork_traceflag inline在文件fork.c中,2.4.18版本没有独立出去 */
- if (trace)
- clone_flags |= CLONE_PTRACE;
- }
- /* 检查clone_flags,创建一个新的进程,拷贝由clone_flags指定的信息,但新进程不运行 */
- p = copy_process(clone_flags, stack_start, regs, stack_size, parent_tidptr, child_tidptr);
- /*
- * Do this prior waking up the new thread - the thread pointer
- * might get invalid after that point, if the thread exits quickly.
- */
- pid = IS_ERR(p) ? PTR_ERR(p) : p->pid;
- /* 进程复制成功 */
- if (!IS_ERR(p))
- {
- struct completion vfork;
- if (clone_flags & CLONE_VFORK)
- {
- p->vfork_done = &vfork;
- init_completion(&vfork);
- }
- if ((p->ptrace & PT_PTRACED) || (clone_flags & CLONE_STOPPED))
- {
- /*
- * We'll start up with an immediate SIGSTOP.
- */
- sigaddset(&p->pending.signal, SIGSTOP);
- set_tsk_thread_flag(p, TIF_SIGPENDING);
- }
- if (!(clone_flags & CLONE_STOPPED))
- {
- /*
- * Do the wakeup last. On SMP we treat fork() and
- * CLONE_VM separately, because fork() has already
- * created cache footprint on this CPU (due to
- * copying the pagetables), hence migration would
- * probably be costy. Threads on the other hand
- * have less traction to the current CPU, and if
- * there's an imbalance then the scheduler can
- * migrate this fresh thread now, before it
- * accumulates a larger cache footprint:
- */
- /* 唤醒新fork的进程 */
- if (clone_flags & CLONE_VM)
- wake_up_forked_thread(p);
- else
- wake_up_forked_process(p);
- }
- else
- {
- int cpu = get_cpu();
- p->state = TASK_STOPPED;
- if (cpu_is_offline(task_cpu(p)))
- set_task_cpu(p, cpu);
- put_cpu();
- }
- ++total_forks;
- if (unlikely (trace))
- {
- current->ptrace_message = pid;
- ptrace_notify ((trace << 8) | SIGTRAP);
- }
- if (clone_flags & CLONE_VFORK)
- {
- wait_for_completion(&vfork);
- if (unlikely (current->ptrace & PT_TRACE_VFORK_DONE))
- ptrace_notify ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP);
- }
- else
- /*
- * Let the child process run first, to avoid most of the
- * COW overhead when the child exec()s afterwards.
- */
- /* 让fork的子进程先运行 */
- set_need_resched();
- }
- return pid;
- }
读核日记fork
最新推荐文章于 2024-09-19 14:18:08 发布