操作系统 - 进程管理 1
进程的基本状态和转换
基本状态:
就绪状态: 外部条件已经满足, 但因为得不到 CPU 资源, 暂时无法运行
执行状态: 就绪状态得到 CPU 资源后进入执行状态
阻塞状态: 进程因为等待某件事情的发生而暂时不能执行, 也就是不具备外部执行条件.
进程转换:
进程在被挂起 (suspend) 之后进入静止状态, 静止状态也分为就绪和阻塞. 因此进程共有 5 种状态. 通过 Active 激活原语可将静止进程转换到活动进程, 同样通过 suspend 原语可以将活动进程变为静止进程, 即为挂起.
Linux 进程的状态
Linux 在内核中通过 task_struct 结构来表示, 进程状态通过该结构的 state 成员来描述.
TASK_RUNNING 状态: 该状态包括上述状态中运行和就绪状态, 靠是否正在占用 CPU 资源进行区分, Linux 中有 current 变量进行区分.
TASK_INTERRUPTIBLE 状态: 可中断的等待状态, Linux 把等待状态分为可中断的等待状态和不可中断的等待状态. 可中断的意思是该进程的等待状态可以通过信号唤醒进入就绪状态.
TASK_UNINTERRUPTIBLE 状态: 不可中断的等待状态.
TASK_STOPPED 状态: 一般由运行状态转换而来, 等待某种特殊的处理.
进程组成
程序: 表示进程需要完成的功能
数据集合: 表示进程需要处理的数据
PCB(Process Control Block):PCB 是用来描述进程当前的状态和本身的特性的数据结构. 包含了一个进程的描述信息, 控制信息和资源信息. PCB 最早被创建, 在进程完成其功能才被释放. PCB 大致有四个部分: 进程标识符, 处理机状态, 进程调度信息和进程控制信息.
进程标识符: 唯一地标识一个进程
进程内部标识符: 操作系统为每个进程赋予的唯一的数字表示符
外部标识符: 由创建者提供, 由字母, 数字组成.
处理机状态: 由处理机的各种寄存器的内容组成. 当处理机被中断时, PCB 要保留寄存器中的这些信息, 以便在进程重新执行时能够从断点处继续执行. 寄存器包括: 通用寄存器, 指令计数器, 程序状态字 PSW 和用户栈指针.
进程调度信息: 与进程调度和进程对换有关的信息. 包括进程状态, 进程优先级, 进程调度有关的信息和事件或阻塞原因(指进程由执行状态转变为阻塞状态所需要等待的发生的事件).
进程控制信息:
程序和数据的地址: 进程的程序和所需的数据所在的地址. 程序在运行时可以从 PCB 中获得所需的数据的地址.
进程同步和通信机制
资源清单: 一张除了 CPU 资源外, 包含了进程所需的全部资源以及进程已经获取到的资源的清单. 进程可以根据该清单确定是否已经满足外部条件进入就绪状态.
链接指针: 本进程 PCB 所在队列的下一个进程 PCB 的首地址.
PCB 的组织方式
线性方式: 系统预先确定整个系统同时存在的最大线程数目和静态分配空间. 限定了同时存在的进程的最大数目, 同时在进行进程调度时需要扫描整张表, 效率比较低.
链接方式: 按照进程的不同状态将其放在不同的队列中. 在单 CPU 的情况下, 运行状态的队列只有一个. 其他的状态的队列可以有多个, 按照不同的划分. 对于就绪状态的队列, CPU 调度程序总是把第一个 PCB 从该队列拿下投入运行队列. 而新加入就绪队列的 PCB 按照某种调度算法进行插入. 阻塞队列有多个, 对应不同的阻塞原因. 正在运行的进程由于某种原因进入阻塞状态时就会进入阻塞队列. Linux 就是应用这种 PCB 的组织模式.
索引方式: 建立几张索引表, 如就绪索引表, 阻塞索引表, 表中记录了某个 PCB 在 PCB 表中的地址.
进程控制
进程控制即系统使用一个具有特定功能的程序段来创建, 撤销进程以及完成进程各状态间的转换.
进程的家族关系:
进程可利用系统调用功能来创建新进程, 创建者称为父进程, 被创建者称为子进程. 子进程也可创建进程, 从而形成一棵进程树.
子进程的许多功能都是从父进程继承而来, 包括: 用户标识符, 环境变量, 打开文件, 文件系统的当前目录, 已经连接的共享存储区和信号处理例程入口表等.
还有一种进程在系统启动时被创立, 在系统停止时终止, 有及其重要的地位. 如 UNIX 系统的 0^# 进程和 1^# 进程. 0^# 进程是系统的调度和对换进程, 1^# 进程是创建进程, 是所有进程的祖先进程.
Linux 系统由 init()函数创建系统的第一个进程 init, 标识符为 1, 其完成一些初始化任务如打开系统控制台, 安装根文件系统, 启动系统的守护进程, 执行系统的初始化程序等. init 进程使用 ect/inittab 作为脚本文件创建新进程.
进程的创建与终止
要运行程序必须要创建一个进程, 导致进程创建的事件有用户登录, 作业调度和为用户提供服务等.
创建进程的步骤:
申请空白 PCB. 为新进程获得一个唯一的进程标识符, 并在 PCB 集合中申请一块空白的 PCB.
为进程分配资源. 包括新进程的程序, 数据及用户栈所需的内存空间. 此时, 系统必须知道新进程所需的内存大小.
初始化进程控制块.
将新进程的 PCB 插入就绪状态队列.
终止进程:
当系统发生了要求进程终止的事件, 操作系统就调用终止进程的原语, 终止进程.
根据进程标识符从 PCB 队列中检索出该进程的 PCB, 读取其状态.
若处于执行状态, 则立即停止执行, 停止后重新进行进程调度.
检查该进程是否有子进程, 若有, 子进程全部终止.
释放终止进程的资源, 归还给父进程或系统.
将该进程从 PCB 队列中移除.
进程的阻塞与唤醒.(比较简单, 省略)
Linux 系统调用
fork()系统调用: Linux 利用 fork()系统调用创建一个新进程. 调用格式通常为: int pid = fork();
pid=0: 创建子进程成功, 表示从子进程返回, CPU 正在运行该子进程.
pid>0: 创建子进程成功, 表示从父进程返回, pid 为新创建子进程的标识符.
pid=-1: 创建子进程失败
exec()系统调用:
fork()系统调用创建的子进程和父进程执行的是同一段代码, 但是完成的是不同的工作. Linux 中 fork()创建子进程后, 可以由 exec()系统调用执行另一个程序.
调用格式为:
- int execl(path,arg0[,arg1,..],0)
- char *path,*arg0,*arg1..
exec 函数族的作用是根据文件名找到相应的可执行文件, 也就是在调用进程的内部执行一个可执行文件. 执行成功不会返回, 失败返回 - 1
exit()系统调用:
Linux 利用 exit 来实现进程的自我终止. 进程在调用 exit()并非马上消失, 而是进入了僵尸状态, 它已经释放了除 PCB 外的几乎所有内存.
wait()系统调用:
该调用将进程挂起, 直到接收到终止信号.
来源: http://www.bubuko.com/infodetail-3394601.html