为什么引入进程
多个程序并发执行可以有效地提高系统资源(特别是CPU)的利用率,同时当多个程序竞争CPU资源时进行任务轮换执行可以提高多用户的执行相应速度,此外计算机实际上支持多个程序的并发执行,但是现有的程序等概念无法准确描述程序动态轮换执行,因此需要一种同一的方法监视,管理以及控制处理器中不同程序的动态执行过程,由此引入了进程的概念。
什么是进程
进程没有准确的概念,但是可以从不同的角度去描述:一个正在执行的程序、计算机中正在运行的程序的一个实例、可以分配给cPU并且由CPU处理的一个实体、由一个顺序执行的代码段一个当前状态和一组相关系统资源所刻画的活动单元。
进程的特征
动态性
因创建产生,由调度而执行,没有资源而暂停、因结束或者撤销而消亡
并发性
多个程序并发执行,提高CPU的等资源的利用率
独立性
进程是一个可以独立运行的基本单位,也是系统可以分配资源和调度的基本单位。
异步性
各个进程独立运行,并以不可预知的速度向前推进。
交互性
一个进程在运行过程中可能和其他进程发生直接或者间接的相互作用
进程的描述
进程由多种状态,运行中至少由以下几种状态:
- 就绪:获得处理器以外的所有资源,一旦获得cpu资源就立即执行
- 运行
- 阻塞:正在运行的进程,因某些原因而暂停运行
可以增加描述:
- 新建状态
- 退出状态
- 挂起状态(阻塞挂起、就绪挂起【换入换出】)
进程的数据结构
对每一个进程,操作系统需要以下信息:
- 可执行代码
- 程序需要的数据
- 程序的上下文(PCB)
操作系统管理进程调度创建以下数据结构:
- 进程索引表
- 进程映像存储区域
- 进程链表队列
进程控制块PCB:用来描述和记录进程的动态变化的信息(线性的数据结构),该结构存储程序执行的上下文环境,系统为每一个进程都定义了一个进程控制块(PCB),当进程结束操作系统回收该PCB,系统根据PCB感知进程,PCB是进程存在的唯一标识,通过PCB系统可以访问到一个进程的所有信息。一般来说,PCB包括以下信息:进程标识符(PID)、进程当前的状态、进程队列中的指针(记录PCB链表下一个PCB结构的位置)、程序地址范围(开始地址和结束地址)、程序优先级、CPU现场保护、通信信息(与其他进程的交互信息)、PPID(父进程的PID)、占有资源的清单
进程操作
进程操作由系统内核完成,通过执行各种原语实现(原语的执行不可中断)
进程创建
由创建原语完成、被创建者为子进程、创建者为父进程,创建原语的主要功能是为被创建进程创建一个PCB。其过程如下:向系统申请PCB,根据父进程的参数初始化PCB,将其插入就绪队列,返回进程标识符
进程撤销
由撤销原语完成,系统将释放资源。(两种撤销策略:撤销指定PID的进程,撤销进程及其子孙进程)
撤销过程:从PCB集合中找到目标PCB,处理运行标识、设置调度表示(对另一种策略还将对其子孙进程进行处理)。将资源归还给父进程或者系统、撤销PCB。
进程阻塞和唤醒
阻塞原语:由执行状态->阻塞状态
唤醒原语:由阻塞状态->就绪状态
阻塞过程:首先处理相应进程的CPU现场,将进程插入等待队列、从就绪队列选择一个进程执行
注意
一个进程由执行状态转变为阻塞状态,是该进程自己调用阻塞原语去完成的
而进程由阻塞状态到就绪状态,是另一个发现者进程(或中断服务程序)调用唤醒原语实现的,一般这个发现者进程与被唤醒进程是合作的并发进程(通过使用信号量的P/V操作)
进程调度
为了管理进程和进程调度,操作系统将具有相同属性的进程保存到进程队列(如:就绪、挂起。。。)
进程调度发生的情况:因等待某些事而等待(如等待IO数据),时间片到了、出现了更高优先级的进程、任务完成自动结束
进程调度需要注意的问题:进程队列的迁移、下一个进程的选取、进程的切换。
下一个进程的选取
FIFO
优先级
进程切换
需要保存上下文,然后切换到另一个进程,注意上下文切换需要浪费CPU时间,同时需要保存与内存相关的信息(如段表、页表等信息)
进程间通信
系统中的进程有两种:独立进程,即不被其他进程影响也不会影响其他进程;协作进程,而进程间协作的核心就是共享信息即进程间通信,从而达到分工和协作。进程通信的两种方式如下:
共享内存
如shmget()和shmat()系统调用可以实现内存共享
消息传递
需要明确发送者和接收者,如send(P,message);receive(Q, message);
创建进程
进程是一个可运行的程序,linux上可用fork();创建子进程。
可执行文件的格式
ELFheader在文件开始处描述了整个文件的组织,Section提供了目标文件的各项信息(如指令、数据、符号表、重定位信息等),Program header table指出怎样创建进程映像,含有每个program header的入口,section header table包含每一个section的入口,给出名字、大小等信息。像bmp、exe等文件一样,ELF的文件头包含整个文件的控制结构。
加载到内存后程序状态
1、栈区(stack):由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap):由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
3、全局区(静态区):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。
4、文字常量区:常量字符串就是放在这里的。 程序结束后由系统释放。
5、程序代码区:存放函数体的二进制代码。
堆栈段:
1. 为函数内部的局部变量提供存储空间。
2. 进行函数调用时,存储“过程活动记录”。
3. 用作暂时存储区。如计算一个很长的算术表达式时,可以将部分计算结果压入堆栈。
数据段(静态存储区):
包括BSS段的数据段,BSS段存储未初始化的全局变量、静态变量。数据段存储经过初始化的全局和静态变量。
代码段:
又称为文本段。存储可执行文件的指令。
堆:
就像堆栈段能够根据需要自动增长一样,数据段也有一个对象,用于完成这项工作,这就是堆(heap)。堆区域用来动态分配的存储,也就是用 malloc 函数活的的内存。calloc和realloc和malloc类似。前者返回指针的之前把分配好的内存内容都清空为零。后者改变一个指针所指向的内存块的大小,可以扩大和缩小,他经常把内存拷贝到别的地方然后将新地址返回