为了让大家了解、掌握进程的相关知识,这个不管是面试或者平常工作中都需要接触的重头戏。洪爵以问答的方式把常见的、易错的、大家想知道的问题做个收集 + 解答。
我们上篇文章讲到了进程的创建,那么有始就有终,没有真正永恒的物体。
进程的终止通常由以下条件引起:
1、正常退出(自愿)
2、出错退出(自愿)
3、严重错误(非自愿)
4、被其他进程杀死(非自愿)
当编译器对给定程序编译完成后,那么编译进程会执行一个系统调用,通知操作系统它的工作已经完成了,在UNIX中调用exit,在Windows调用ExitProcess,即可正常退出进程。
进程的第二种终止方式是进程发现了错误,比如说输入命令javac HelloWorld.java,但是HelloWorld.java文件并不存在,这个时候javac进程就会选择自愿退出。
进程终止的第三个原因是由进程引起的错误,比较常见的是程序中的错误所致。例如引用了不存在的内存、除零操作等等。
第四种方式是某个进程执行一个系统调用,通知操作系统杀死某个其他进程,在UNIX系统中,这个系统调用是kill,在Win32中对应的函数时TerminateProcess。
某些系统中,进程创建了另一个进程后,父进程和子进程会以某种形式保持联系。子进程自身也可以创建更多的进程(禁止套娃),组成进程的层次结构。需要注意的是,进程只有一个父进程,但是可以有[0,正无穷)个子进程。
在UNIX中,进程和它所有子进程及它的后裔共同组成一个进程组。那么UNIX在启动中是如何初始化自己?在UNIX中有一个称为init的特殊进程,在一开机就会被加载运行,并且会读入一个说明终端数量的文件,为每一个终端创建一个新进程,这些进程等待用户登录,如果其中某个用户登录成功了,该进程就会执行一个shell,等待接收命令,接收的这些命令可以启动更多的进程,以此类推,整个系统中,所有的进程都属于以init为根的一颗树。
但是对于windows系统来说,就没有进程层次的概念了,所有的进程地位相同。唯一类似进程层次的东西是父进程会得到一个令牌(句柄),这个句柄可以控制子进程,但是这个句柄可以传送给其他进程,这样就不存在进程层次。
在UNIX中就不能剥夺子继承的“继承权”。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jYU1VlPZ-1659980482586)(C:\Users\Knight\Downloads\未命名文件.png)]
1)运行态(该时刻进程实际占用CPU)
2)就绪态(可运行,但因为其他进程正在运行而暂时停止)
3)阻塞态(除非某种外部事件发生,否则进程不能运行)
前两种状态在逻辑上是类似的。处于这两种状态的进程都可以运行,对于第二种状态暂时没有CPU分配给它。第三种状态与前两种状态不同,处于该状态的进程不能运行,即使CPU空闲也不行。
在操作系统发现进程不能继续运行下去时,发生转换1,像UNIX,当一个进程从管道或设备文件(例如终端)读取数据时,如果没有有效的输入存在,则进程会被自动阻塞。
转换2和3时由进程调度程序引起的,系统认为一个运行进程占用处理器的时间已经过长了,决定让其他进程使用CPU时,会发生转换2。在系统已经让所有其他进程享有了它们应有的公平待遇而重新轮到第一个进程再次占用CPU运行时,会发生转换3。
当进程等待的一个外部时间发生时(如输入到达),则发生转换4。如果此时没有其他进程运行,则立即触发转换3,该进程就开始运行了。否则该进程就将处于就绪态,等待CPU空闲并且轮到它运行。
尽管每个进程是一个独立的实体,有自己的程序计数器和内部状态,但是进程之间经常需要相互作用,一个进程的输出结果可能是另一个进程的输入。
举个例子:
cat a.txt b.txt c.txt d.txt | grep abcd
在shell中输入以上命令后,第一个进程会运行cat,将4个文件连接并且输出,第二个进程运行grep,它从输入中选择所有包含的abcd的行。这时,可能发生这种情况:grep准备就绪可运行,但输入还没有完成,于是需要阻塞grep等到输入到来。
为了实现进程模型,操作系统会维护一个进程表(一个结构数组),每个进程占用一个进程表项(有的人称之为进程控制块)。该表项包含了进程状态的重要信息:包括程序计数器、堆栈指针、内存分配状况、所打开文件的状态、账号和调度信息,以及其他在进程由运行态转换到就绪态或阻塞态时必须保存的信息,从而保证该进程随后能再次启动,就像未被中断过一样。
典型的进程表表项中的一些字段:
进程管理 | 存储管理 | 文件管理 |
---|---|---|
寄存器 | 正文段指针 | 根目录 |
程序计数器 | 数据段指针 | 工作目录 |
程序状态字 | 堆栈段指针 | 文件描述符 |
堆栈指针 | 用户ID | |
进程状态 | 组ID | |
优先级 | ||
调度参数 | ||
进程ID | ||
… … |
一个进程在执行过程中可能被中断数千次,但关键时每次中断后,被中断的进程都返回到与中断发生前完全相同的状态。
愿每个人都能带着怀疑的态度去阅读文章并探究其中原理。
道阻且长,往事作序,来日为章。
期待我们下一次相遇!