2.1 进程介绍
伪并行(pseudoparallelism)单CPU一次只能处理一个程序,不断在程序中切换,速度很快,给人一种并行的错觉。
多处理机(multiprocessor)真正的硬件并行。
2.1.1 进程模型
在该模型中,计算机上的所有可行的程序,包括操作系统,被组织成若干顺序进程,简称
进程(process)。一个进程就是一个正在执行的程序,包括程序计数器、寄存器和变量当前值。
伪并行情况下的快速切换称为
多道程序设计(multiprogramming)。
进程和程序之间的区别:程序是静态的,是一种用适当形式描述的算法;进程是动态的,指的是执行程序的一系列动作的总和。
进程模型的关键思想:一个进程是某种类型的一个活动,有程序、输入、输出和状态。单个处理机被若干进程共享,使用某种调度算法决定何时停止一个进程的工作,并转而为另一个进程提供服务。
2.1.2 进程的创建
进程的创建有四个原因:
- 系统初始化:操作系统启动时通常会创建若干进程,由这些初始的进程再创建出其他进程,一些是前台进程,负责与用户进行交互,另一些是后台进程,称为守护进程(daemon)
- 正在运行的一个进程执行了创建进程的系统调用。从技术上看,其他三个情形都能归于此类。
- 用户请求创建一个新进程。
- 批处理作业的初始化。仅在大型机批处理系统中应用。
在MINIX3系统中,只有一个系统调用创建进程:fork。用于创建一个与调用进程相同的副本。就是说,所创建的进程和源进程拥有相同的内存映像、环境
字符串和打开文件。子进程调用execve或exec族的函数使之修改内存映像并运行新程序。之所以用两个步骤来创建进程,是为了在fork和exec函
数之间,完成对标准输入文件、标准输出文件和标准出错文件重定向等文件描述符操作。
2.1.3 进程的终止
进程的终止有几下几个原因:
- 正常退出(自愿):完成工作并正常退出,调用exit。
- 出错退出(自愿):进程发现严重错误,如操作文件但文件不存在。
- 严重错误(非自愿):由于进程引起错误,如执行非法指令、引用不存在的内存等,通常是程序的错误。
- 被其他进程杀死(非自愿):其他有权限的进程调用kill“杀死”进程。
2.1.4 进程的层次结构
进程创建一个进程之后,父进程和子进程会存在某种联系保持关联。一个进程只有一个父进程(子进程数目不限)。
在MINIX3中,进程和他的所有子进程及其后裔共同组成一个进程组。如果一个信号发送给一个进程组,进程组中的每个进程都可以捕获到该信号、忽略该信号或被信号杀死。
使用进程树的例子:MINIX3的启动初始化过程,在引导映像中有两个特殊进程:再生服务器(reincarnation
server)和init。再生服务器进程负责启动和重启驱动(drivers)和服务器(server),初始时处于阻塞态,等待init通知它启动什
么。
init进程执行/etc/rc脚本,想再生服务器进程发送命令启动引导镜像中不存在的驱动和服务器。
init完成上述工作后,读取/etc/ttytab查看存在的终端(或者虚拟终端)。init进程为每个终端fork一个getty进程,显示登录提示
符,等待输入。输入名字后,getty以该名字为参数exec一个login进程,若登录成功,login进程将运行(而不是再次fork)用户的
shell进程。因此,shell是init的一个子进程。
2.1.5 进程的状态
进程一共有三种状态:
- 运行态(Running,在该时刻实际占用处理机)
- 就绪态(Ready,可运行,因为其他进程正在占用处理机,因此暂时挂起)
- 阻塞态(Blocked,除非某种外部事件发生,否则不能运行)
下图显示了进程的状态之间的相互转换:
2.1.6 进程的实现
为了实现进程模型,操作系统维持一张进程表(process
table),每一个进程占其中一个表项(进程控制块)。表项记录了进程的状态、程序计数器、栈指针、内存分配状况、打开文件状态、统计和调度信息、定时
器和其他信号,以及进程由运行态到就绪态切换时所必须保存的其他信息。
MINIX3调度器工作方式:
每类I/O设备都有一个靠近
内存底部的位置,称为
中断向量,包含中断服务器的入口地址。当一个中断发生时,
中
断硬件将当前运行的进程的程序计数器、程序状态字以及可能的一个或多个寄存器的值压入(当前)堆栈,计算机随即跳转到中断向量指向的位置,上面的工作是由
硬件完成的。下面是软件的任务,中断服务过程的工作从把当前进程全部寄存器值存入进程表项开始。当前进程号和一个指向其表项的指针被存入全局变量。
随后,将中断存入的那部分信息从堆栈中删除,并将栈指针指向一个被进程处理程序(process handler)所使用的临时堆栈。保
存寄存器值,设置栈指针等操作无法用C语言描述,所以用一个短小的汇编程序例程完成。该例程结束后,调用一个C过程来完成特定中断类型剩下的工作。(蓝线
部分不是特别明白,个人的理解是这样的:中断服务将硬件中断压栈的那些信息,包括各种寄存器的值存入进程表中,然后可以从堆栈中删除,就是说硬件中断不能
直接操作进程表,需要堆栈来暂时存储相关信息,然后将栈指针指向进程处理程序使用的临时堆栈是指软件层面调用进程处理程序。不知道对不对。。。)
一个中断发生后作为操作系统最底层的调度器的工作步骤:
- 硬件压栈程序计数器
- 硬件按中断向量下载新的程序计数器
- 汇编语言程序存储寄存器值
- 汇编语言程序设置新的栈
- C语言中断服务例程运行
- 消息传递代码对等待的就绪任务进行标识
- 调度器决定那个进程是下一个将运行的进程
- C程序段返回汇编代码
- 汇编程序开始运行当前进程
2.1.7 线程
在相同的地址空间中有多个控制流并行地运行,仿佛是单独的进程一样(它们共享相同的地址空间)。这些控制流通常称为线程(thread),有时也成为轻量进程(lightweight process)。
可以把进程视为一个资源的集合,而线程就是控制流。进程用来集合资源,而线程才是CPU真正调度的实体。为了实现线程,同进程一样,也存在一张县城表,每
个线程占其中一项。线程的信息包括程序计数器(用于挂起和恢复)、寄存器值(挂起时要记录)和状态(同进程一样的三种)。
线程管理的位置--用户空间还是内核
- 用户空间:线程切换速度远远高于内核调用。
- 内核:若放在用户空间,一个线程阻塞,真个进程就必须阻塞。
线程带来的问题:
- fork的系统调用:创建新进程时,子线程是否继承父进程所拥有的线程?
- 共享数据结构:两线程操作同一文件会引起冲突。
- 错误报告:一个线程设置了errno,另一个线程在读取errno前,第三个进程执行系统调用并清除了errno。
- 信号(signal):有些信号逻辑上是针对线程的,有些不是。
- 堆栈管理:进程有多个线程,意味着有多个堆栈。若内核感知不到线程的堆栈,堆栈发生故障时无法进行处理。
阅读(777) | 评论(0) | 转发(0) |