Linux内核分析 笔记六 进程的描述和进程的创建 ——by王玥
一、知识点总结
(一)进程的描述
1.操作系统内核里有三大功能:
- 进程管理
- 内存管理
- 文件系统
2.进程描述符:task_struct
2.进程描述符——struct task_struct
1. pid_t pid又叫进程标识符,唯一地标识进程
2.双向循环链表链接起了所有的进程,也表示了父子、兄弟等进程关系
3. struct mm_struct 指的是进程地址空间,涉及到内存管理(对于X86而言,一共有4G的地址空间)
4. thread_struct thread 与CPU相关的状态结构体
5. struct *file表示打开的文件链表
6. Linux为每个进程分配一个8KB大小的内存区域,用于存放该进程两个不同的数据结构:Thread_info和进程的内核堆栈
3.进程状态转换图
数据结构分析:
struct task_struct {
1236 volatile long state; //运行状态
1237 void *stack; //进程的内核堆栈
1238 atomic_t usage;
1239 unsigned int flags;//每个进程的标识符 1240 unsigned int
#ifdef CONFIG_SMP //这部分是条件编译
1243 struct llist_node wake_entry;
1244 int on_cpu;
1245 struct task_struct *last_wakee;
1246 unsigned long wakee_flips;
1247 unsigned long wakee_flip_decay_ts;
.....
struct list_head tasks; //这部分很关键,是进程的列表
1296#ifdef CONFIG_SMP
1297 struct plist_node pushable_tasks;
1298 struct rb_node pushable_dl_tasks;
1299#endif
1300
1301 struct mm_struct *mm, *active_mm;//和地址的内存空间,内存管理有关的,每个地址有独立的地址空间。
1302#ifdef CONFIG_COMPAT_BRK
1303 unsigned brk_randomized:1;
1304#endif
1305 /* per-thread vma caching */
1306 u32 vmacache_seqnum;
1307 struct vm_area_struct *vmacache[VMACACHE_SIZE];
1308#if defined(SPLIT_RSS_COUNTING)
1309 struct task_rss_stat rss_stat;
1310#endif
链表的数据结构如下:
它是一个双向链表,参见include/linux/list.h
1330 pid_t pid;//进程的pid,来标识某一个进程
1331 pid_t tgid;
....
/*
1338 * pointers to (original) parent process, youngest child, younger sibling,//进程的父子关系
1339 * older sibling, respectively. (p->father can be replaced with
1340 * p->real_parent->pid)
1341 */
1342 struct task_struct __rcu *real_parent; /* real parent process */
1343 struct task_struct __rcu *parent; /* recipient of SIGCHLD, wait4() reports */
1344 /*
1345 * children/sibling forms the list of my natural children
1346 */
1347 struct list_head children; /* list of my children */
1348 struct list_head sibling; /* linkage in my parent's children list */
1349 struct task_struct *group_leader; /* threadgroup leader */
1350
...1411/* CPU-specific state of this task */
1412 struct thread_struct thread;//当前CPU相关的状态,在进程切换时起关键作用。![]()
(二)进程的创建
- 1.进程的状态以及fork一个进程的用户态代码
- 2.fork系统调用在父进程和子进程各返回一次
- 3.TASK_RUNNING具体是就绪还是执行,要看系统当前的资源分配情况
- 4. TASK_ZOMBIE也叫僵尸进程
fork一个子进程的代码
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) //pid == 0和下面的else都会被执行到(一个是在父进程中即pid ==0的情况,一个是在子进程中,即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.}- iret与int 0x80指令对应,一个是弹出寄存器值,一个是压入寄存器的值
- 如果将系统调用类比于fork();那么就相当于系统调用创建了一个子进程,然后子进程返回之后将在内核态运行,而返回到父进程后仍然在用户态运行
- 回顾:系统调用的进程创建过程

创建一个新进程在内核中的执行过程
1.一个新创建的子进程,(当它获得CPU之后)是从哪一行代码进程执行的?
- 与之前写过的my_kernel相比较,kernel中是可以指定新进程开始的位置(也就是通过eip寄存器指定代码行)。fork中也有相似的机制
这就涉及子进程的内核堆栈数据状态和task_struct中thread记录的sp和ip的一致性问题,这是在哪里设定的?copy_thread in copy_process
- fork、vfork和clone三个系统调用都可以创建一个新进程,而且都是通过调用do_fork来实现进程的创建;
- Linux通过复制父进程来创建一个新进程,那么这就给我们理解这一个过程提供一个想象的框架:
复制一个PCB——task_struct
- err = arch_dup_task_struct(tsk, orig);
要给新进程分配一个新的内核堆栈
- ti = alloc_thread_info_node(tsk, node);
- tsk->stack = ti;
- setup_thread_stack(tsk, orig); //这里只是复制thread_info,而非复制内核堆栈
要修改复制过来的进程数据,比如pid、进程链表等等都要改改吧,见copy_process内部。
从用户态的代码看fork();函数返回了两次,即在父子进程中各返回一次,父进程从系统调用中返回比较容易理解,子进程从系统调用中返回,那它在系统调用处理过程中的哪里开始执行的呢?这就涉及子进程的内核堆栈数据状态和task_struct中thread记录的sp和ip的一致性问题,这是在哪里设定的?copy_thread in copy_process
- *childregs = *current_pt_regs(); //复制内核堆栈
- childregs->ax = 0; //为什么子进程的fork返回0,这里就是原因!
- p->thread.sp = (unsigned long) childregs; //调度到子进程时的内核栈顶
- p->thread.ip = (unsigned long) ret_from_fork; //调度到子进程时的第一条指令地址
- dup_thread复制父进程的PCB

- copy_process修改复制的PCB以适应子进程的特点,也就是子进程的初始化

分配一个新的内核堆栈(用于存放子进程数据)
- 内核堆栈的一部分也要从父进程中拷贝

-
根据拷贝的内核堆栈情况设置eip,esp寄存器的值
使用gdb跟踪创建新进程的过程
- 更新menu内核,然后删除test_fork.c以及test.c(以减少对之后实验的影响)

- 编译内核,可以看到fork命令

- 启动gdb调试,并对主要的函数设置断点


- 在MenuOS中执行fork,就会发现fork函数停在了父进程中
- 继续执行之后,停在了do_fork的位置。然后n单步执行,依次进入copy_process、dup_task_struct。按s进入该函数,可以看到dst =src(也就是复制父进程的struct)

3.在copy_thread中,可以看到把task_pg_regs(p)也就是内核堆栈特定的地址找到并初始化
4.到了159、160行的代码就是把压入的代码再放到子进程中:
*children = *current_pt_regs();
childregs->ax = 0;164行,是确定返回地址
p->thread.ip = (unsigned long) ret_from_fork;最后,可以输入finish使得进程运行完。
Linux内核分析 笔记六 进程的描述和进程的创建 ——by王玥的更多相关文章
- Linux内核分析第六周学习总结:进程的描述和进程的创建
韩玉琪 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.进程的描述 ...
- LINUX内核分析第六周学习总结——进程的描述和进程的创建
LINUX内核分析第六周学习总结——进程的描述和进程的创建 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/cours ...
- Linux内核分析第六周学习笔记——分析Linux内核创建一个新进程的过程
Linux内核分析第六周学习笔记--分析Linux内核创建一个新进程的过程 zl + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/U ...
- LINUX内核分析第六周学习总结——进程的描述与创建
LINUX内核分析第六周学习总结--进程的描述与创建 标签(空格分隔): 20135321余佳源 余佳源 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc ...
- linux内核分析第六周学习笔记
LINUX内核分析第六周学习总结 标签(空格分隔): 20135328陈都 陈都 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc.study.163.c ...
- Linux内核分析实验六
Linux内核分析实验六 进程控制块PCB——task_struct(进程描述符) 为了管理进程,内核必须对每个进程进行清晰的描述,进程描述符提供了内核所需了解的进程信息. struct task_s ...
- Linux内核分析(六)----字符设备控制方法实现|揭秘系统调用本质
原文:Linux内核分析(六)----字符设备控制方法实现|揭秘系统调用本质 Linux内核分析(六) 昨天我们对字符设备进行了初步的了解,并且实现了简单的字符设备驱动,今天我们继续对字符设备的某些方 ...
- 20135327郭皓--Linux内核分析第六周 进程的描述和进程的创建
进程的描述和进程的创建 一.进程的描述 操作系统三大功能: 进程管理 内存管理 文件系统 进程描述符task_struct数据结构 task _ struct:为了管理进程,内核必须对每个进程进行清晰 ...
- Linux内核分析——第六周学习笔记20135308
第六周 进程的描述和进程的创建 一.进程描述符task_struct数据结构 1.操作系统三大功能 进程管理 内存管理 文件系统 2.进程控制块PCB——task_struct 也叫进程描述符,为了管 ...
随机推荐
- 11LaTeX学习系列之---LaTeX的特殊字符
目录 目录 前言 (一)源代码 (二)输出效果 目录 本系列是有关LaTeX的学习系列,共计19篇,本章节是第11篇. 前一篇:10LaTeX学习系列之---Latex的文档结构 后一篇:12LaTe ...
- 软件工程实践_Task1
(1)回想一下你初入大学时对计算机专业的畅想 当初你是如何做出选择计算机专业的决定的? 说起来,当初选择计算机专业的缘由,更多应该归因于兴趣.虽然对CS全然不知,但也一点都不妨碍对它的神奇感到向往.再 ...
- Department and Student
软工结对作业之二 本人ID:杨光海天 031502634 队友(大佬)ID:陈涵 031502106 GitHub链接 BIN文件地址 代码文件 整体概况 模型建立 学生类,属性包括: * 1)编号 ...
- Java中实现多线程继承Thread类与实现Runnable接口的区别
Java中线程的创建有两种方式: 1. 通过继承Thread类,重写Thread的run()方法,将线程运行的逻辑放在其中 2. 通过实现Runnable接口,实例化Thread类 在实际应用中, ...
- [Jsoi2013]快乐的jyy
题目 这个需要我们瞎\(yy\)一下就能做了 我们先对于第一个串建立\(PAM\) 我们把第二个串丢上去匹配,这里匹配出来的是以每一个位置为结尾且在另一个串里存在的最长回文后缀的长度 对于每一个位置开 ...
- ORB-SLAM2(1) Linux下配置和编译
ORB-SLAM2 官网:https://github.com/raulmur/ORB_SLAM2 配置教程:http://blog.csdn.net/zzlyw/article/details/54 ...
- JS进阶之---基本数据类型,引用类型,内存空间
一.内存空间: 为了便于理解,我们暂且先将Js的内存分为栈内存和堆内存. JavaScript具有垃圾自动回收机制,内存的分配与回收都完全实现了自动管理.所以我们在开发时一般会忽视内存空间的问题.但是 ...
- .net Parallel并行使用注意事项
因项目响应过慢,代码优化空间不大,在暂时无法调整系统架构的情况下,只有使用.NET中的TPL解决一些模块耗时过多的问题.但在使用过程中也碰到了一些问题,现在把它写下来,用于备忘. 1. Paralle ...
- C中指针符*和取址符&
学习了C语言之后,关于指针部分看了无数遍,有时候明明觉得自己看懂了,指针就是地址,但是总是在看代码时候糊里糊涂的搞不明白,最近又关于指针强化了一把. 大部分情况下对于程序中指针糊涂是因为不明白指针符“ ...
- leetcode46. Permutations 、47. Permutations II、 剑指offer字符串的排列
字符串排列和PermutationsII差不多 Permutations第一种解法: 这种方法从0开始遍历,通过visited来存储是否被访问到,level代表每次已经存储了多少个数字 class S ...


...
