《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一个子进程的代码

  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) /*与else共同执行*/
  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系统调用在子进程和父进程各返回一次。子进程中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。

时间: 2024-10-20 06:13:00

《Linux内核分析》第六周笔记 进程的描述和进程的创建的相关文章

LINUX内核分析第六周学习总结——进程的描述和进程的创建

LINUX内核分析第六周学习总结——进程的描述和进程的创建 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.知识概要 进程的描述 进程描述符task_struct数据结构(一) 进程描述符task_struct数据结构(二) 进程的创建 进程的创建概览及fork一个进程的用户态代码 理解进程创建过程复杂代码的方法 浏览进程创建过程相关的关键代码 创建的新进程是从哪里开始执行的

Linux内核分析——第六周学习笔记20135308

第五周 进程的描述和进程的创建 一.进程描述符task_struct数据结构 1.操作系统三大功能 进程管理 内存管理 文件系统 2.进程控制块PCB——task_struct 也叫进程描述符,为了管理进程,内核需要对每个进程进行描述,它就提供了内核所需了解的进程信息. struct task_struct数据结构很庞大,1235行~1644行 3.Linux进程状态 Linux进程的状态与操作系统原理中的描述的进程状态有所不同 操作系统状态: 就绪态 运行态 阻塞态 linux进程状态: 4.

Linux内核分析——第六周学习笔记

进程的描述和进程的创建 前言:以下笔记除了一些讲解视频中的概念记录,图示.图示中的补充文字.总结.分析.小结部分均是个人理解.如有错误观点,请多指教! PS.实验操作会在提交到MOOC网站的博客中写.

20135327郭皓--Linux内核分析第六周 进程的描述和进程的创建

进程的描述和进程的创建 一.进程的描述 操作系统三大功能: 进程管理 内存管理 文件系统 进程描述符task_struct数据结构 task _ struct:为了管理进程,内核必须对每个进程进行清晰的描述,进程描述符提供了内核所需了解的进程信息. 进程的状态:Linux进程的状态(就绪态.运行态.阻塞态) 进程的标示pid:用来标示进程 进程描述符task_struct数据结构(重要部分): 1 struct task_struct { 2 volatile long state; /* 进程

Linux内核分析第六周作业

分析Linux内核创建一个新进程的过程 首先更新MenuOS的代码,加入调用fork的命令.吐槽一句,实验楼免费用户无法连网.还好只要去github复制一段代码即可 先观察一下fork命令的实现 1 int Fork(int argc, char *argv[]) 2 { 3 int pid; 4 /* fork another process */ 5 pid = fork(); 6 if (pid<0) 7 { 8 /* error occurred */ 9 fprintf(stderr,

《Linux内核分析》第一周笔记 计算机是如何工作的

一.计算机是如何工作的? 1.存储程序计算机工作模型 1)冯诺依曼体系结构 学习研究计算机的基本概念.就是指存储程序计算机.所有的有计算功能的电子设备小到计算器,大到超级计算机核心部分都可以用这种体系结构来描述. 2)存储程序计算机工作模型 从硬件(计算机的主板):逻辑上抽象为,CPU与内存之间通过总线连接,CPU内部有一个关键寄存器IP(Instruction Pointer)(在16位CPU中叫IP,在32位CPU中叫EIP,在64位CPU中叫RIP),总是指向内存的某块区域,指向内存中的代

linux内核分析 第六周 分析Linux内核创建一个新进程的过程

进程的描述 操作系统的三大管理功能:进程管理.内存管理.文件系统 为了管理进程,内核必须对每个进程进行清晰的描述,进程描述符提供了内核所需了解的进程信息. 进程控制块PCB task_struct:进程状态.进程打开的文件.进程优先级信息 task_struct总体数据结构的抽象: tty:控制台 fs:文件系统 files:文件描述符 mm:内存管理 signal:信号描述 进程的状态: 注意:Linux下,中就绪状态和运行状态都是TASK_RUNNING 一.gdb跟踪分析一个fork系统调

linux内核分析 第六周读书笔记

第三章 进程管理 3.1 进程 进程:处于执行期的程序 线程是在进程活动中的对象:内核调度的对象是线程而不是进程,在Linux系统中,并不区分线程和进程 在现代操作系统中, 进程提供两种虚拟机制:虚拟内存器和虚拟内存. 进程在创建它的时刻开始存活,这通常是调用fork系统的结果.该系统调用通过复制一个现有进程来创建一个全新的进程.fork系统调用从内核返回两次,一次到父进程,另一次回到新产生的子进程. 通常,创建新的进程都是为了立即执行新的不同的程序,而接着调用exec()这组函数就可以创建新的

linux内核分析第六周-分析Linux内核创建一个新进程的过程

Linux内核对进程管理是操作系统的重要任务之一. 此次实验就是了解内核创建一个新进程的大致过程. 为了简单,使用fork再用户态创建一个进程.代码如下: 下面是准备工作??? cd LinuxKernel rm -rf menu git clone https://github.com/mengning/menu.git cd menu mv test_fork.c test.c make rootfs 打开gdb进行远程调试? 设置断点 b sys_clone b do_fork b dup

LINUX内核分析第七周学习总结——可执行程序的装载

LINUX内核分析第六周学习总结——进程的描述和进程的创建 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.知识概要 (一)预处理.编译.链接和目标文件的格式 1.可执行程序是怎么得来的 2.目标文件的格式ELF 3.静态链接的ELF可执行文件和进程的地址空间 (二)可执行程序.共享库和动态加载 1.装载可执行程序之前的工作 2.装载时动态链接和运行时动态链接应用举例 (三)