Linux进程编程实践2——进程状态、模拟实现僵尸进程和孤儿进程

一、Linux进程状态

操作系统的经典三态为:就绪、阻塞、运行
对于Linux内核来说我们可以在kernel源代码里看到进程的状态

/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};

  • R运行状态(running) : 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
  • S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠
  • D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
  • T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
  • X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态
  • Z僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用,后面讲)没有读取到子进程退出的返回代码时就会产生僵死(尸)进程。
  • 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态

二、僵尸进程

2.1 什么是僵尸进程?

如果子进程先退出父进程还没退出,那么子进程必须等到父进程捕获到了子进程的退出状态才真正结束,否则这个时候子进程就成为僵尸进程。之所以被称为僵死进程是因为他虽然死掉了,但依然在进程表中存在

2.2 模拟实现僵尸进程

僵尸进程的原理是子进程先退出,而父进程没有捕获到子进程的退出状态,这是子进程就变成了一个僵尸进程,我们利用fork()来创建出一个子进程,并于10s后退出

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

int main()
{
  pid_t ret = fork();
  if(ret < 0)
  {
    printf("创建进程失败!\n");
  }
  else if(ret == 0)
  {
    int count = 0;
    while(count++ < 5)
    {
      printf("我是子进程(id:%d)\n",getpid());
      sleep(2);
    }
    exit(0);
  }
  else
  {
    while(1)
    {
      printf("我是父进程(id:%d)\n",getpid());
      sleep(1);
    }
  }
  sleep(1);
  return 0;
}

上述代码编译后运行,可以看到如下效果
在这里插入图片描述

我们另开一个窗口并编写一个监测进程脚本来查看进程的状态

while :; do ps axj | head -1 && ps axj | grep 06-zombie| grep -v grep; sleep 1 ; echo "#############"; done

在这里插入图片描述
这样僵尸进程就被模拟出来了

Q1:为什么会有僵尸进程?

  • 给进程设置僵尸状态的目的是维护子进程的信息,以便父进程在以后某个时间获取。
  • 这些信息包括子进程的进程ID、终止状态以及资源利用信息(CPU时间,内存使用量等等)。
    • 如果一个进程终止,而该进程有子进程处于僵尸状态,那么它的所有僵尸子进程的父进程ID将被重置为1(init进程)。继承这些子进程的init进程将清理它们(init进程将wait它们,从而去除僵尸状态)。

Q2:僵尸进程的危害有哪些?

  • 僵尸进程存在的原因是为了维护子进程的信息,如果父进程不读取子进程的退出状态,那么子进程会一直维持僵尸状态
  • 维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说, Z状态一直不退出, PCB一直都要维护
  • 结构体本身占用内存,如果Z状态一直不退出,就会造成内存资源的浪费
  • 造成内存泄漏

三、孤儿进程

3.1 什么是孤儿进程?

如果父进程先退出子进程还没退出那么子进程的父进程将变为init进程。

注意:

  • 任何一个进程都必须由父进程
  • 如果父亲进程先结束,子进程会托孤给1号进程

3.2 模拟实现孤儿进程

僵尸进程的模拟实现是通过fork()创建子进程并先于父进程退出就可以实现,同理,孤儿进程只需要让父进程先于子进程退出即可,只需对代码逻辑稍加修改就能实现

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

int main()
{
  pid_t ret = fork();
  if(ret < 0)
  {
    printf("创建进程失败!\n");
  }
  else if(ret > 0)
  {
    int count = 0;
    while(count++ < 5)
    {
      printf("我是父进程(id:%d)\n",getpid());
      sleep(2);
    }
    exit(0);
  }
  else
  {
    while(1)
    {
      printf("我是子进程(id:%d)\n",getpid());
      sleep(1);
    }
  }
  sleep(1);
  return 0;
}

编译运行上述代码:
在这里插入图片描述
我们另开一个窗口并编写一个监测进程脚本来查看进程的状态

while :; do ps axj | head -1 && ps axj | grep 07-orphan | grep -v grep; sleep 1 ; echo "#############"; done

在这里插入图片描述

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值