第一次作业 基于Linux Kernel 2.6.32 的进程模型分析

时间:2021-09-02 07:08:40

1. 作业内容

挑选一个开源的操作系统,深入源码分析其进程模型,具体包含如下内容:

  • 操作系统是怎么组织进程的
  • 进程状态如何转换(给出进程状态转换图)
  • 进程是如何调度的
  • 谈谈自己对该操作系统进程模型的看法

    2、进程介绍

    2.1 进程的概念

      进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

    2.2 进程的查看

      2.2.1 Windows查看进程

        在Windows系统下可用CTRL+ALT+DEL组合键打开任务管理器对进程进行查看:第一次作业 基于Linux Kernel 2.6.32 的进程模型分析

    3、操作系统如何组织进程

    3.1 进程控制块(PCB)

      Linux系统中主要的活动实体就是进程。

      每个进程执行一段独立的程序并且在进程初始化的时候拥有一个独立的控制线程。换句话说,每一个进程都拥有一个独立的程序计数器,用这个这个程序计数器可以追踪下一条将要被执行的指令。

      所有的进程都被放在一个叫做进程控制块(PCB),的数据结构中,可以理解为进程属性的集合,该控制块由操作系统创建和管理。每个进程在内核中都有一个进程控制块来维护进程相关的信息,Linux内核的进程控制块是(task_struct)结构体。Linux通过task_struct(PCB)结构体来描述一个进程的所有信息,结构体被定义在include/linux/sched.h中。进程创建时,操作系统就新建一个PCB结构,它之后就常驻内存,任一时刻可以存取, 在进程结束时删除。PCB是进程实体的一部分,操作系统通过PCB表来管理和控制进程,是进程存在的唯一标志。

    4、进程状态的转化

    4.1 三态模型

    一个进程从创建而产生至撤销而消亡的整个生命周期,可以用一组状态加以刻划,根据三态模型,进程的生命周期可分为如下三种进程状态: 
    1. 运行态(running):占有处理器正在运行 
    2. 就绪态(ready):具备运行条件,等待系统分配处理器以便运行 
    3. 等待态(blocked):不具备运行条件,正在等待某个事件的完成

    第一次作业 基于Linux Kernel 2.6.32 的进程模型分析

    4.进程是如何调度的

      4.1 调度进程

      内核中的调度程序用于选择系统中下一个要运行的进程。调度程序可以看作为在所有处于运行状态的进程之间分配CPU运行时间的管理代码。Linux进程是抢占式的,抢占发生在进程处于用户执行状态阶段,在内核执行时是不能被抢占的。

      通过调度函数schedule()函数扫描任务数组,通过比较每个就绪态任务的运行时间递减滴答计数counter的值来确定当前哪个进程运行的时间最少。哪一个值大,就表示运行时间还不长,于是就选中该进程,并使用任务切换宏函数切换到该进程运行

      4.2 进程切换

       每当选择出一个新的可运行进程时,schedule()函数就会调用定义在include/asm/system.h 中的switch_to0宏执行实际进程切换操作。该宏会把CPU 的当前进程状态(上下文) 替换成新进程的状态。在进行切换之前,switch to()首先检查要切换到的进程是否就是当前进程,如果是则什么也不做,  直接退出。否则就首先把内核全局变量current 置为新任务的指针,然后长跳转到新任务的任务状态段TSS组成的地址处,造成CPU 执行任务切换操作。此时CPU 会把其所有寄存器的状态保存到当前任务寄存器TR 中TSS 段选择符所指向的当前进程任务数据结构的tss 结构中,然后把新任务状态段选择符所指向的新任务数据结构中tss 结构中的寄存器信息恢复到CPU 中,  系统就正式开始运行新切换的任务了。 

      4. 3 中止进程

      当一个进程结束了运行或在半途中终止了运行,那么内核就需要释放该进程所占用的系统资源。这包括进程运行时打开的文件、申请的内存等。

      当一个用户程序调用exit()系统调用时,就会执行内核函数do exit()。该函数会首先释放进程代码段和数据段占用的内存页面,关闭进程打开着的所有文件,对进程使用的当前工作目录、根目录和运行程序的i节点进行同步操作。如果进程有子进程,则让init 进程作为其所有子进程的父进程。如果进程是- 个会话头进程并且有控制终端,则释放控制终端,并向属于该会话的所有进程发送挂断信号SIGHUP,这通常会终止该会话中的所有进程。然后把进程状态置为僵死状态TASK ZOMBIE。并向其原父进程发送SIGCHLD 信号,通知其某个子进程已经终止。最后do_ exit()调用调度函数去执行其他进程。由此可见在进程被终止时,它的任务数据结构仍然保留着。因为其父进程还需要使用其中的信息。
      在子进程在执行期间,父进程通常使用wait()或waitpid()函数等待其某个子进程终止。当等待的子进程被终止并处于僵死状态时,父进程就会把子进程运行所使用的时间累加到自己进程中。最终释放已终止子进程任务数据结构所占用的内存页面,并置空子进程在任务数组中占用的指针项。

    参考资料

    1. https://baike.baidu.com/item/%E8%BF%9B%E7%A8%8B/382503?fr=aladdin

    2. https://baike.baidu.com/item/%E8%B0%83%E5%BA%A6%E7%AE%97%E6%B3%95/3017645?fr=aladdin

    3. https://www.cnblogs.com/puputongtong/p/5451210.html

    4. https://blog.csdn.net/gatieme/article/details/52067518