#####################################
作者:张卓
原创作品转载请注明出处:《Linux操作系统分析》MOOC课程 http://www.xuetangx.com/courses/course-v1:ustcX+USTC001+_/about
#####################################
203 #define TASK_RUNNING 0
204 #define TASK_INTERRUPTIBLE 1
205 #define TASK_UNINTERRUPTIBLE 2
208 /* in tsk->exit_state */
209 #define EXIT_DEAD 16
210 #define EXIT_ZOMBIE 32
1. fork一个进程
#include
#include
#include
int main(int argc, char * argv[])
{
int pid;
/* fork another process */
pid = fork();
if (pid < 0)
{
/* error occurred */
fprintf(stderr,"Fork Failed!");
exit(-1);
}
else if (pid == 0)
{
/* child process */
printf("This is Child Process!\n");
}
else
{
/* parent process */
printf("This is Parent Process!\n");
/* parent will wait for the child to complete*/
wait(NULL);
printf("Child Complete!\n");
}
}
上面一段小程序在用户态创建一个子进程。调用fork()函数之后,会有两次返回:一次在父进程中返回子进程的PID,一次在子进程中返回0。Linux通过复制父进程来创建一个新进程,我们可以简单的猜想一下,调用fork()之后,陷入内核主要做了什么?简单设想一下内核执行过程的框架:
kernel/fork.c:
do_fork():
...
copy_process(): /* 在do_fork()调用copy_process()复制老进程的寄存器,所有适当的进程环境 */
dup_task_struct(): /* 在copy_process()中调用dup_task_struct(),复制task_struct */
alloc_thread_info_node()
arch_dup_task_struct()
setup_thread_stack()
copy_files()
copy_fs()
...
copy_thread()(arch/x86/kernel/process_32.c ):
*childregs = *current_pt_regs(); /*复制内核堆栈的一部分(struct pt_rergs) ,int指令和SAVE_ALL压到内核栈的内容*/
childregs->ax= 0; /* 子进程的返回值为什么是0,这就是答案*/
p->thread.ip = (unsigned) ret_from_fork;
/* 拷贝内核堆栈数据和指定新进程的第一条指令地址 */
/*对创建的进程进行一些错误检查, 检查标志位;最后唤醒新进程*/
if (!IS_ERR(p)) {
...
wake_up_new_task(p); /*首次唤醒创建的进程,同时完成一些初始化调度的所需的工作,并将进程放入运行队列中*/
...
} else {
nr = PTR_ERR(p);
}
return nr; /* 这里返回的就是子进程的PID */
在调用copy_thread()的时候,子进程thread.ip 被赋值为ret_from_fork的地址,所以子进程开始执行的第一条指令就是ret_from_fork.
arch/x86/kernel/entry_32.S:
ENTRY(ret_from_fork)
子进程执行完ret_form_fork之后,将又一次返回用户空间,且返回值为0。
/*
* 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,
unsigned long stack_size,
int __user *parent_tidptr,
int __user *child_tidptr)
{
struct task_struct *p;
int trace = 0;
long nr;
/*
* Determine whether and which event to report to ptracer. When
* called from kernel_thread or CLONE_UNTRACED is explicitly
* requested, no event is reported; otherwise, report if the event
* for the type of forking is enabled.
*/
if (!(clone_flags & CLONE_UNTRACED)) {
if (clone_flags & CLONE_VFORK)
trace = PTRACE_EVENT_VFORK;
else if ((clone_flags & CSIGNAL) != SIGCHLD)
trace = PTRACE_EVENT_CLONE;
else
trace = PTRACE_EVENT_FORK;
if (likely(!ptrace_event_enabled(current, trace)))
trace = 0;
}
p = copy_process(clone_flags, stack_start, stack_size,
child_tidptr, NULL, trace);
/*
* Do this prior waking up the new thread - the thread pointer
* might get invalid after that point, if the thread exits quickly.
*/
if (!IS_ERR(p)) {
struct completion vfork;
struct pid *pid;
trace_sched_process_fork(current, p);
pid = get_task_pid(p, PIDTYPE_PID);
nr = pid_vnr(pid);
if (clone_flags & CLONE_PARENT_SETTID)
put_user(nr, parent_tidptr);
if (clone_flags & CLONE_VFORK) {
p->vfork_done = &vfork;
init_completion(&vfork);
get_task_struct(p);
}
wake_up_new_task(p);
/* forking complete and child started to run, tell ptracer */
if (unlikely(trace))
ptrace_event_pid(trace, pid);
if (clone_flags & CLONE_VFORK) {
if (!wait_for_vfork_done(p, &vfork))
ptrace_event_pid(PTRACE_EVENT_VFORK_DONE, pid);
}
put_pid(pid);
} else {
nr = PTR_ERR(p);
}
return nr;
}