进程概述

进程概述

进程概念

进程定义

a084f67f611ec916e89c3757bcf14667.png

进程状态

51238e9c1e1bcddf61d990aa90246f49.png

进程控制块

  • 定义:每个进程都维护的一个结构数组,用于保存进程所有的相关信息

    处理器 只会通过进程控制块 找到进程在内存中的所有相关信息

  • 相关信息:(进程相关的所有信息都存放在进程控制块中:可能是具体的数据也可能是指针)

    进程描述信息 进程控制信息和管理信息 资源分配信息 处理器相关信息
    进程唯一标识符 进程状态 代码段指针 程序状态字寄存器
    用户唯一标识符 进程优先级 数据段指针 地址寄存器
    子进程 & 父进程唯一标识符 代码段入口地址 堆栈段指针 程序计数器
    程序磁盘地址 文件描述符 通用寄存器
    进入内存时间 & 处理器使用时间 外接设备
    信号量
  • 上下文:当前进程的物理实体(PCB)和进程的运行环境(处理器各个寄存器值)被统称为上下文

  • 细节:进程控制块采用两种方式进行组织:链表 / 索引表

31a2d2f8d0e97ef1834e0c09f62c532a.png

进程调度

进程实现

实现进程的所有函数实际上都是系统调用且都在内核态下运行

  • 前提:按照 UNIX 标准实现进程;Windows 实现类似

  • 进程层次结构:

  • 进程实现方式

    • 进程创建:fork()

      • 创建过程:

        1. 父进程调用 fork() 函数创建自身的 ==副本进程==(子进程) -> fork() 函数返回子进程的进程标识符

        2. 操作系统创建 空白进程控制块 (如果进程控制块创建失败则会直接导致 进程创建失败)

        3. 操作系统为进程分配内存空间 (如果内存空间不足以容纳新的进程则会导致 进程处于阻塞态)

        4. 操作系统 初始化进程控制块信息

        5. 调度程序将子进程调度进入就绪队列等待执行

        6. 子进程可以调用 exec() 函数载入新的程序运行

        解释:子进程不再是父进程的副本,调用 `fork()` 也可以创建子进程了
        
      • 写时复制:

        • 引入:

          父进程在调用 fork() 函数时会创建副本子进程;显然需要将父进程的所有内容全部复制一遍

          但是大多数的子进程在被创建后都会立刻执行 exec() 函数去执行另一个程序;这样就显得此前复制操作非常没有必要

        • 概念:

          • 子进程在被创建后和父进程共享同一块内存空间

          • 等待子进程需要对内存空间进行 ==写操作== 时才单独为子进程分配单独的内存空间并复制父进程的内容

        • 细节:

          1. 父进程和子进程共享内存空间时是不可以对其进行写操作的

          2. 子进程写操作

            • 子进程不使用 exec() 进行写操作:操作系统仍然会让子进程和父进程共享代码段;其余内容重新分配
            • 子进程使用 exrc() 进行写操作:操作系统会对子进程的所有内容重新分配

        写时复制

      4bf1c3e2265801cf9eeb03aa08a9ac57.png

    • 进程等待:wait()

      • 父进程创建子进程之后选择等待子进程执行结束后再执行就会使用 wait() 指令
    • 进程删除:exit() & kill()

      • 正常退出:进程使用 exit() 指令即可退出;子进程退出后 exit() 指令返回 ==退出状态==

      • 异常退出:进程出现访问越界,使用非法指令,运行超时,运算出错等都会造成进程异常结束 (子进程使用了不该使用的资源)

      • 外界干预:父进程被终止或者程序员手动干预

      • 终止过程:

        1. 操作系统根据 进程唯一标识符 查找其 进程控制块 (进程控制块最后删除)
        2. 操作系统 终止 该进程的 所有子进程 并且将其拥有的资源全部 还给父进程或操作系统
        3. 操作系统在在 链表删除 该进程的 进程控制块

        级联终止:如果一个父进程被终止,其派生的所有子进程一并终止

    • 细节:

      1. 父进程调用 fork() 函数会得到子进程的进程标识符;子进程如果仍然是副本的话调用 fork() 函数只会得到 0

        解释:因为子进程也是从 fork() 函数开始执行的,如果子进程也正常调用 fork() 函数创建子进程,那就成死循环了

      2. fork() 函数时 UNIX 中 ==唯一== 可以创建进程的函数

      3. exec() 并不是一个具体的函数而是包含了 execl execv execle execve execlp execvp 六种具体函数

      4. Windows 进程相关函数:

        • CreateProcess = fork + exec
        • ExitProcess = exit;TerminateProcess = kill
#include <stdlib>

int main()
{
int childPid; // 子进程的进程标识符
childPid = fork(); // 创建子进程
if (pid < 0) // 进程创建失败
{
fprintf(stderr, "Fork Failed");
exit(-1); // 异常退出
}
else if (childPid == 0) // 子进程开始执行时也是从第一行开始执行的,毕竟是父进程的副本
{
// 子进程进入这个分支,载入其他的程序
execlp("/bin/ls", "ls", NULL);
}
else // 父进程创建子进程之后进入的分支
{
// 父进程进行等待
wait (NULL);
printf ("Child Complete");
exit(0);
}
}

进程协作 & 通信

生产者-消费者模式

Author: Fuyusakaiori
Link: http://example.com/2021/08/16/os/process/进程管理/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.