Linux内核创建一个新进程的过程
2016-04-03 01:53
555 查看
罗冲 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
按照课程中给定的方法进行编译。
启动完成后,在弹出窗口输入:
程序会在断点处停下:
从这里可以看到在中断进入时,系统会保存一系列寄存器的值。
对于主进程,即当前正在执行上述代码的进程来说,其过程与普通的系统调用没有差别:
1)用户程序利用int 80,进入到内核态。此时CPU会自动保存用户的SS、ESP、EFLAGS、CS、EIP
2 ) 接着执行entry_32.S,即总控程序(调用entry_32.S中的ENTRY(system_call)),调用SAVE_ALL保存数据
3) entry_32.S保存完数据后,会调用
其中sys_call_table为函数入口地址表。
4)经过计算后,找到函数,
其中返回值为do_fork()的返回值,即子进,程的pid的值
5)经过总控程序后,返回用户空间,继续执行后面的代码,即汇编之后的代码。
子进程就是在copy_process中进行创建,并运行的。
查看ret_from_fork函数,可以看到相对于主进程的system_call而言,最终的处理都是调用
4000
jmp syscall_exit, 而子进程相对于主进程少了int 80与SAVE_ALL的压栈。但是子进程在 copy_thread()中的
已经保存了相应的值,因此,子进程执行到此处时又与主进程相同了,但是其中eax的值已经修改为0, 以保证子进程可以执行它自己的程序。
2)子进程的创建在内核空间,首次调用也是在内核空间,再从内核空间调度到用户空间
1. 准备
在menu代码中增加fork函数int pid; /* fork another process */ asm volatile( "mov $0x78, %%eax\n\t" "int $0x80\n\t" "mov %%eax, %0\n\t" :"=m"(pid) : ); 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"); } return 0;
按照课程中给定的方法进行编译。
2 实验开始
按照之前课程的gdb的方法,进行启动跟踪启动完成后,在弹出窗口输入:
程序会在断点处停下:
3. 代码分析
3.1 系统调用分析
在本例中,通过int 0x80来进行系统调用时,其进入内核空间与返回用户空间的堆栈情况从这里可以看到在中断进入时,系统会保存一系列寄存器的值。
3.2 fork主进程的返回
当执行mfork的时候,会调用中断:int pid; /* fork another process */ asm volatile( "mov $0x78, %%eax\n\t" "int $0x80\n\t" "mov %%eax, %0\n\t" :"=m"(pid) : );
对于主进程,即当前正在执行上述代码的进程来说,其过程与普通的系统调用没有差别:
1)用户程序利用int 80,进入到内核态。此时CPU会自动保存用户的SS、ESP、EFLAGS、CS、EIP
2 ) 接着执行entry_32.S,即总控程序(调用entry_32.S中的ENTRY(system_call)),调用SAVE_ALL保存数据
3) entry_32.S保存完数据后,会调用
call *sys_call_table(,%eax,4)
其中sys_call_table为函数入口地址表。
4)经过计算后,找到函数,
SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp, int __user *, parent_tidptr, int __user *, child_tidptr, int, tls_val) { return do_fork(clone_flags, newsp, 0, parent_tidptr, child_tidptr); }
其中返回值为do_fork()的返回值,即子进,程的pid的值
5)经过总控程序后,返回用户空间,继续执行后面的代码,即汇编之后的代码。
3.3 fork子进程的创建与返回
相对于主进程来说,子进程是由主进程复制而来。接下来分析子进程的创建过程。当系统进入到sys_clone之后,会调用do_fork函数创建子进程。而do_fork()函数中,有一条语句:p = copy_process(clone_flags, stack_start, stack_size,child_tidptr, NULL, trace);
子进程就是在copy_process中进行创建,并运行的。
3.3.1 copy_process函数的分析
copy_process()整个函数可以分为三个部分:创建、修改、复制。static struct task_struct *copy_process(...) { ... //1.复制。在这个函数中,它创建了两个对象task_struct与thread_info,并且将部分基本信息复制过去了。 p = dup_task_struct(current); //2. 修改。 当task_struct复制过来之后,修改时间之类的。 //3. 复制。诸如下面的代码 ... retval = copy_thread(clone_flags, stack_start, stack_size, p); ... }
3.3.2 copy_thread()分析
copy_thread()是子进程创建的一个关键部分,其函数是arch\x86\kernel\process_32.c中。下面结合代码,继续分析int copy_thread(unsigned long clone_flags, unsigned long sp, unsigned long arg, struct task_struct *p) { ... //1. 获取当前主进程的堆栈数据的地址。拷贝当前主进程的已有数据。 //pt_regs的内容就是int 80与SAVE_ALL的值 *childregs = *current_pt_regs(); //设置子进程堆栈的eax的值为0,保证子进程的返回值为0 childregs->ax = 0; if (sp) childregs->sp = sp; //子进程的ip返回地址,即子进程返回时的执行位置 p->thread.ip = (unsigned long) ret_from_fork; ... }
3.3.3 子进程的返回
当子进程创建成功后,系统会将它加入到运行列表中,等待CPU调度,当CPU执行时,此时子进程的eip指向是entry_32.S中的ret_from_fork,子进程也从此处开始执行ENTRY(ret_from_fork) CFI_STARTPROC pushl_cfi %eax call schedule_tail GET_THREAD_INFO(%ebp) popl_cfi %eax pushl_cfi $0x0202 # Reset kernel eflags popfl_cfi jmp syscall_exit CFI_ENDPROC END(ret_from_fork)
查看ret_from_fork函数,可以看到相对于主进程的system_call而言,最终的处理都是调用
4000
jmp syscall_exit, 而子进程相对于主进程少了int 80与SAVE_ALL的压栈。但是子进程在 copy_thread()中的
*childregs = *current_pt_regs();这里写代码片
已经保存了相应的值,因此,子进程执行到此处时又与主进程相同了,但是其中eax的值已经修改为0, 以保证子进程可以执行它自己的程序。
4. 总结
1) 进程的创建也是一种系统调用。 主进程的返回与普通的系统调用的逻辑相同,但是子进程通过ret_from_fork与pt_reg的配合,巧妙的实现了子进程的返回2)子进程的创建在内核空间,首次调用也是在内核空间,再从内核空间调度到用户空间
相关文章推荐
- Linux socket 初步
- Linux Kernel 4.0 RC5 发布!
- linux lsof详解
- linux 文件权限
- Linux 执行数学运算
- 10 篇对初学者和专家都有用的 Linux 命令教程
- Linux 与 Windows 对UNICODE 的处理方式
- Ubuntu12.04下QQ完美走起啊!走起啊!有木有啊!
- 解決Linux下Android开发真机调试设备不被识别问题
- 运维入门
- 运维提升
- Linux 自检和 SystemTap
- 神器SystemTap
- Ubuntu Linux使用体验
- c语言实现hashmap(转载)
- Linux 信号signal处理机制
- linux下mysql添加用户
- Scientific Linux 5.5 图形安装教程