进程的描述和进程的创建
2016-03-30 11:14
267 查看
进程的描述和进程的创建-分析Linux内核创建一个新进程的过程
符钰婧 原创作品转载请注明出处
《Linux内核分析》MOOC课http://mooc.study.163.com/course/USTC-1000029000
这次的实验是围绕着fork函数对应的系统调用处理过程来进行的。
一、首先简单阅读理解一下task_struct数据结构(配图为视频截图):
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330104239769-341609861.jpg)
1236行【state】表示进程状态;
1237行【stack】指定了内核的进程堆栈;
1242行【SMP】是一个条件编译,多处理器时用到;
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330104403223-117796371.jpg)
这一段代码和进程调度相关
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330104556066-1417642136.jpg)
1295行【tasks】进程链表(双向循环链表);
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330104712394-1104771475.jpg)
管理进程地址空间;
*可简单的认为:每一个进程都有自己独立的进程地址空间。
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330104744894-1598186012.jpg)
进程的父子关系(可用图直观表示)
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330104811832-1058986443.jpg)
P0有三个儿子P1、P2、P3;P1有两个兄弟;P3有一个儿子。
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330104906394-818657287.jpg)
当前任务CPU相关的状态。在进程上下文切换的时候起着关键性的作用。
二、接下来分析fork函数对应的内核处理过程sys_clone。
虽然使用的是sys_clone函数,但最终调用的还是do_fork
do_fork的内容:
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330104942863-874487445.jpg)
这是fork的主要例程
其中有一个copy_process:
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330105024363-23015917.jpg)
这是创建一个进程内容的主要代码
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330105226098-28149457.jpg)
这里的if语句都是一些出错处理
进入复制:
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330105321019-1892743461.jpg)
把数据结构src加个*表示它的值,然后赋给dst
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330105408941-1045031120.jpg)
内核堆栈就是由thread_info和堆栈两个合在一起的union;
所以上面的代码就是alloc了一个内核堆栈。
实际的代码:
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330105516785-921172433.jpg)
153行表示创建了两个一定大小的页(内存)
创建的页面一部分用来存放thread_info,另一部分就是内核堆栈;
这一段代码做了实际分配内核空间的效果。
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330105613004-807280748.jpg)
这个函数也是做了一个复制的工作
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330105639832-1513755618.jpg)
回到这个位置,p指向的是进程的PCB;
代码的后面有大量修改进程的内容(初始化)。
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330105716488-88818829.jpg)
这是理解的关键。
在copy_thread的时候都做了些什么:
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330105844785-1458367909.jpg)
从子进程pid(内核堆栈)的位置找到了栈空间的地址;
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330105858941-193604992.jpg)
然后赋给了栈底;
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330105927426-625913948.jpg)
这里做的是内核堆栈中已有数据的拷贝和制定新进程的第一条指令地址;
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330110015129-165435874.jpg)
同时还复制了thread.ip的值。
ip指向的是ret_from_fork,也就是说子进程是从这个位置开始执行的。
在复制内核堆栈的时候只复制了SAVE_ALL相关的一部分内容:
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330110044223-857172042.jpg)
其中的内容为int指令和SAVE_ALL压到内核栈的内容。
系统调用的总控程序:
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330110114285-1614314775.jpg)
子进程从290行开始执行,此时它的内核堆栈只有一点内容;
然后进入298行:
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330110156691-2134608576.jpg)
之后内核堆栈继续执行,然后正常的返回到用户态。
三、用gdb跟踪分析sys_clone
(1)先把menu删掉,然后克隆一份新的
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330110230988-184100442.jpg)
然后用test_fork.c将test.c覆盖掉,然后make_rootfs。
(2)进行编译可看到增加了fork指令
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330110251941-1755015950.jpg)
(3)进行gdb调试(准备工作)
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330110324379-1727777536.jpg)
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330110421738-1861931607.jpg)
(4)设断点
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330110519129-1583344032.jpg)
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330110545473-1668118155.jpg)
(5)按c执行,可看到执行到do_fork处
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330110609816-1526776340.jpg)
(6)按n可执行到copy_process
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330110943785-1373037024.jpg)
(7)之后进入了dup_task_struct:
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330111252676-1205734447.jpg)
(8)继续执行可到copy_thread
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330111330144-15299711.jpg)
之后可跟踪到ret_form_fork,单步执行直到结束。
四、总结
当子进程获得cpu控制权开始运行的时候,它的ret_from_fork可以做一系列工作,然后返回到用户态;
此时的进程已经不是原来父进程的进程空间了。
---恢复内容结束---
符钰婧 原创作品转载请注明出处
《Linux内核分析》MOOC课http://mooc.study.163.com/course/USTC-1000029000
这次的实验是围绕着fork函数对应的系统调用处理过程来进行的。
一、首先简单阅读理解一下task_struct数据结构(配图为视频截图):
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330104239769-341609861.jpg)
1236行【state】表示进程状态;
1237行【stack】指定了内核的进程堆栈;
1242行【SMP】是一个条件编译,多处理器时用到;
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330104403223-117796371.jpg)
这一段代码和进程调度相关
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330104556066-1417642136.jpg)
1295行【tasks】进程链表(双向循环链表);
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330104712394-1104771475.jpg)
管理进程地址空间;
*可简单的认为:每一个进程都有自己独立的进程地址空间。
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330104744894-1598186012.jpg)
进程的父子关系(可用图直观表示)
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330104811832-1058986443.jpg)
P0有三个儿子P1、P2、P3;P1有两个兄弟;P3有一个儿子。
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330104906394-818657287.jpg)
当前任务CPU相关的状态。在进程上下文切换的时候起着关键性的作用。
二、接下来分析fork函数对应的内核处理过程sys_clone。
虽然使用的是sys_clone函数,但最终调用的还是do_fork
do_fork的内容:
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330104942863-874487445.jpg)
这是fork的主要例程
其中有一个copy_process:
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330105024363-23015917.jpg)
这是创建一个进程内容的主要代码
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330105226098-28149457.jpg)
这里的if语句都是一些出错处理
进入复制:
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330105321019-1892743461.jpg)
把数据结构src加个*表示它的值,然后赋给dst
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330105408941-1045031120.jpg)
内核堆栈就是由thread_info和堆栈两个合在一起的union;
所以上面的代码就是alloc了一个内核堆栈。
实际的代码:
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330105516785-921172433.jpg)
153行表示创建了两个一定大小的页(内存)
创建的页面一部分用来存放thread_info,另一部分就是内核堆栈;
这一段代码做了实际分配内核空间的效果。
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330105613004-807280748.jpg)
这个函数也是做了一个复制的工作
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330105639832-1513755618.jpg)
回到这个位置,p指向的是进程的PCB;
代码的后面有大量修改进程的内容(初始化)。
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330105716488-88818829.jpg)
这是理解的关键。
在copy_thread的时候都做了些什么:
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330105844785-1458367909.jpg)
从子进程pid(内核堆栈)的位置找到了栈空间的地址;
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330105858941-193604992.jpg)
然后赋给了栈底;
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330105927426-625913948.jpg)
这里做的是内核堆栈中已有数据的拷贝和制定新进程的第一条指令地址;
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330110015129-165435874.jpg)
同时还复制了thread.ip的值。
ip指向的是ret_from_fork,也就是说子进程是从这个位置开始执行的。
在复制内核堆栈的时候只复制了SAVE_ALL相关的一部分内容:
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330110044223-857172042.jpg)
其中的内容为int指令和SAVE_ALL压到内核栈的内容。
系统调用的总控程序:
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330110114285-1614314775.jpg)
子进程从290行开始执行,此时它的内核堆栈只有一点内容;
然后进入298行:
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330110156691-2134608576.jpg)
之后内核堆栈继续执行,然后正常的返回到用户态。
三、用gdb跟踪分析sys_clone
(1)先把menu删掉,然后克隆一份新的
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330110230988-184100442.jpg)
然后用test_fork.c将test.c覆盖掉,然后make_rootfs。
(2)进行编译可看到增加了fork指令
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330110251941-1755015950.jpg)
(3)进行gdb调试(准备工作)
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330110324379-1727777536.jpg)
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330110421738-1861931607.jpg)
(4)设断点
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330110519129-1583344032.jpg)
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330110545473-1668118155.jpg)
(5)按c执行,可看到执行到do_fork处
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330110609816-1526776340.jpg)
(6)按n可执行到copy_process
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330110943785-1373037024.jpg)
(7)之后进入了dup_task_struct:
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330111252676-1205734447.jpg)
(8)继续执行可到copy_thread
![](https://images2015.cnblogs.com/blog/745167/201603/745167-20160330111330144-15299711.jpg)
之后可跟踪到ret_form_fork,单步执行直到结束。
四、总结
当子进程获得cpu控制权开始运行的时候,它的ret_from_fork可以做一系列工作,然后返回到用户态;
此时的进程已经不是原来父进程的进程空间了。
---恢复内容结束---
相关文章推荐
- 两个input放一行不能对齐
- 2015-2016前端知识体系
- java中的枚举类型
- Nginx-upstream负载均衡
- HDU 2685 I won't tell you this is about number theory 数学结论
- 【JavaScipt】可选的分号
- linux下常用命令
- 英语学习(二)
- CSS box-flex属性,然后弹性盒子模型简介
- Linux电源管理(1)_整体架构
- struts文件上传与下载简单DEMO
- Android中JNI编程详解
- js验证身份证
- js拖拽遇到的问题,待解决
- Android中JNI编程详解
- jsp内置对象详解
- 项目加入 TFS报错
- VMware SDS 之三 : VSAN的体系结构 (含VSAN 6.0、6.1版的新内容)
- 你的团队需要一套工具指南
- 重装系统后debug.keystore消失的处理