20135239益西拉姆 Linux内核分析 进程的描述和进程的创建

【益西拉姆 原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000】

第六周 进程的描述和进程的创建

一、 进程的描述

  • 进程控制块PCB——task_struct
  • 为了管理进程,内核必须对每个进程进行清晰的描述,进程描述符提供了内核所需了解的进程信息。
  • struct task_struct数据结构很庞大
  • Linux进程的状态与操作系统原理中的描述的进程状态似乎有所不同,比如就绪状态和运行状态都是TASK_RUNNING,为什么呢?
  • 进程的标示pid
  • 所有进程链表struct list_head tasks;
    • 内核的双向循环链表的实现方法 - 一个更简略的双向循环链表
  • 程序创建的进程具有父子关系,在编程时往往需要引用这样的父子关系。进程描述符中有几个域用来表示这样的关系
  • Linux为每个进程分配一个8KB大小的内存区域,用于存放该进程两个不同的数据结构:Thread_info和进程的内核堆栈
    • 进程处于内核态时使用,不同于用户态堆栈,即PCB中指定了内核栈,那为什么PCB中没有用户态堆栈?用户态堆栈是怎么设定的?
    • 内核控制路径所用的堆栈很少,因此对栈和Thread_info来说,8KB足够了
  • struct thread_struct thread; //CPU-specific state of this task
  • 文件系统和文件描述符
  • 内存管理——进程的地址空间

进程的创建

  • 进程的创建概览及fork一个进程的源代码
  • 回顾:
    • startkernel创建了cpuidle,也就是0号进程。而0号进程又创建了两个线程,一个是kernel_init,也就是1号进程,这个进程最终启动了用户态;
    • 另一个是kthreadd。这就是“道生一,一生二”。0号进程是固定的代码;
    • 1号进程是通过复制0号进程PCB之后在此基础上做修改得到的。
  • iret与int 0x80指令对应,一个是弹出寄存器值,一个是压入寄存器的值
  • 如果将系统调用类比于fork();那么就相当于系统调用创建了一个子进程,然后子进程返回之后将在内核态运行,而返回到父进程后仍然在用户态运行

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)
      {
           /* 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");
      }
}

`

创建一个新进程在内核中的执行过程

  1. fork、vfork和clone三个系统调用都可以创建一个新进程,而且都是通过调用do_fork来实现进程的创建;
  2. Linux通过复制父进程来创建一个新进程,那么这就给我们理解这一个过程提供一个想象的框架:
    • 复制一个PCB——task_struct
    • err = arch_dup_task_struct(tsk, orig);
    • 要给新进程分配一个新的内核堆栈
    • `ti = allocthreadinfo_node(tsk, node);

      tsk->stack = ti;

      setupthreadstack(tsk, orig); //这里只是复制,而非复制内核堆`

  3. 要修改复制过来的进程数据,比如pid、进程链表等等都要改改吧,见copy_process内部。
  4. 从用户态的代码看fork();函数返回了两次,即在父子进程中各返回一次,父进程从系统调用中返回比较容易理解,子进程从系统调用中返回,那它在系统调用处理过程中的哪里开始执行的呢?这就涉及子进程的内核堆栈数据状态和taskstruct中thread记录的sp和ip的一致性问题,这是在哪里设定的?copythread in copy_process
  5. *childregs = *current_pt_regs(); //复制内核堆栈
  6. childregs->ax = 0; //为什么子进程的fork返回0,这里就是原因!
  7. p->thread.sp = (unsigned long) childregs; //调度到子进程时的内核栈顶
  8. p->thread.ip = (unsigned long) ret_from_fork; //调度到子进程时的第一条指令地址

使用gdb跟踪创建新进程的过程

  • 更新menu内核,然后删除test_fork.c以及test.c(以减少对之后实验的影响
  • 编译内核,可以看到fork命令
  • 启动gdb调试,并对主要的函数设置断点
  • 在MenuOS中执行fork,就会发现fork函数停在了父进程中
  • 继续执行之后,停在了dofork的位置。然后n单步执行,依次进入copyprocess、duptaskstruct。按s进入该函数,可以看到dst = src(也就是复制父进程的struct)
  • 在copythread中,可以看到把taskpg_regs(p)也就是内核堆栈特定的地址找到并初始化
  • 到了159、160行的代码就是把压入的代码再放到子进程中:
     `*children = *current_pt_regs();
    
      childregs->ax = 0;`
    
  • 164行,是确定返回地址 p->thread.ip = (unsigned long) ret_from_fork;
  • 最后输入finish运行完毕。

总结

本周主要就是课本的进程一章的拓展,通过实践来更加的运用完整,很有趣。

时间: 2024-08-25 13:53:31

20135239益西拉姆 Linux内核分析 进程的描述和进程的创建的相关文章

20135239 益西拉姆 linux内核分析 进程的切换和系统的一般执行过程

week 8 进程的切换和系统的一般执行过程 [ 20135239 原文请转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000] 一.进程调度与进程调度的时机分析 操作系统原理中介绍了大量进程调度算法,这些算法从实现的角度看仅仅是从运行队列中选择一个新进程,选择的过程中运用了不同的策略而已.对于理解操作系统的工作机制,反而是进程的调度时机与进程的切换机制更为关键. 不同类型的进程有不同的调度需求 第一

20135239 益西拉姆 linux内核分析 可执行程序的装载

益西拉姆 + 原创作品请勿转载 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ” week 7 可执行程序的装载 1.预处理.编译.链接和目标文件的格式 从c语言到可执行程序的由来过程 可执行文件的创建——预处理.编译和链接 以helloworld为例 -s assembler 汇编 gcc -o hello hello.o -m32 是把hello.o链接成可执行文件. ELF格式的文件是怎么回事? v

20135239益西拉姆 Linux内核分析 汇编一个简单的c程序并分析其指令过程

益西拉姆+<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 第一周linux内核分析 学习笔记 一.计算机是如何工作的? 什么是冯诺依曼体系结构? 简单来讲就是存储程序计算机,而存储程序计算机又是指从硬件角度来看, X86汇编基础 学习笔记 详细内容都写在笔记中.再次不多说. 总结:以前一直搞不太懂汇编代码,学的不明不白,虽然现在也是学的不是太明白,至少知道了各个代码什么意思,以及该如何表现,这让我觉得老师的课时

20135239 益西拉姆 linux内核分析 跟踪分析Linux内核的启动过程

回顾 1.中断上下文的切换——保存现场&恢复现场 本节主要课程内容 Linux内核源代码简介 1.打开内核源代码页面 arch/目录:支持不同CPU的源代码:其中的X86是重点 init/目录:内核启动相关的代码基本都在该目录中(比如main.c等) start_kernel函数就相当于普通C程序的main函数 kernel/目录:Linux内核核心代码在kernel目录中 README 介绍了什么是Linux,Linux能够在哪些硬件上运行,如何安装内核源代码等 构造一个简单的linux系统m

20135239 益西拉姆 linux内核分析 读书笔记之第四章

chapter 4 进程调度 4.1 多任务 多任务操作系统就是能同时并发的交互执行多个进程的操作系统. 多任务系统可以划分为两类: - 非抢占式多任务: - 进程会一直执行直到自己主动停止运行(这一步骤称为让步) - 抢占式多任务: - Linux/Unix使用的是抢占式的方式:强制的挂起进程的动作就叫做抢占.进程在被抢占之前能够运行的时间是预先设置好的(也就是进程的时间片) 4.2 linux的进程调度 O(1)调度器:对大服务器的工作负载很理想,但是缺少交互进程. 反转楼梯最后期限调度算法

20135239 益西拉姆 linux内核分析 使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用

https://drive.wps.cn/preview#l/759e32d65654419cb765da932cdf5cdc 本次直接在wps上写的,因为不能连同图片一起粘贴过来,一个一个粘比较费时,所以弄了个wps链接,只能下载之后观看,但是很快就好啦,不要介意,嘿嘿.

20135239益西拉姆第四次实验报告

北京电子科技学院(BESTI) 实验报告 课程:JAVA第四次实验报告 班 级: 1352 姓 名:益西拉姆 学 号:20135239 成 绩: / 指导教师: 娄嘉鹏 实验日期: 2015.06.09 实验密级: / 预习程度: / 实验时间:15:00--18:00 仪器组次:39 必须/选修: 选修 实验序号:04 实验名称: 第四次实验 实验仪器: 名称 型号 数量 PC机 DELL 1 实验内容: 1:编写网络通信程序.(基于TCP) 2:对通信内容使用对称加密算法进行加密. 3:使用

20135201李辰希《Linux内核分析》第六周 进程的描述与创建

李辰希 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.进程的描述 操作系统的三大管理功能: 进程管理(最重要的) 内存管理 文件系统 为了管理进程,内核必须对每个进程进行清晰的描述,进程描述符提供了内核所需了解的进程信息. 进程控制块PCB task_struct: 进程状态 进程打开的文件 进程优先级信息 task_struct总体数据结构的抽象: tty:控制台 fs:文件系统

《linux 内核分析》 第6周 进程创建及描述

王一+ <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.task_struct 操作系统的三大核心功能:1.进程管理 2.内存管理 3. 文件系统 struct task_struct{     volatile long state; //进程状态,-1表示不可执行,0表示可执行,大于1表示停止     void *stack; //内核堆栈