文章目录
1 进程
1.1定义
- 进程定义:一个就有一定独立功能的程序在一个数据集合上的一次动态执行过程。
1.2组成
- 组成:一个程序的所有状态信息
- 程序代码
- 数据
- 程序计数器,指示下一条指令位置
- 一组通用寄存器当前值、堆和栈
- 一组系统资源
1.3进程和程序关系
关系:
- 程序使产生进程的基础
- 程序每次执行构成不同的进程
- 进程使程序功能的体现
- 通过多次执行,一个程序可以对应多个进程:通过调用关系,一个进程可包含多个程序。
区别:(菜谱和做菜的过程)
- 进程是动态的,程序是静态的:程序是有序代码的集合,进程是程序的执行。进程分为核心态和用户态,内核态比如读取文件,此时需要与硬盘通信,所以要进入内核态
进程是暂时的,程序是永久的。
进程和程序组成不同:进程包括程序数据和进程控制块(进程状态信息)
菜谱:程序、厨师:CPU、蔬菜:数据、做菜过程:进程
1.4进程特点
特点:
动态性:动态创建结束进程
并发性:进程可以独立调度并占用处理机运行
独立性:不同进程工作不互相影响
制约性:访问共享数据、资源或进程间同步而产生制约
1.5 进程控制结构
程序 = 算法+数据集结构
描述进程的数据结构:进程控制块(Process Control Block,PCB)
操作系统为每个进程都维护了一个PCB,用来保存与该进程有关的各种状态信息,是进程存在的唯一标志。
进程控制块:操作系统控制进程运行所用的信息集合,实现进程创建、回收以及运行时的组织管理。
进程控制块组成
- 进程标识信息:
- 本进程的标识、产生者标识(父进程标识)、用户标识;
- 处理及状态信息保存区:
用户可见寄存器,用户程序可以使用的数据、地址寄存器等;
控制和状态寄存器:程序计数器(PC)/程序状态字(PSW)
栈指针:过程调用、系统调用、中断处理和返回时需要 - 进程控制信息:
调度和状态信息:进程的管理控制
进程间通信信息:通信信息
储存管理信息:内存管理
进程所用资源:文件管理
有关数据结构连接信息:进程与进程之间的关系
进程控制块组织方式:
- 链表:进程需要动态插入删除
- 索引表:
1.6进程生命期
1.6.1进程生命期:
- 进程创建、运行、等待、唤醒、结束
进程创建:
- 引起进程创建3个事件:
1.系统初始化
2.用户请求创建一个新进程
3.正在运行的进程执行了创建进程的系统调用
进程运行:
- 内核选择一个就绪进程、占用CPU并执行。(调度算法)
进程等待:
- 进程等待
1.请求等待系统服务
2.启动某种操作,无法马上完成
3.需要的数据没有到达(读文件)
- 进程只能自己阻塞自己,进程自身知道何时需要等待
进程唤醒:
1.被阻塞资源可以满足
2.等待事件到达
3.将该进程PCB插入到就绪队列
- 进程唤醒需要被其他进程唤醒
进程结束:
1.正常退出(自愿)
2.错误退出(自愿)
3.致命错误(强制)
4.被其他进程杀死(强制)
1.6.2进程变化状态模型
进程三种基本状态:进程在结束前仅处于三种状态之一
- 运行态(Run):正在处理机上运行
- 就绪态(Ready):等待运行
- 等待态(阻塞态)(Block):暂停运行
进程其他状态: - 创建状态(New):正在被创建,就绪态之前
- 结束状态(Exit):进程结束或强制结束,进程消失的状态
1.6.3进程挂起
进程挂起状态:进程没有占用内存空间。此时进程映像在磁盘上。
- 阻塞挂起状态:进程在外存等待某事件出现
- 就绪挂起状态:进程在外存,进入内存即可运行
内存挂起状态转换:
- 阻塞到阻塞挂起
- 就绪到就绪挂起:高优先级阻塞和低优先级就绪
- 运行到就绪挂起:抢先式分时系统高优先级阻塞挂起事件进入就绪挂起,运行程序转到就绪挂起状态
外存挂起状态转换:
- 阻塞挂起到就绪挂起
解挂/激活
- 阻塞挂起到阻塞
- 就绪挂起到就绪
1.6.4状态管理
状态队列:
- 维护一组队列,表示系统当中所有进程的当前状态
- 不同状态分别用不同的队列表示
- 每个进程PCB根据它的状态加入到相应的队列当中,当一个进程状态发生变化,PCB从一个状态队列中脱离出来,加入另一个队列。
2线程
2.1 概念
-线程: 进程中的一条执行流程。线程 = 进程-共享资源。
进程用来管理资源,线程用来控制运行。
- 资源组合的角度:进程把一组相关资源组合起来,构成了一个资源平台包括地址空间(代码段、数据段)、打开的文件等各种资源;
- 从运行的角度:代码在这个资源平台上的一条执行流程。
- 线程有自己的线程控制块(TCB),线程共享内存空间。
优点:
- 一个进程中可以同时存在多个线程,各线程之间可以并发执行,个线程之间可以共享地址空间和文件等资源。
缺点:
- 一个线程崩溃会导致所属进程所有线程崩溃
2.2进程与线程比较
- 进程:资源分配单位,线程:CPU调度单位
- 进程具有一个完整资源平台,线程只独享必不可少的资源,如寄存器和栈。线程共享地址空间和文件资源。
- 线程具有程序运行的基本状态以及转换。
- 线程能减少并发执行时间和空间开销:线程创建和终止时间比进程短,统一进程内线程切换时间比进程段,线程不需要内核通信。
2.3线程实现
- 用户线程
- 用户线程库函数完成管理,操作系统只能看到线程所属进程,不能直接参与线程调度
- 缺点:阻塞性系统调用实现,一个线程发起,整个进程等待,此时整个进程都阻塞。多线程执行时,每个线程得到的时间片较少,执行较慢。
- 内核线程
- 操作系统完成管理,操作系统内核中实现一种线程机制,由操作系统内核函数/系统调用来完成线程的创建终止和管理。用内核维护PCB和TCB。某个内核线程发起的系统调用被阻塞,不会影响其他内核线程运行。时间片分配给线程,多线程进程获得更多CPU事件。
- 轻量级进程
内核支持的用户线程。一个进程可以有一个或多个轻量级进程,每个量级进程由一个单独的内核线程在支持。
- 用户线程与内核线程关系
多用户对一内核
一对一
多对多
3进程控制
3.1上下文控制
上下文切换:停止当前运行进程,调度其他进程。进程切换需要关注寄存器,以及被进程使用的寄存器(程序计数器、栈指针等)。
采用进程队列管理进程,操作系统将进程控制块放置在一个合适的队列里
- 就绪队列
- 等待I/O队列
- 僵尸队列
3.2进程创建、加载、等待和删除
Windows:
CreateProcess(filename) 创建时关闭所有在子进程里的文件描述符:
CreateProcess(filename,CLOSE_FD) 创建时改变子进程的环境
CreateProcess(filename,CLOSE_FD,new_envp)
Unix:fork/exec
- fork():把一个进程复制成两个进程
parent(old PID) child(new PID)
exec():用新程序重写当前进程 PID不变
fork()
- fork:创建一个继承的子进程
- 内部:复制父进程所有变量、内存和CPU寄存器(除PID)
- 返回:子进程返回0,父进程返回子进程标识符,子进程可以通过getpid获取PID
int pid = fork();
if(pid==0){ //子进程pid返回0
...
}
exec()
- exec:加载新程序取代当前运行程序
- 加载一个不同的程序,从_start位置开始执行
- 允许一个进程指定的参数数量(argc)和他字符串参数数组(argv)
- 子进程可以执行和父进程相同的进程,也可以通过exec执行不同的程序,执行exec时栈堆代码段都会被新程序覆盖。
int pid = fork();
if(pid==0){ //子进程pid返回0
exec_status = exec("calc",argc,argv0,argv1,...); //加载新进程代码
printf("childerror"); //上一行执行成功,不会执行这句话
}else if(pid>0){
printf("father: ")
...
child_status = wait(pid);
}
if(pid<0){//error}
vfork
- 有时内存复制时没作用的,而且占了大量时间。
- 1.使用vfork(只复制一小部分),然后调用exec
- 2.通过虚存管理,copy on write(COW)技术,使用时再复制,当写的时候把写的部分
区分开
wait
- wait():父进程等待子进程结束,回收子进程返回值,释放子进程空间
- 子进程向父进程返回一个值,父进程必须接受这个值并处理僵尸进程。
- 父进程等待子进程结束,当子进程调用exit时,解锁父进程,并通过exit传递得到的返回值做为wait调用的一个结果,如果没有子进程存活,wait立即返回。
- 如果有父进程的僵尸等待,wait立即返回其中一个值解除僵尸状态。
exit()
此时系统会:
将程序的结果作为一个参数,关闭所有打开的文件以及连接,释放内存和大部分支持进程操作的操作系统结构,检查父进程是否存活(如果存活,保留结果的值知道父进程需要,此时进程没有真正的死亡而是进入了僵尸状态,至父进程回收。没有存活释放所有数据结构至进程死亡),最后清理所有等待的僵尸进程。
如果父进程已经结束,root祖宗进程会定期扫描进程,并代理完成僵尸进程的回收。
exec运行可能在不同位置(加载需要一段时间),比如从running到阻塞态。