分析system_call中断处理过程
一、实验过程
新版 MenuOS 中添加了 fork 功能。
接下来用 gdb 跟踪调试了 fork 等工作过程。
二、实验分析
1. 预备知识
首先我们分析下 task_struct 结构,其中主要有:
- 进程状态( 记录进程等待、运行或死锁三种状态 )
- 调度信息
- 标识符
- 进程间的通讯情况
- 进程链接信息( 进程链表的插入等操作信息 )
- 时间和定时器信息
- 文件系统信息
- 页面管理信息
- 和处理器相关的环境( 上下文 )信息等
具体代码参见:http://codelab.shiyanlou.com/xref/linux-3.18.6/include/linux/sched.h#1235
2. 进程创建分析
fork 函数对应着内核处理过程中的 sys_clone。
首先看下 fork 一个子进程的代码
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 int main(int argc, char * argv[])
5 {
6 int pid;
7 /* fork another process */
8 pid = fork();
9 if (pid < 0)
10 {
11 /* error occurred */
12 fprintf(stderr,"Fork Failed!");
13 exit(-1);
14 }
15 else if (pid == 0)
16 {
17 /* child process */
18 printf("This is Child Process!\n");
19 }
20 else
21 {
22 /* parent process */
23 printf("This is Parent Process!\n");
24 /* parent will wait for the child to complete*/
25 wait(NULL);
26 printf("Child Complete!\n");
27 }
28 }
通过调用 fork() 来创建一个新的进程,创建的进程有父子关系。通过复制现有进程来实现新创建一个新的进程。fork、vfork和clone三个系统调用都可以创建一个新进程,而且都是通过调用do_fork来实现进程的创建。
复制过程为先负值一个 PCB—task_struct
1 err = arch_dup_task_struct(tsk, orig);
再给新的进程分配一个内核堆栈
1 ti = alloc_thread_info_node(tsk, node);
2 tsk->stack = ti;
3 setup_thread_stack(tsk, orig); // 这里只是复制thread_info,而非复制内核堆栈
此次复制并非把进程原封不动的复制过来,其中 pid、进程链表等数据都需要修改,这部分需要参见 copy_process 内部。
新进程的创建是从 return_from_fork 开始执行的,复制时至复制了 int 指令和 save_all 等部分内容。之后新的参数,系统调用号等压入栈中。
copy_process 函数被 do_fork 函数调用,主要完成进程数据结构的创建,资源的初始化。初始化的方式可以通过重新分配进行初始化,也可以和父进程共享同一组页表,最终初始化的方式由传入 clone 的参数来确定。
三、总结
总的来说,新的进程的创建是通过 do_fork 函数来实现的。新进程的创建过程是从return_from_fork开始,大体上就是先复制 PCB,再给新的进程分配要给新的内核堆栈,并修改复制过来的进程数据。
参考资料
- http://www.oss.org.cn/kernel-book/ch04/4.3.htm
李若森
原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000