进程的组成、组织

进程和程序的区别

  • 程序是静态的,是一系列指令集合
  • 进程是动态的,是程序的一次执行过程

一个程序可以产生多个进程,操作系统要怎么区分各个进程呢?

当进程被创建的时候,操作系统会为该进程分配一个唯一的PID(Process ID,进程ID),还要记录进程所属用户ID(UID,基本的进程描述信息,可以让操作系统区分各个进程),还要记录给进程分配了哪些资源(如:分配了多少内存、正在使用哪些I/O设备、正在使用哪些文件等,可用于实现操作系统对资源的管理),还要记录进程的运行情况(如:CPU使用时间、磁盘使用情况、网络流量使用情况等,可用于实现操作系统对进程的控制、调度)。

**这些信息都被保存在一个数据结构PCB中,即进程控制块。**操作系统要对各个并发运行的进程进行管理,但凡管理时所需要的信息都会放在PCB中。
进程的组成、控制及通信
当创建一个进程时,系统为该进程建立一个PCB;当进程执行时,系统通过其PCB了解进程的现行状态信息,以便对其进行控制和管理;当进程结束时,系统收回其PCB,该进 程随之消亡。操作系统通过PCB表来管理和控制进程。
进程的组成、控制及通信
那么进程由哪些部分组成呢?

进程包括PCB、程序段和数据段。
进程的组成、控制及通信
进程和进程实体的区别

打个比方:进程实体就是一个静止不动的人,你可以观察到TA有头、四肢和躯干;进程就是这个人跑起来了,然后你可以观察到TA的头、四肢和躯干是怎样协作的,有什么变化,比如肌肉收缩舒张之类的。

进程是系统进行资源分配和调度的一个独立单位。

进程的特征

  • 动态性:进程最基本的特性
  • 并发性:可以有多个进程并发运行
  • 独立性:进程是能独立运行、独立获取资源和独立接受调度的基本单位
  • 异步性:各个进程的执行进度都可知、不同步
  • 结构性:每个进程都由PCB、程序段和数据段组成

进程的组织方式

管理进程就是管理进程的PCB。一个系统中通常可能拥有数百乃至上千个进程,为了有效地管理如此多的PCB,系统需要采用适当的方式将它们组织在一起。通常采用的组织结构有数组、散列表和链表3种方式。

  • 数组方式是将所有的PCB顺序存放在一个一维数组中。这种方式比较简单,但操作起来效率低。
  • 链表方式是将PCB链成一个链表。链式结构的特点就是灵活,便于插入和删除PCB。
  • 散列表方式是通过在PCB数组或链表上设置散列表,以加快访问速度。

实际的系统中通常会综合采用这些方法,以达到最好的效率。
Linux系统采用了多种方式来组织进程PCB,主要有以下几种:

  • 进程链表
    系统将所有的PCB链成一个双向循环链表,PCB通过它的tasks字段链入进程链表。表头指针在0号进程的PCB中。遍历该链表即可顺序地找到每个进程的PCB。
  • PID散列表
    在许多情况下,内核需要根据进程的PID查找进程。顺序扫描进程链表并逐个检查其中的PID是相当低效的。为了加快查找速度,内核中设置了若干个散列(Hash)表,其中PID散列表用于将PID映射到进程的PCB。PID散列表是一个链式散列表,所有的PCB都通过pid_chain和pid_list字段链入到这个散列表中。用PID查找散列表就可快速找到它的PCB。
  • 进程树链表
    Linux系统中,进程之间存在着父子和兄弟关系。每个进程都有一个父进程,即创建了此进程的进程。一个进程可以创建0至多个进程,称为它的子进程。具有相同父进程的进程称为兄弟进程。这样,系统中的所有进程形成了一棵进程树,每个进程都是书中的一个节点,树的根是int进程,它是所有进程的祖先进程。
  • 可执行链表
    为了方便进程的调度,系统把所有处于就绪状态的PCB组成就绪队列,处于就绪状态的进程通过PCB中的run_list字段链入适当的队列中。在进程切换时,进程调度程序从就绪队列中选择一个让其运行。当然还有执行队列和阻塞队列。
  • 等待链表
    进程因不同的原因而睡眠。系统将睡眠的进程分类管理,每类对应一个特定的事件,用一个等待队列链接。等待队列的节点并不是PCB本身,而是代表一个等待进程的节点,其中包含了指向进程PCB的指针。当某一事件发生时,内核会唤醒相应的等待队列中满足等待条件的进程,将唤醒的进程节点从队列中删除,将该进程的PCB加入到可执行队列中。

进程控制

进程控制的主要功能是对进程进行管理,可以创建、撤销进程、实现进程状态转换等,用原语实现
进程的组成、控制及通信
之前说过原语具有原子性,进程进行状态转换就需要原语进行操作。假设一个就绪队列的进程CPU时间片到了,那么负责进程控制的内核要做两件事:

  1. 将该进程的状态修改为执行态
  2. 将该进程从就绪队列放到执行队列

这两个操作必须是具有原子性,所以利用原语进行操作。

如何实现原语的原子性

使用关中断指令开中断指令这两个特权指令实现。

正常情况下CPU每执行一条指令都要检查中断信号,而执行了关中断指令后,CPU就不检查了,直接执行后面的语句,直到执行完开中断指令才恢复检查中断信号。

进程控制相关的原语
进程的组成、控制及通信
进程的组成、控制及通信
进程的组成、控制及通信

进程通信

进程是分配系统资源的基本单位,各个进程的内存空间都是私有的,为了保证安全,不允许一个进程直接访问另一个进程的内存空间。但是某些情况下又需要用到另一个进程内存空间中的数据,这时候就需要某些方法去实现线程通信。

  • 共享内存(最快,但需要信号量等工具去控制进程同步)
    两个进程对共享空间的访问是互斥的,操作系统只负责提供共享空间和同步互斥工具(如P、V操作)

    • 基于数据结构的共享

      在共享空间利用数组之类的数据结构去存储共享数据,但这种方式速度慢、限制多,是一种低级通信方式

    • 基于存储区的共享

      在内存中划分一块共享存储区,数据的形式、存储位置都由进程控制,而不是操作系统,这种共享方式速度更快,是一种高级通信方式

  • 消息队列
    数据交换以格式化消息为单位,通过操作系统提供的发送消息接收消息两个原语进行数据交换。所有消息由消息头(类似报文,包括发送进程ID、接收进程ID、消息类型、消息长度等格式化信息)和消息体组成,消息由发送消息原语直接挂到接收进程的消息缓冲队列中,由接收消息原语负责取消息。

    • 消息队列可以独立于读写进程存在,从而避免了FIFO中为了同步管道的打开和关闭可能产生的困难;
    • 避免了FIFO的同步阻塞问题,不需要进程自己提供同步方法;
    • 读进程可以根据消息类型有选择地接收消息,而不像FIFO那样只能默认地接收.
  • 管道通信

    • 匿名管道

      具有以下限制:

      • 只支持半双工通信(单向交替传输);
      • 无名管道只能用于父子进程或兄弟进程之间,必须用于具有亲缘关系的进程间的通信。
    • 命名管道(FIFO)

      • 命名管道是FIFO文件,存在于文件系统中,可以通过文件路径名来指出。
      • 命名管道可以在不具有亲缘关系的进程间进行通信。
  • 套接字

    用于实现不同机器间的进程通信

相关文章:

  • 2021-08-21
  • 2022-12-23
  • 2022-12-23
  • 2021-07-09
  • 2022-01-26
猜你喜欢
  • 2021-08-05
  • 2021-12-25
  • 2021-08-08
  • 2022-12-23
  • 2022-02-20
  • 2021-08-16
  • 2021-07-19
相关资源
相似解决方案