Linux编程(系统里的僵尸们)

2019-08-08 15:23:57 浏览数 (1)

我们通常说进程是动态的活动实体,这是很形象的,进程就像一个人一样,它会有很多种运行状态,一会儿睡眠、一会儿暂停、一会儿又继续执行运行。而且,他还会死掉变僵尸!

下图给出Linux进程从被创建(生)到被回收(死)的全部状态,以及这些状态发生转换时的条件:

结合上图,一起理一理进程从生到死的过程:

1,从“蛋生”可以看到,一个进程的诞生,是从其父进程调用fork( )开始的。

2,进程刚被创建出来的时候,处于TASK_RUNNING状态,从图中看到,处于该状态的进程可以是正在进程等待队列中排队,也可以占用CPU正在运行,我们习惯上称前者为“就绪态”,称后者为“执行态”。当进程状态为TASK_RUNNING并且占用CPU时才是真正的运行。

3,刚被创建的进程都处于“就绪”状态,等待系统调度,内核中的函数sched( )被称为调度器,他会根据各种参数来选择一个等待的进程去占用CPU。进程占用CPU之后就可以真正运行了,运行时间有个限定,比如20毫秒,这段时间被称为time slice,即“时间片”的概念。时间片耗光的情况下如果进程还没有结束,那么会被系统重新放入等待队列中等待。另外,正处于“执行态”的进程即使时间片没有耗光,也可能被别的更高优先级的进程“抢占”CPU,被迫重新回到等到队列中等待。

换句话说:进程跟人一样,从来都没有什么平等可言,有贵族就有屌丝,他们要处理的事情有不同的轻重缓急之分。

4,进程处于“执行态”时,可能会由于某些资源的不可得而被置为“睡眠态/挂起态”,这些情况下进程的状态都会变成TASK_INTERRUPIBLE或者TASK_UNINTERRUPIBLE。当进程所等待的资源变得可获取时,又会被系统置为TASK_RUNNING状态重新就绪排队。

5,当进程收到SIGSTOP或者SIGTSTP中的其中一个信号时,状态会被置为TASK_STOPPED,此时被称为“暂停态”,该状态下的进程不再参与调度,但系统资源不释放,直到收到SIGCONT信号后被重新置为就绪态。

6,运行的进程跟人一样,迟早都会死掉。进程的死亡可以有多种方式,可以是寿终正寝的正常退出,也可以是被异常杀死。比如上图中,在main函数内return或者调用exit( ),包括在最后线程调用pthread_exit( )都是正常退出,而受到致命信号死掉的情况则是异常死亡,不管怎么死,最后内核都会使得进程的状态变成所谓的僵尸态。

为什么一个进程的死掉之后还要把尸体留下呢?因为进程在退出的时候,将其退出信息都封存在他的尸体里面了,比如如果他正常退出,那退出值是多少呢?如果被信号杀死?那么是哪个信号呢?这些“死亡信息”都被一一封存在该进程的PCB当中,好让别人可以清楚地知道:我是怎么死的。

那谁会关心他是怎么死的呢?答案是他的父进程,他的父进程之所以要创建他,很大的原因是要让这个孩子去干某一件事情,现在这个孩子已死,那事情办得如何,孩子是否需要有个交代?但他又死掉了,所以之后将这些“死亡信息”封存在自己的尸体里面,等着父进程去查看,比如父子进程可以约定:如果事情办成了退出值为0,如果权限不足退出值为1,如果内存不够退出值为2等等等等。父进程可以随时查看一个已经死去的孩子的事情究竟办得如何。

可以看到,在工业社会中,哪怕是进程间的协作,也充满了契约精神。

0 人点赞