《Linux内核分析》第六周笔记 进程的描述和进程的创建
进程的描述和进程的创建
一、进程的描述
1、进程描述符task_struct数据结构(一)
操作系统的三大功能:进程管理(核心)、内存管理、文件系统。
进程控制块PCB——task_struct(进程描述符):为了管理进程,内核必须对每个进程进行清晰的描述,进程描述符提供了内核所需了解的进程信息。
- struct task_struct数据结构很庞大,共有约400行代码

- Linux进程的状态与操作系统原理中的描述的进程状态似乎有所不同,比如就绪状态和运行状态都是TASK_RUNNING,为什么呢?
操作系统原理中有三个状态:就绪状态、运行状态。阻塞状态。
调用fork创建一个新进程的时候实际上的状态是TASK_RUNNING(就绪但没有在运行),当调度器选择一个task时还是切换到TASK_RUNNING,(为什么呢?当进程是TASK_RUNNING状态时是可运行的,但是否运行还是看是否获得CPU的控制权(有没有在CPU上实际的执行))进程调用do_exit()中止执行,进入TASK_ZOMBIE(僵尸进程)
一个正在运行的进程在等待特定的事件或者是资源的时候会进入阻塞态,当阻塞的条件没有了的时候,就进入就绪态。
- 进程的标示pid
pid及tpid用来标识进程的
- 所有进程链表struct list_head tasks;
- 内核的双向循环链表的实现方法 - 一个更简略的双向循环链表
- 程序创建的进程具有父子关系,在编程时往往需要引用这样的父子关系。进程描述符中有几个域用来表示这样的关系
- Linux为每个进程分配一个8KB大小的内存区域,用于存放该进程两个不同的数据结构:Thread_info和进程的内核堆栈
- 进程处于内核态时使用,不同于用户态堆栈,即PCB中指定了内核栈,那为什么PCB中没有用户态堆栈?用户态堆栈是怎么设定的?
- 内核控制路径所用的堆栈很少,因此对栈和Thread_info来说,8KB足够了
- struct thread_struct thread; //CPU-specific state of this task
- 文件系统和文件描述符
- 内存管理——进程的地址空间
2、进程描述符task_struct数据结构(二)
阅读理解task_struct数据结构
struct task_struct {
1236 volatile long state; /*state是运行状态*/
1237 void *stack;/*指定了进程的内核堆栈 */
1238atomic_tusage;
1239 unsigned int flags; /* 标识符*/
1240 unsigned int ptrace;
1241
1242#ifdef CONFIG_SMP/*条件编译多处理器用到*/
1243 struct llist_nodewake_entry;
1244 int on_cpu;
1245 struct task_struct *last_wakee;
1246 unsigned long wakee_flips;
1247 unsigned long wakee_flip_decay_ts;
1248
1249 int wake_cpu;
1250#endif
1251 int on_rq;/*运行队列和进程调度相关程序*/
1252
1253 int prio, static_prio, normal_prio;
1254 unsigned int rt_priority;
1255 const struct sched_class *sched_class;
1256 struct sched_entityse;
1257 struct sched_rt_entityrt;
1258#ifdef CONFIG_CGROUP_SCHED
1259 struct task_group *sched_task_group;
1260#endif
1295 struct list_head tasks;/*进程链表*/


/*双向链表*/
1296#ifdef CONFIG_SMP
1297 struct plist_nodepushable_tasks;
1298 struct rb_nodepushable_dl_tasks;
1299#endif
1300
1301 struct mm_struct *mm, *active_mm;/*进程管理进程的地址空间相关*/ 每个进程有独立的进程地址空间4G,32位x86。
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_statrss_stat;
1310#endif
1330 pid_t pid;/*标识*/
1331pid_ttgid;
1337 /*
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 */
1411/*当前任务和CPU相关的状态,在进程上下文切换的过程中起着重要的作用 */
1412 struct thread_struct thread;
1413/* filesystem information */

1414 struct fs_struct *fs;/*文件系统相关的数据结构*/
1415/* open file information */
1416 struct files_struct *files;/*打开的文件描述符列表*/
1417/* namespaces */
1418 struct nsproxy *nsproxy;
1419/* signal handlers *//*与信号处理相关的工作*/
二、进程的创建
1、进程的创建概览及fork一个进程的用户态代码
进程描述符是整个系统管理中挈领性的东西。
了解进程是如何创建的?进程之间如何调度切换的?

一号进程的创建:复制了0号进程的pcb,然后根据1号进程的需要修改,再加载一个init执行程序 。
fork一个子进程的代码
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- 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) /*与else共同执行*/
- {
- /* 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。父进程中pid的返回值是:子进程的ID。
fork之后两个进程。
2、理解进程创建过程复杂代码的方法
在进程调度的过程中,调度到一个未调度的新进程,执行的起点是我们设定的my process的ip。
创建一个新进程就是复制当前进程的信息来实现的。
一个父进程创建一个子进程,有一个地方复制子进程的pcb,修改复制出来的pcb.
要给新进程分配一个新的内核堆栈.
回顾:系统调用的进程创建过程

Linux中创建进程一共有三个函数:
- fork,创建子进程
- vfork,与fork类似,但是父子进程共享地址空间,而且子进程先于父进程运行。
- clone,主要用于创建线程
这里值得注意的是,Linux中得线程是通过模拟进程实现的,较新的内核使用的线程库一般都是NPTL。
3、浏览进程创建过程相关的关键代码

4、创建的新进程是从哪里开始执行的
fork,vfork,clone都可以创建新进程,他们都是通过调用do_fork来实现的。

ip指向ret_from_fork
fork()系统调用产生的子进程在系统调用处理过程中从ret_from_fork开始执行。
只复制了部分内核堆栈

5、使用gdb跟踪创建新进程的过程
为了减少对之后实验的影响,删除test_fork.c以及test.c,编译内核:

gdb调试,设断点:


执行fork,发现fork函数停在了父进程中。
特别关注新进程是从哪里开始执行的?为什么从哪里能顺利执行下去?即执行起点与内核堆栈如何保证一致。
- ret_ from_ fork决定了新进程的第一条指令地址。
- 子进程从ret_ from_ fork处开始执行。
- 因为在ret_ from_ fork之前,也就是在copy_ thread()函数中* childregs = * current_ pt_ regs();该句将父进程的regs参数赋值到子进程的内核堆栈。
- * childregs的类型为pt_ regs,里面存放了SAVE_ ALL中压入栈的参数,因此在之后的RESTORE ALL中能顺利执行下去。
三、总结
fork()函数创建新进程是通过下列一系列函数实现的:fork() -> sys_clone() -> do_fork() -> dup_task_struct() -> copy_process() -> copy_thread() -> ret_from_fork()。操作系统的三大功能:进程管理(核心)、内存管理、文件系统。进程控制块PCB——task_struct(进程描述符):为了管理进程,内核必须对每个进程进行清晰的描述,进程描述符提供了内核所需了解的进程信息。Linux通过复制父进程来创建一个新进程,fork系统调用在子进程和父进程各返回一次。子进程中pid的返回值是:0。父进程中pid的返回值是:子进程的ID。
《Linux内核分析》第六周笔记 进程的描述和进程的创建的更多相关文章
- Linux内核分析第六周学习笔记——分析Linux内核创建一个新进程的过程
Linux内核分析第六周学习笔记--分析Linux内核创建一个新进程的过程 zl + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/U ...
- LINUX内核分析第六周学习总结——进程的描述和进程的创建
LINUX内核分析第六周学习总结——进程的描述和进程的创建 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/cours ...
- LINUX内核分析第六周学习总结——进程的描述与创建
LINUX内核分析第六周学习总结--进程的描述与创建 标签(空格分隔): 20135321余佳源 余佳源 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc ...
- linux内核分析第六周学习笔记
LINUX内核分析第六周学习总结 标签(空格分隔): 20135328陈都 陈都 原创作品转载请注明出处 <Linux内核分析>MOOC课程 http://mooc.study.163.c ...
- Linux内核分析——第六周学习笔记20135308
第六周 进程的描述和进程的创建 一.进程描述符task_struct数据结构 1.操作系统三大功能 进程管理 内存管理 文件系统 2.进程控制块PCB——task_struct 也叫进程描述符,为了管 ...
- Linux内核分析第六周学习总结:进程的描述和进程的创建
韩玉琪 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.进程的描述 ...
- 20135327郭皓--Linux内核分析第六周 进程的描述和进程的创建
进程的描述和进程的创建 一.进程的描述 操作系统三大功能: 进程管理 内存管理 文件系统 进程描述符task_struct数据结构 task _ struct:为了管理进程,内核必须对每个进程进行清晰 ...
- Linux内核分析——第六周学习笔记
进程的描述和进程的创建 前言:以下笔记除了一些讲解视频中的概念记录,图示.图示中的补充文字.总结.分析.小结部分均是个人理解.如有错误观点,请多指教! PS.实验操作会在提交到MOOC网站的博客中写.
- "Linux内核分析"第六周实验报告
张文俊 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 1.进程的描述 ...
- linux内核分析 第六周
一.进程的描述 为了管理进程,内核必须对每个进程进行清晰的描述,进程描述符提供了内核所需了解的进程信息. 1.进程控制块PCB--task_struct 进程状态 进程打开的文件 进程优先级信息 2. ...
随机推荐
- 类装载器-ClassLoader
类装载器的工作机制 类装载器就是寻找类的字节码文件并构造出类在JVM内部表示对象的组件.在Java中,类装载器把一个类装入JVM中,需要经过以下步骤: 装载:查找和导入Class文件. 链接:执行校验 ...
- JDBC学习笔记之SQLException介绍
1. SQLException 的概述 当使用 JDBC 与数据源(在本文中的数据源表示我们实际使用的数据库)进行交互的时候遇见错误的时候,将会抛出名为 SQLException 的异常.一个 SQL ...
- vue_表单控件
Vue.js中提供 v-model 的指令对表单元素进行双向数据绑定,在修改表单元素值的同时,实例 vm 中对应的属性值也同时更新,反之亦然.本小节介绍主要input元素绑定v-model 后的具体用 ...
- .whl文件打开方式 Python
wheel文件本质上就是zip或者rar,只不过他更加方便python的安装以及使用.在之前的图片中我们只要使用pip install wheel 就可以安装wheel. 在安装了wheel之后我们可 ...
- javascript中apply()和call()方法及区别
call()和apply()方法 1.方法定义 call方法: 语法:obj.call(thisObj, arg1, arg2, ...); 定义:调用一个对象的一个方法,以另一个对象替换当前对象. ...
- Git基础命令的使用——详细教程2
Git安装默认完成后,在左面点击鼠标右键,鼠标右键菜单中会多出来两个选项: a) Git GUI Here <—> 图型界面模式 b) Git Bash Here <—> ...
- go标准库的学习-fmt
参考https://studygolang.com/pkgdoc 导入方式: import "fmt" mt包实现了类似C语言printf和scanf的格式化I/O.格式化动作(' ...
- python3 练习题(多级菜单)
'''多级菜单需求:1.现有省/市/县3级结构,要求程序启动后,允许用户可依次选择进入各子菜单2.可在任意一级菜单返回上一级3.可以在任意一级菜单退出程序所需新知识点: 列表/字典'''menu = ...
- 使用 ps、strace、lsof 进行 Linux 进程 trouble-shooting
linux_observability_tools 介绍 在Linux 下进行进程的排错,有很多方法.比如,修改源代码,print出一些关键的信息,如果代码是Python 的话,可以使用trace ...
- leetcode538. Convert BST to Greater Tree
https://www.cnblogs.com/grandyang/p/6591526.html 这个题本质上是中序遍历的反向.中序遍历是从小到大,而这个题目是从大到小,然后每个数加上比自己大的所有数 ...