可以把一个运行的程序看成一个进程。
进程包含了正在运行的一个程序的所有状态信息:程序的代码、程序处理的数据、程序计数器中的值(指示下一条将运行的指令)、一组通用的寄存器的当前值、一组系统资源(如打开的文件)。
1)动态性:可动态地创建、结束进程
2)并发性:进程可以被独立调度并占用处理机运行
3)独立性:不同进程的工作不相互影响 (页表 是 保证独立性的重要机制。)
4)制约性:因访问共享数据/资源或进程间同步而产生制约
进程控制块 PCB 是操作系统管理控制进程运行所用的信息集合,是进程存在的唯一标识,每个进程都在操作系统中有一个对应的PCB。进程终止的话,PCB 也应该随之被回收。
1)进程标识信息
本进程的标识、本进程的产生者标识(父进程标识);用户标识。
2)处理机状态信息保存区
保存进程的进行现场信息。
① 用户可见寄存器:用户程序可以使用的数据、地址等寄存器。
② 控制和状态寄存器,如 程序计数器 PC、程序状态字 PSW。
③ 栈指针。过程调用 / 系统调用 / 中断处理和返回时 需要用到它。
进程的生命周期管理:
不考虑生命结束的话,进程有三种基本状态:运行(Running ,当一个进程正在处理机上运行时)、就绪(Ready ,一个进程获得了除处理机之外的一切所需资源,一旦得到处理机即可运行 ) 和 等待(又称 阻塞 Blocked,一个进程等待某一时间而暂停运行时。如等待某资源;等待输入 / 输出完成)。
如果考虑创建态 和 结束态:
进程挂起 意味着 进程没有占用内存空间。进程挂起 Suspend 是指 把一个进程从内存转到外存中。 之前说虚拟内存的时候,运行的程序可能把一部分空间导到硬盘上,腾出更多的空间给需要的程序使用。”运行的程序“ 就是指进程。
挂起有两种:
1)等待 / 阻塞挂起状态(Blocked-suspend):进程在外存并等待某事件的出现。
2)就绪挂起状态(Ready-suspend): 进程在外存,但只要进入内存,即可运行。
由操作系统来维护一组队列,表示系统中所有进程的当前状态。不同队列表示不同状态,如就绪队列、阻塞队列、各种等待队列,根据进程状态不同,进程PCB加入相应队列,进程状态变化时,它所在的PCB会从一个队列换到另一个。由操作系统来维护一组队列,表示系统中所有进程的当前状态。
例子:
编写一个MP3播放软件。核心功能模块有三个:
① 从MP3音频文件当中读取数据
② 对数据进行解压缩
③ 把解压缩后的音频数据播放出来
单进程的实现方法:
main( )
{
while(true)
{
//IO 读文件
Read( );
// CPU 解压
Decompress( );
//播放
Play( );
}
}
Read( ) { … }
Decompress( ) { … }
Play( ) { … }
这样写的问题:播放出来的声音能否连贯?而且各个函数之间不是并发执行,影响资源的使用效率。
那就用多进程:
但是这样写又有了新的问题:进程之间如何通信,共享数据?而且系统开销较大:创建进程、进程结束、进程切换。
描述指令流执行状态。它是进程中的指令执行流的最小单元,是CPU调度的基本单位。
一个线程崩溃,会导致其所属进程的所有线程崩溃 (因为共享啦)
使用浏览器,可以用线程的方式实现,但是这时候发现,某一个网页崩溃,整个浏览器都崩溃了,所以现在的浏览器很多都是采取进程的方式实现,因为性能不再是瓶颈,而安全性更重要(比如 Chrome 就是用进程打开网页)。
1)进程是资源分配单位,线程是CPU调度单位
2)进程拥有一个完整的资源平台,而线程只独享指令流执行的必要资源,如寄存器和栈
3)线程具有就绪、等待和运行三种基本状态和状态间的转换关系
4)线程能减少并发执行的时间和空间开销
① 线程的创建时间比进程短(因为进程创建还需要创建其他管理信息等,而线程直接使用进程所属的进程的资源即可)
② 线程的终止时间比进程短
③ 同一进程内的线程切换时间比进程短(因为同一进程的线程具有同一个地址空间,即具有同一页表,而进程的切换还需要切换页表,切换页表的开销是比较大的,因为涉及到访问的地址不同、缓存信息不同、TLB信息等都需要重新加载。)
④ 由于同一进程的各线程间共享内存和文件资源,可不通过内核进行直接通信
① 用户线程:
操作系统看不到的线程,(操作系统只能看到整个进程的信息,但是看不到其中的线程;而如果进程被操作系统设置为等待状态,那么进程中的所有线程就不能执行了)在用户空间实现。
由一组用户级的线程库函数来完成线程的管理,包括线程的创建、终止、同步和调度等。
② 内核线程:
由操作系统管理起来的线程,操作系统能看到的;
在内核中实现由内核通过系统调用实现的线程机制,由内核完成线程的创建、终止和管理。
用户线程和内核线程的对应关系:
③轻量级进程:
在内核中实现,支持用户线程。
内核支持的用户线程。一个进程可有一个或多个轻量级进程,每个轻权进程由一个单独的内核线程来支持。(Solaris/Linux)
暂停当前运行进程,从运行状态变成其他状态,调度另一个进程从就绪状态变成运行状态。
哪些进程可以做切换?能够在 CPU 上执行的进程是放在就绪队列中的,是个链表。内核为每个进程维护了对应的进程控制块(PCB);内核将相同状态的进程的PCB放置在同一队列:
1)fork() 创建一个继承的子进程复制父进程的所有变量和内存;复制父进程的所有CPU寄存器(有一个寄存器例外)。 fork()执行过程对于子进程而言,是在调用时间对父进程地址空间的一次复制。
(2)parent (old PID), child (new PID)
(3)exec()用新程序来重写当前进程 / 让当前进程执行新的程序
PID 没有改变。
用 fork 和 exec 创建进程的示例:
// 创建子进程
int pid = fork();
if(pid == 0)
{
// 子进程在这里继续
// Do anything (unmap memory, close net connections…)
//系统调用 exec() 加载程序取代当前运行的程序
exec(“program”, argc, argv0, argv1, …);
//正常来说,执行了 exec 后,子进程的地址空间里的代码段被覆盖,接下来这句 printf...应该是被覆盖了的,不会执行到的
printf("Why would I execute?")
}
else
{
if(pid>0)
{
//父进程在这里继续
pringtf("Whose your daddy?");
...
//等待子进程结束wait,返回说明子进程结束
child_status=wait(pid);
}
else
//如果 pid<0,说明是失败的系统调用
pringtf("error occurred");
}
CreateProcess(filename, CLOSE_FD)
2)创建时改变子进程的环境
CreateProcess(filename, CLOSE_FD, new_envp)
为什么要有 wait()?父进程为什么需要等子进程结束呢?(子进程直接 exit 退出不就行了嘛?)问题在于,调用了 exit() 后,进程所需要的资源是否都会被回收到操作系统中呢?有一个资源很难回收——就是在操作系统内部 代表进程存在的 ”进程控制块“。当子进程执行 exit() 后,会通过操作系统通知父线程,如果这时父进程在执行 wait() ,那么父进程就可以帮助子进程完成资源的释放,主要是 PCB 资源。
当子进程执行过 exit() 且 父进程还没有执行完 wait() ,子进程并没有释放,也不属于就绪 / 等待 / 运行态,它属于 僵尸态?【要死还没有死】。
如果父进程先于子进程死亡,那子进程不会再有一个进程在 wait 它,那这个子进程的 PCB 该由谁回收呢? 最早的进程称为祖宗进程,祖宗进程会定期扫描进程控制块链表,代替僵尸状态的进程的父进程完成回收。
免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删