进程管理
进程概念
进程的基本概念
程序运行在并发环境中的问题:
- 运行过程不确定
- 结果不可再现(程序运行被其它程序打扰)
- 解决方案:对运行过程施加相互制约
新的概念:进程
- 描述和管理运行程序的“运行过程”–进程
进程定义
- 进程是程序在某个数据集合上的一次运行活动
- 数据集合:软/硬件环境,多个进程共存/共享的环境
进程的特征
- 动态性:进程是程序的一次执行过程,动态产生/消亡
- 并发性:进程同其他进程一起向前推进
- 异步性:进程按各自速度向前推进
- 独立性:进程是操作系统分配资源和调度CPU的单位
进程和程序的区别
- 动态与静态
- 进程是动态的:程序的一次执行过程
- 程序是静态的:一组指令的有序集合
- 暂存与长存
- 进程是暂停的:在内存驻留
- 程序是长存的:在介质上长期保存
- 程序和进程的对应
- 一个程序可能有多个进程
进程的类型
- 按使用资源的权限:
- 系统进程:指系统内核相关的进程
- 用户进程:运行于用户态的进程
- 按对CPU的依赖性:
- 偏CPU进程:计算型进程
- 偏I/O进程:侧重于I/O的进程
进程状态
运行状态(Running)
- 进程已经占有CPU,在CPU上运行。
就绪状态(Ready)
- 具备运行条件但由于无CPU,暂时不能运行
阻塞状态(Block)(等待Wait状态)
- 因为等待某项服务完成或信号不能运行的状态.阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。
- 如等待:系统调用,I/O操作,合作进程信号…
进程状态的变迁
- 进程的状态可以依据一定的条件相互转化
深度阻塞一般是要请求文件、I/O等服务
调试一个进程往往让这个进程暂停成挂起态。
通常我们所认为的进程有五大状态,新建态,就绪态,阻塞态,运行态,退出态。
事实上还存在被挂起的进程。https://blog.csdn.net/freeelinux/article/details/53562592
僵死态 https://blog.csdn.net/diediexiaomi/article/details/76695747
进程的描述
进程控制块(Process Control Block,PCB)
- 描述进程状态、资源、和与相关进程关系的数据结构。
- PCB是进程的标志
- 创建进程时创建PCB;进程撤销后PCB同时撤销。
进程 = 程序+PCB
state表示进程的状态:运行、就绪、阻塞
counter:进程还可以运行tick数量,减至0后进入就绪。
priority:和进程优先级有关
signals:信号处理函数的入口
blocked:阻塞的信号
pid:和进程的标识有关
p_pptr:和进程家族关系有关
next_task:和进程链表、遍历进程相关
mm:和内存有关
fs:和文件系统有关
policy:和进程调度策略有关
和进程标识相关的成员变量
Linux进程的标识
- PID
- PPID:父进程ID
- PGID:进程组ID
进程的切换
进程的上下文
- Context,进程运行环境,CPU环境
进程切换过程
- 换入进程的上下文进入CPU(从栈上来)
- 换出进程的上下文离开CPU(到栈上去)
进程控制
进程控制概念
- 在进程生存全期间,对其全部行为的控制
- 四个典型的控制行为
- 创建进程
- 阻塞进程
- 撤销进程
- 唤醒进程
进程的创建
- 功能:创建一个具有指定标识(ID)的进程
- 参数:进程标识、优先级、进程起始地址、CPU初始状态、资源清单等
创建进程的过程
- 创建一个空白PCB
- 获得并赋予进程标识符ID
- 为进程分配空间
- 初始化PCB(默认值)
- 插入相应的进程队列(新进程插入就绪队列)
进程的撤销
- 功能
- 撤销一个指定的进程
- 收回进程所占有的资源,撤销该进程的PCB
进程撤销的时机/事件
- 正常结束
- 异常结束
- 外界干预
进程撤销的参数
- ID号
进程撤销的实现
- 在PCB队列中检索出该PCB
- 获取该进程的状态
-
若该进程处在运行态,立即终止该进程
递归:检查是否有子进程,先“撤销”子进程
- 释放进程占有的资源
- 将进程从PCB队列中移除
进程阻塞
- 功能:停止进程的运行,变为阻塞
阻塞的时机/事件
- 请求系统服务:由于某种原因,OS不能立即满足进程的要求
- 启动某种操作:进程启动某操作,阻塞等待该操作完成
- 新数据尚未到达:A进程要获得B进程的中间结果,A进程等待
- 无新工作可作:进程完成任务后,自我阻塞,等待新任务到达
参数
- 阻塞原因
- 不同原因构建有不同的阻塞队列
进程阻塞的实现
- 停止运行
- 将PCB“运行态”改“阻塞态”
- 插入相应原因的阻塞队列
- 转调度程序
进程唤醒
- 功能:唤醒处于阻塞队列当中的某个进程
引起唤醒的时机/事件
- 系统服务由不满足到满足
- I/O完成
- 新数据到达
- 进程提出新请求(服务)
参数
- 被唤醒进程的标识
进程控制原语
- 由若干指令构成的具有特定功能的函数
- 具有原子性,其操作不可分割
进程控制原语
- 创建原语
- 撤销原语
- 阻塞原语
- 唤醒原语
进程创建原语
Windows进程控制
system("C:\\DrawRec.exe");
WinExec("C:\\DrawRec.exe",SW_SHOWMAXIMIZED);
ShellExecute(NULL,"open","C:\\DrawRect.exe",NULL,NULL,SW_SHOWNORMAL);
CreateProcess(NULL,"C:\\DrawRec.exe",NULL,NULL,FALSE,NULL,NULL,NULL,&si,&pi);
三个函数的原型:
它们在一定程度上都是对CreateProcess进行封装。
CreateProcess()创建进程:
- 创建进程内核对象,创建虚拟地址空间
- 装载EXE和/或DLL的代码和数据到地址空间中
- 创建主线程和线程内核对象
- 启动主线程,进入主函数(main)
Linux进程控制
复制后生成的子进程和父进程有相同的代码、数据和堆栈,只有进程id号和时间不一样。
子进程是父进程的复制,自然包括代码。
exec装入的是可执行程序,这意味着数据段、代码段、堆栈,全都被换掉了。当一个进程无事可做时,可以使用exec重生。
前5个都是对第六个函数的封装。
线程
线程的概念(Thread)
- 线程是可由CPU直接运行的实体。是程序内部的一个执行路径。当操作系统支持线程的时候,进程就不再是CPU调度的单位了,取而代之的是线程。
- 一个进程可以创建多个线程。
- 多个线程共享CPU可以实现并发运行。
线程是比进程更小的并发单位。
临界区和锁
临界区和临界资源的访问特点:
- 具有排他性
- 并发进程不能同时进入临界区
设计临界区访问机制的四个原则
- 忙则等待:当临界区忙时,其它进程必须在临界区外等待
- 空闲让进:当无进程处于临界区时,任何有权进程可进入临界区
-
有限等待:进程进入临界区的请求应在有限的时间内得到满足。
思考:临界区的设置大些好还是小些好?
临界区不能随意扩大,否则导致其它进程等待更长的时间。临界区也不能太小,刚好是让进程之间互不干扰。
- 让权等待:等待进程放弃CPU。(让其它进程有机会得到CPU)
锁机制
同步和P-V操作
同步和互斥的概念
进程的互斥关系
多个进程由于共享了独占性资源,必须协调各进程对资源的存取顺序,确保没有任何两个或以上的进程同时进行存取操作。
具有互斥关系的进程都和临界资源共享有关!
资源:临界资源
存取操作区域:临界区
进程的同步关系
若干合作进程为了完成一个共同的任务,需要相互协调运行步伐:
一个进程开始某个操作之前必须要求另一个进程已经完成某个操作,否则前面的进程只能等待。
P-V操作概念
信号灯的概念
P-V操作的定义
P-V操作解决互斥问题
实质是实现对临界区的互斥访问:允许最多一个进程处于临界区
应用过程:
- 进入临界区之前先执行P操作(可能阻塞当前进程)
- 离开临界区之后再执行V操作(可能唤醒某个进程)
S的初值要设置的合理。
P-V操作实现进程同步
同步机制实质:
- 运行条件不满足时,能让进程暂停
- 运行条件满足时,能让进程立即继续
P-V操作应用于进程同步的基本思路:
- 暂停当前进程:在关键操作之前进行P操作
- 必要时可暂停
- 继续进程:在关键操作之后执行V操作
- 必要时唤醒合作进程
- 定义有意义的信号量S,并设置合适的初值。
- 信号量S能明确地表示“运行条件”
分析:两者同步过程?何时阻塞,何时唤醒?S1、S2变化?
假定司机进程先运行。P(S1)操作使S1由0变为-1。-1小于0,阻塞。
售票员进程运行。运行完关门之后,V(S1)操作使S1由-1变为0,并唤醒司机进程。
司机进程和售票员进程并发(行驶和售票)。
售票员进程试图开门,P(S2)操作使S2由0变为-1,阻塞。
司机进程停车,V(S2)操作使S2由-1变为0,并唤醒售票员进程。
售票员进程开门。
经典同步问题
Windows和Linux同步机制
Windows进程同步机制
synchapi.h
- 临界区(锁):CRITICAL_SECTION
- 互斥量(Mutex)(锁):HANDLE
- 信号量(Semaphore):HANDLE
- 事件(Event):HANDLE
- 等待操作:WaitForSingleObject和WaitForMultipleObjects
临界区(CRITICAL_SECTION)
- 在进程内使用,保证仅一个线程可以申请到该对象。
- 临界区内是临界资源的访问 WaitForSingleObject:等待上边的三个线程结束之后才会返回,然后执行printf
程序运行多次发现,不仅结果不是240,而且每次结果都不一样(都比240要小)?
分析代码发现:
int iCopy = nSum;
nSum = iCopy + i;
上面这两行代码是临界区,其中nSum是临界资源(全局变量),由三个线程共享。
互斥量(Mutex)
- 保证只有一个线程或进程可以申请到该对象。
- 可以跨进程使用
- 可以有名称
- 互斥量比临界区要耗费更多资源,速度慢
信号量(Semaphore)
信号量机制和临界区机制、互斥量机制有很大的不同。
- 允许指定数目的多个线程/进程访问"临界区"(不是上边的CRITICAL_SECTION,只是概念上的临界区)
- 一种资源计数器,用于限制并发线程的数量
- 初始值可设置为N,表示N个进程/线程并发访问 信号量是具有状态的对象。
事件
Linux同步机制
Linux父子进程同步
main(){
pid_t pid,pid_1,pid_2;
pid = fork();
if(pid == 0){ //子进程
pid_1 = getpid();
printf("pid_1 = %d \n",pid_1);
sleep(10);
}
if(pid > 0){ //父进程
pid_2 = wait(NULL);
printf("pid_2 = %d \n",pid_2);
}
exit(0);
}
程序运行流程?pid_1和pid_2的值?
父进程阻塞(wait),子进程执行打印子进程pid。子进程执行完,wait返回,返回值为子进程pid,父进程继续运行,打印子进程pid。然后退出进程。
pid_1和pid_2都是子进程pid。
上面的变量i在子进程里被修改为2,并打印。
在父进程直接打印i。
父进程上来就休眠,所以子进程先运行,打印2,然后父进程休眠完打印1。
对于普通变量,父进程和子进程各自操作变量副本,互不影响。
父进程先写A,然后子进程写B,父进程写C。
进程通信
匿名管道通信(windows)
任务:把一个CMD控制台程序改成窗口程序