- 操作系统会在任何时候暂停或继续一个程序的运行
程序A:a,c->a.exe | 程序B:b,c->b.exe |
---|---|
1)... | 1)... |
2)i=100; | 2)i=200; |
3)... | 3)... |
4)printf("A:i=%d,"i) | 4)printf("B:i=%d,"i) |
i为全局变量,A,B并发运行后:i的值不确定且不能重复
-
程序运行在并发环境中可能存在的问题
运行过程不确定
结果不可再现(程序运行被干扰)
需要对运行过程施加相互制约(进程) -
进程定义
描述和管理程序的运行过程
进程是程序在某个数据集合上的一次运行活动,数据集合指软/硬件环境,在这个环境中多个进程可共存。
任务管理器
-
进程的特征
动态性:进程是程序一次执行过程,动态产生,动态消亡
并发性:进程同其他进程一起向前推进
异步性:进程按各自速度向前推进
独立性:进程是系统分配资源和调度CPU的单位(线程) -
进程与程序的区别
动态与静态:进程是动态的(程序一次执行过程),程序是静态的(一组指令的有序集合)
暂存与长存:进程是暂存的(在内存驻留),程序是长存的(在介质上长期保存)
程序和进程的对应:一个程序可能有多个进程 -
进程的类型
按使用资源权限:系统进程(系统内核相关的进程),用户进程(运行于用户态的进程)
按对CPU的依赖性:偏CPU进程(计算型进程),偏I/O进程(侧重I/O的进程) -
进程的状态
名称 | 状态 |
---|---|
运行状态(Running) | 进程已经占有CPU,在CPU上运行 |
就绪状态(Ready) | 具备运行条件但由于无CPU,暂时不能运行 |
阻塞状态(Block)或等待状态(Wait) | 因为等待某项服务完成或信号而不能运行的状态,如等待:系统调用,I/O操作,合作进程信号 |
-
进程状态的变迁
进程的状态可根据一定条件相互转化
状态转变 | 原因 |
---|---|
就绪->运行 | 进程调度 |
运行->就绪 | 时间片耗尽,被抢占 |
运行->阻塞 | 请求服务,等待信号 |
阻塞->就绪 | 服务完成,信号来到 |
不存在 阻塞->运行 和 就绪->阻塞,阻塞必须通过就绪才能运行,就绪运行后才能进入阻塞。
更多状态如:新建状态和终止状态

- Linux进程的状态
名称 | 状态 |
---|---|
可运行态 | 就绪和运行 |
阻塞(等待)态 | 浅度阻塞(可中断),深度阻塞(不可中断,请求文件服务、I/O服务,系统服务) |
僵死态 | 进程终止执行,释放大部分资源 |
挂起态 | 进程被临时挂起,暂存 |

-
进程控制块(Process Control Block, PCB)
描述进程状态,资源和与相关进程关系的数据结构,PCB是进程的标志,创建进程时创建PCB,进程撤销后PCB也撤销
进程=程序+PCB -
基本PCB的数据结构
PCB的基本成员 | |
---|---|
name(ID) | 进程名称(标识符) |
status | 状态 |
next | 指向下一个PCB的指针 |
start_addr | 程序地址 |
priority | 优先级 |
cpu_status | 现场保护区(堆栈) |
comm_info | 进程通信 |
process_family | 家族 |
own_resource | 资源 |
- Linux的进程控制块PCB:task_struct
(1)进程状态
(2)调度信息
(3)标识符
(4)内部进程通信讯息
(5)链接信息
(6)时间和计时器
(7)文件系统
(8)虚拟内存信息
(9)处理器信息
和进程标识相关的成员变量,LINUX进程标识(PID,PPID父进程ID,PGID进程组ID),用户标识(UID用户ID,GID用户组ID)
-
进程的切换
进程上下文,context,进程运行环境,CPU环境
换入进程的上下文进入CPU(从栈上来)
换出进程的上下文离开CPU(到栈上去) -
进程控制
操作系统在进程生存全期间,对其全部行为的控制,主要包括创建进程,阻塞进程,撤销进程和唤醒进程 -
进程创建,创建一个具有指定标识(ID)的进程
参数:进程标识,优先级,进程起始地址,CPU初始状态,资源清单等
过程:创建一个空白PCB->获取并赋予进程标识符ID->为进程分配空间->初始化PCB(默认值)->插入相应的进程队列(新进程插入就绪队列)
//进程创建伪代码
Creat(Si,Mi,Pi) //CPU的状态,内存,优先级
{
p=Get_New_PCB(); //分配新的PCB
pid=Get_New_PID(); //分配新的进程PID
p->ID=pid; //设置进程PID
p->CPU_state=Si; //CPU的状态
p->Memory =Mi; //内存
p->Priority=Pi;//优先级
p->Status.Type="Ready"; //进程状态
p->Status.List=RL; 进程队列RL:Ready List
...............
Insert(RL,p); //把进程p插入就绪队列
Scheduler(); //调度程序
}
-
进程撤销,撤销一个指定的进程,收回进程所占有的资源,撤销该进程的PCB
时机/事件/原由:正常结束,异常结束,外界干预
参数:进程标识
进程撤销的实现:在PCB队列中检索出该PCB->获取该进程的状态->根据进程状态,处理进程(若该进程处于运行态,立即终止该进程)->释放进程占用资源->将进程从PCB队列中移除。Linux中存在父子进程,撤销进程,需要一并撤销子进程,及子进程的子进程(递归)。 -
进程阻塞,停止进程的执行,变为阻塞
时机/事件/原由:请求系统服务(由于某种原因,操作系统不能立即满足进程的要求),启动某种操作(进程启动某操作,阻塞等待该操作完成),新数据尚未到达(A进程要获得B进程的中间结果,A进程等待),无新工作可做(进程完成任务后,自我阻塞,等待新任务到来)
参数:阻塞原因,不同原因构建不同的阻塞队列
进程阻塞的实现:停止运行->将PCB“运行态”改“阻塞态”->插入相应原因的阻塞队列->转调用程序 -
进程唤醒,唤醒处于阻塞队列中的某个进程
时机/事件/原由:相对于进程阻塞,系统服务由不满足到满足,I/O完成,新数据到达,进程提出新请求(服务)
参数:被唤醒进程的标识 -
进程控制原语,是由若干指令构成的具有特定功能的函数,具有原子性,其操作不可分割,必须一气呵成,不能停止。
包括:创建原语,撤销原语,阻塞原语,唤醒原语。 -
Windows进程控制
编程创建:system、WinExec、ShellExecute、CreateProcess(最底层)
CreateProcess创建新进程:创建进程内核对象,创建虚拟地址空间->装载EXE和/或DLL的代码和数据到地址空间->创建主线程和线程内核对象->启动主线程,进入主函数
编程结束:ExitProcess(温和结束执行),TeriminateProcess(强制结束执行)。 -
Linux进程控制
编程创建:fork
新进程是当前进程的子进程,父进程是fork()的调用者,子进程是新创建的进程。
子进程是父进程的复制,具有相同的代码,相同的数据,相同的堆栈,子进程与父进程具有相同行为,差别在于进程标识不同,与时间相关的信息不同。
父子进程可以并发运行,并发运行从创建进程后执行,避免重复创建进程的结果。
int main(void)
{
pid_t pid
pid=fork();
if (pid == 0)
printf("Hello World!\n")
else if (pid > 0)
printf("How are you?\n")
}
在子进程中,pid = 0
在父进程中,pid > 0(等于子进程的ID)
出错,pid = -1
-
exec函数族,不由fork创建,是所有进程的祖先。
exec函数族:execl,execlp,execle,execv,elecvp,exece(最底层)
调用时装入一个指定的可执行程序运行,使得子进程具有和父进程完全不同的新功能
过程:根据文件名找到相应得可执行程序->将可执行程序得内容填入子进程得地址空间->进入新进程执行不再返回 -
线程(Thread)
当操作系统支持线程时,进程不再是CPU的调度单位,线程成为CPU的调度单位
线程是可由CPU直接运行的实体,一个进程可以创建多个线程,多个线程共享CPU可以实现并发运行,具有比进程更高的并发力度。
CreateThread,把一个函数创建为一个线程 -
单线程和多线程
单线程程序只有一个线程,即Windows程序省缺的主线程main
多线程程序含有至少两个线程,主线程和至少一个用户线程 -
线程技术典型适用场景
程序多个功能需要并发运行
提高窗口程序的交互性(前台交互主线程和后台计算子线程)
需要改善程序结构的地方
充分发挥多核CPU的性能 -
创建线程:CreateThread(Win32),AfxBeginThread(MFC),CreateRemoteThread(远程线程)pthread_create(Linux,pthread.h)
-
线程的麻烦:程序难以调试,并发过程难以控制,线程安全问题
-
进程不能一边画圆一边画方?
可以,不同程序窗口,不完全保障同时开始和完成。 -
交互子线程,计算主线程?
交互子线程需要小心处理交互顺序,计算主进程影响后续交互。 -
临界资源和临界区
临界资源:一次只允许一个进程独占访问(使用)的资源,如最前面代码A和B中共享变量i
临界区:进程中访问临界资源的程序段,包括写或读临界资源。
访问特点:具有排他性,并发进程不能同时进入临界区
访问机制涉及原则:
忙则等待(当临界区忙时,其他进程必须在临界区外等待)
空闲让进(当无进程处于临界区时,任何有权进程可进入临界区)
有限等待(进程进入临界区的请求应在有限时间内得到满足,临界区不易过大,但必须包含所有临界资源)
让权等待(等待进程放弃CPU,让其他进程有机会得到CPU) -
锁机制
基本原理:设置一个标志,标识临界资源“可用”还是“不可用”?1:0
上锁操作:进入临界区之前检查标志是否“可用”,若“不可用”,在临界区等待,若“可用”,访问临界资源并修改标志为“不可用”。上锁原语
开锁操作:离开临界区时将标志修改为“可用”状态,开锁原语 -
用锁机制访问临界区
步骤:初始化锁的状态S=1(可用)->进入临界区之前执行上锁Lock操作->离开临界区之后执行开锁unLock操作。
网友评论