什么是进程
进程是由一组元素组成的实体,进程的两个基本元素为程度代码和代码相关的数据集
进程可以表示为一下元素:
- 标识符:跟这个进程相关的唯一的标识符,区别其他进程
- 状态:程序正在进行,则处于运行态
- 优先级:相对于其他进程的优先级
- 程序计数器:程序中即将被执行的下一条指令的地址
- 内存指针:程序代码和进程相关数据的指针,其他进程共享内存块的指针
- 上下文数据
- I/O状态信息
- 记账信息
上述信息放置在进程控制块中
进程状态
从处理器角度上看,它在指令序列中按照某种顺序执行指令,这个顺序根据程序计数器寄存器中不断变化的值来指示,程序计数器可能指向不同进程中不同部分的程序代码
分派器:从一个进程切换到另一个进程
进程队列:队列中每一项都指向某个特定进程的指针,或者队列可以由数据块构成的链表组成,每个数据块表示一个进程
进程的派生:为一个进程的显式请求创建另一个进程
父进程:一个进程派生另一个进程
子进程:被派生的进程
进程终止的原因:
- 正常完成
- 超过运行时限
- 无可用内存
- 越界:访问不允许访问的内存单元
- 保护错误:使用不允许使用的资源和文件
- 算术错误
- 时间超出:等待时间超出
- I/O失败
- 无效指令
- 特权指令:使用为操作系统保留的指令
- 数据误用
- 操作员或者操作系统干涉
- 父进程终止
- 父进程请求
操作系统的控制结构
操作系统维护的四个不同类型的表:
内存,I/O,文件和进程
内存表用于跟踪内存和外存,内存的某些部分为操作系统保留,一部分留给进程
保存在外存的进程使用某种类型的虚拟内存或简单的交换机制
内存表:
- 分配给进程的内存
- 分配给进程的外存
- 内存块或虚拟内存块的任何保护属性
- 管理虚拟内存所需要的任何信息
I/O表:
管理计算机系统的I/O设备和通道
如果正在进行I/O操作,操作系统需要知道I/O操作的状态和作为I/O传送的源和目标单元
文件表
进程表
进程位置
进程映像:程序,数据,栈和属性的集合称做进程映像
进程的生命周期:
进程描述符:
进程的优先级,在CPU上运行还是因某个事件被阻塞,需要分配什么样的地址空间,允许访问哪个文件
进程的状态:
可运行状态,可中断的等待状态,不可中断的等待状态,暂停状态,僵死状态
标识一个进程:
能够被独立调度的每个执行上下文都必须有自己的进程描述符
进程与进程描述符具有严格的一一对应的关系,这使得32位的进程描述符地址标识进程成为方便
进程标识符processID,PID存放在进程描述符的pid字段中,PID被顺序编号,PID有上限(32767),达到上限则循环使用已闲置的小pid号
同一组的线程有共同的PID,一个线程组中的所有线程使用和该线程组的领头线程相同的PID
进程描述符处理
进程描述符存放在动态内存中,而不是放在永久分配给内核的内存区
单独为进程分配的存储区域内:
(1)内核态的进程堆栈
(2)线程描述符
这块存储区域的大小为两个页框(8192)
esp为CPU栈指针,存放栈顶单元地址,数据写入堆栈esp减小,thread_info是52字节,内核栈能扩展到8140字节
内核从寄存器esp中得到当前CPU上运行的thread_info结构的地址,取得thread_info地址后调用current宏来取得进程描述符的地址
current->pid返回当前的CPU上执行的进程的PID
进程链表:
将所有的进程描述符链接起来,task_struct结构包含list_head类型的tasks字段,prev和next指向前面和后面的task_struct元素
进程链表的头:init_task,0进程
TASK_RUNNING状态的进程链表:
建立多个可运行进程链表,不同的进程优先权对应一个不同的链表
如果进程的优先级为k(范围为0-139),则run_list将该进程链入优先级为k的链表
多处理器系统中每个CPU都有自己的进程链表
运行队列的数据结构是进程描述符的链表
进程间的关系:
父子关系
一个进程创建多个子进程,子进程之间为兄弟关系
进程0和1由内核创建,进程1是所有进程的祖先
pidhash表及链表:
从PID导出进程描述符指针
进程P1向P2发送kill()系统调用,内核从PID导出进程描述符,从P2的进程描述符取出记录挂起信号的指针
4个散列表:进程描述符包含不同类型的PID字段
PIDTYPE_TGID类型散列表:
pid_hash数组第二个元素为散列表的地址,在散列表71项为起点形成的链表中,有两个PID号为246和4351的进程描述符
PID值存在pid结构的nr字段,pid结构在进程描述符中
pid_list存放链表的头,每个PID链表中指向前一个元素和后一个元素的指针也存放在每个链表元素的pid_list字段
如何组织进程:
等待队列是在事件上的条件等待,希望等待特定事件的进程把自己放在合适的等待队列,并放弃控制权
等待队列表示一组睡眠的进程,当某一个条件变为真时,由内核唤醒它们
等待队列由双向链实现,其元素包括指向进程描述符的指针,每个等待队列都一个等待队列头。
等待队列链表中的每个元素代表一个睡眠进程,该进程等待某个事件的发生,其描述符地址存放在task字段中,task_list字段中包含的是指针,由这个指针把一个元素链到等待相同事件的进程链表中
两种睡眠进程:
(1)互斥进程等待队列元素的flags字段为1,由内核有选择性唤醒
(2)非互斥进程flags字段为0,总是在事件发生时由内核唤醒
等待访问临界资源就是互斥进程的典型例子
进程资源限制:
每个进程都会有资源限制,限制指的是进程能使用的系统资源数量
资源限制存放在current->signal->rlim字段中,该字段为rlim结构的数组
rlim_cur字段是资源的当前资源的限制
rlim_cur表示正运行进程所占用的CPU时间的当前限制
rlim_max字段是资源限制所允许的最大值
进程切换
内核必须能够挂起正在CPU上运行的进程,并恢复以前进程的执行
硬件上下文:
每个进程拥有独立的进程空间,但是必须共享CPU寄存器,恢复一个进程之前,内核确保寄存器装入挂起进程时的值
进程可执行上下文的一个子集,可执行上下文包含进程执行所需要的所有信息
进程切换:保存prev硬件上下文,用next硬件上下文代替prev
linux2.6执行进程切换:
通过一组mov指令逐步执行切换,控制装入数据的合法性,可以检查ds和es段寄存器的合法性
进程切换只发生在内核态
在进程切换之前,用户态所有寄存器的内容都已保存在内核态堆栈上
thread字段:
每次进程切换,被替换的进程的硬件上下文必须保存在别处
每个进程描述符包含一个类型为thread_struct的thread字段,只要进程被切换出去内核就把其硬件的上下文保存在这个结构
执行进程切换:
进程切换分为两步:
(1)切换页全局目录以安装一个新的地址空间
(2)切换内核态堆栈和硬件上下文,硬件上下文提供内核执行新进程需要的所有信息
switch_to 宏
该宏有三个参数:prev,next,last
参数last:进程切换中,涉及到三个进程而不是两个进程切换
创建进程:
效率低:子进程复制父进程所拥有的资源,子进程需要拷贝父进程的整个地址空间
三种机制:
(1)写时复制允许父子进程读相同的物理页
(2)轻量级进程允许父子进程共享每个进程在内核的许多数据结构
(3)vfork()系统调用创建的进程能共享其父进程的内存地址空间,阻塞父进程执行,直到子进程退出或执行一个新的程序为止