面试题:什么是孤儿进程,什么是僵尸进程?如何解决僵尸进程?

针对该面试题,我在这里谈谈我自己的理解。

子进程和fork(  )系统调用

要想了解孤儿进程和僵尸进程,我们首先需要了解子进程这个概念。
  • 进程在执行期间,可以通过fork(  )系统调用来创建一个属于自己的子进程。这时,称调用fork的进程为父进程,由fork创建的进程为该父进程的子进程。一般而言,父子进程共享代码段,但对于数据段、栈段等其他资源,父进程在调用fork函数时会将该部分资源完全复制到子进程中去,父子进程对该部分的资源并不共享。
  • 创建完子进程后,父进程从fork(  )的返回点继续执行,而子进程也是从fork(  )的返回点开始执行。一般而言,要么先执行子进程,要么父子进程并行执行(这取决于操作系统设计者的设计)。
  • 父进程通过调用wait(  )系统调用来将其子进程回收释放。
了解了fork(  )系统调用和子进程的概念后,我们可以来谈谈什么是孤儿进程,什么是僵尸进程。

孤儿进程

其父进程已经终止,但父进程没有调用wait( )将其回收,那么该进程为孤儿进程。

僵尸进程

进程已经终止,但其父进程仍未调用wait( )将其回收,则该进程为僵尸进程(此时父进程并没有终止)。一般而言,僵尸进程只是短暂存在,当其被父进程回收释放时,那么该进程便不再存在。

  • 孤儿进程与僵尸进程的共同点是进程都是子进程,且都已执行完成,但仍未释放资源;区别在于其父进程是否已经终止。
  • 僵尸进程有可能会变为孤儿进程。


如何处理僵尸进程

处理僵尸进程,实际上就是父进程调用wait(  )系统调用回收执行完的子进程的过程。
父进程在执行完成后都会通过调用exit(  )系统调用来对自己进行释放回收。
在每一个进程的PCB中都存储了进程对应的状态,当进程执行完毕时,我们会将其状态设置为DEAD状态。
因此,僵尸进程可以通过下方方法进行处理:
  • 当父进程执行完成后,调用exit(  )前,首先调用wait(  )来回收已执行完成的子进程。
  • wait(  )的实现规则为:
    1)在wait函数中,它会一直循环查找调用wait系统调用的这一进程的子进程(每一个父进程都会有一个子进程队列,查找时顺序查找该队列)。如果所有进程都查找了一遍都没有当前进程的子进程,说明该进程已经不存在子进程,那么我们直接返回,结束函数;
       2)如果查找了一遍发现存在子进程,但子进程的状态都不为DEAD,那么我们调用schedule(  )函数去执行其他进程或线程,该进程等待下一次分配cpu时间再从头寻找子进程;(schedule函数是进程调度函数)
       3)如果在查找的过程中发现了一个状态为DEAD的子进程,那么这就是我们要释放的子进程,我们将其释放;

wait(  )的实现代码,大家可以简略了解下大致的实现过程:
int ProgramManager::wait(int *retval)
{
    PCB *child;
    ListItem *item;
    bool interrupt, flag;

    while (true)
    {
        interrupt = interruptManager.getInterruptStatus();
        interruptManager.disableInterrupt();

        item = this->allPrograms.head.next;

        //查找子进程
        flag = true;
        while (item)
        {
            child = ListItem2PCB(item, tagInAllList);
            if (child->parentPid == this->running->pid)
            {
                flag = false;
                if (child->status == ProgramStatus::DEAD)
                {
                    break;
                }
            }
            item = item->next;
        }

        if (item) // 找到一个可回收的子进程
        {
            if (retval)
            {
                *retval = child->retValue;
            }

            int pid = child->pid;
            releasePCB(child);
            interruptManager.setInterruptStatus(interrupt);
            return pid;
        }
        else 
        {
            if (flag) // 子进程已返回
            {
                
                interruptManager.setInterruptStatus(interrupt);
                return -1;
            }
            else // 存在子进程,但子进程的状态不为DEAD
            {
                interruptManager.setInterruptStatus(interrupt);
                schedule();
            }
        }
    }
}

如何处理孤儿进程

僵尸进程的处理如上所述,那么对于孤儿进程,我们又应该如何对其进行回收呢?
在这里我们需要知道,父进程是如何通过调用exit(  )来回收自身的。
实际上,当父进程执行结束后(亦或者是时间片用完),会立即调用schedule函数来将就绪队列中的下一个进程换上cpu。而在schedule函数中,首先判断当前进程是否已执行完毕,即检查状态是否为DEAD,如果为DEAD则会调用exit(  )将其回收释放,然后再换上就绪队列中的下一个进程。
据此,我们可以设置一个专门存放状态为DEAD的进程的队列,通过在schedule函数中回收该队列的进程来实现对孤儿进程的回收。
全部评论
很有深度
点赞 回复 分享
发布于 2022-08-16 18:27
很有深度
点赞 回复 分享
发布于 2022-08-12 23:38
你下午面试是这道题?
点赞 回复 分享
发布于 2022-08-12 16:34

相关推荐

10-27 02:29
已编辑
门头沟学院 嵌入式工程师
牛客72783561...:简历不是这么写的,你这两个项目只说了用到了什么技术,却没说取得了什么成果,在我看来这就是你自己做的一个demo,没有价值。你为什么不写你电赛国二的那个项目?
点赞 评论 收藏
分享
12-19 16:52
门头沟学院
点赞 评论 收藏
分享
评论
5
9
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务