Linux Task 状态变迁

Linux Task 状态变迁
Linux Task 状态变迁

 

在 Linux 中,通常进程将在下面几种状态之间迁移:

  • 可运行状态和运行状态 
  • 睡眠状态
    • 不可中断的睡眠
    • 仅能被致命信号中断的睡眠
    • 可中断的睡眠
  • 停止态
  • 僵尸态和死亡态

1 可运行状态和运行状态

均用 TASK_RUNNING 表示:

可运行状态

在等待队列上等待调度器调度来获取 CPU 时间片。

运行状态

已经被调度器选中并分配到 CPU 时间片,正在 CPU 上运行。


2 僵尸态和死亡态

在 linux 系统中,资源是以进程为单位分配的,处于僵尸态的进程在父进程回收资源后进入死亡态。

僵尸态 (TASK_DEAD, EXIT_ZOMBIE)

当进程完成执行或终止时,它会向父进程发送 SIGCHLD 信号并进入僵尸状态。如果父进程没有回收僵尸进程,那么这些进程就会一直占用系统资源。

父进程必须使用 wait() 或 waitpid() 系统调用用来获取子进程的退出状态并回收资源。

死亡态 (TASK_DEAD ,EXIT_DEAD)

父进程调用 wait() 或 waitpid() 系统调用获取了子进程的退出状态并将其系统资源释放,子进程进入死亡态。死亡态的进程在进程表中不再存在。

僵尸态是一个过渡状态,表示进程已经结束但是父进程还没有完全释放它,而死亡态状态则是一个最终状态,表示进程已经完全结束并且被释放。

进程回收子进程资源:

int main()
{
    pid_t pid = fork();
    if (pid == 0) {
        // 子进程执行的代码
        return 0;
    }
    else if (pid > 0) {
        // 父进程执行的代码
        std::cout << "Parent process starts\n";
        int status;
        waitpid(pid, &status, 0); // 等待子进程退出,回收其系统资源
        if (WIFEXITED(status)) {
            std::cout << "Child process exited with status: " << WEXITSTATUS(status) << "\n";
        }
        std::cout << "Parent process ends\n";
        return 0;
    }
}

进程回收子线程资源:

void *childThreadFunc(void *arg) {
    // 子线程运行
}

int main() {
    pthread_t childThread;

    pthread_create(&childThread, NULL, childThreadFunc, NULL);

    pthread_join(childThread, NULL); // 等待子线程退出,即使回收它的系统资源

    return 0;
}

注:

如果调用 pthread_detach() 函数可以将子线程设置为 "DETACHED" 状态后,子线程退出时可以自动回收资源,而不需要使用 pthread_join() 函数等待线程退出。

非 "DETACHED" 状态子线程退出时,即使进程没有调用 pthread_join() 回收其资源,其占用的资源将在进程退出后,在回收其资源时统一回收,但是会导致资源回收不及时。

如果父进程提前子进程退出呢?子进程将变成孤儿进程,由 init 进程统一回收孤儿进程的资源。


3 停止态

TASK_STOPPED

处于可运行状态或运行状态的 task,可以使用 SIGSTOP 或 SIGTSTP 信号将 task 置于停止状态 (T),这两个信号的区别:

  • SIGSTOP :强制停止 task 的信号,该 task 不能忽略此信号并将进入停止状态。
  • SIGTSTP :也是停止进程的信号,但 task 可以通过信号处理程序来忽略该信号。

在这种状态下,可以通过发送 SIGCONT 信号使进程恢复到可运行状态或运行状态。


4 休眠状态        

Linux Task 有三种休眠状态,主要区别在于唤醒的方法,包括:

TASK_INTERRUPTIBLE

将其唤醒再次运行的方法有两种:

  • 显式唤醒
  • 收到非屏蔽信号

比如 mutex_lock_interruptible() 申请互斥锁失败会导致进程进入该状态。

TASK_UNINTERRUPTIBLE

将其唤醒再次运行的方法只有一种:

  • 会忽略所有信号,只能显式唤醒

比如等待 IO 资源或 mutex_lock() 申请互斥锁失败会导致进程进入该状态。

TASK_KILLABLE 

将其唤醒再次运行的方法有两种:

  • 显式唤醒它
  • 致命信号(fatal signals)

比如 mutex_lock_killable() 申请互斥锁失败会导致进程进入该状态。

4.1 为什么存在 TASK_KILLABLE?

TASK_KILLABLE [LWN.net]

//include/linux/sched.h
#define TASK_KILLABLE            (TASK_WAKEKILL | TASK_UNINTERRUPTIBLE)

TASK_INTERRUPTIBLE 和 TASK_UNINTERRUPTIBLE 睡眠方式都有优点和缺点。

TASK_INTERRUPTIBLE 状态的 Task

优点:

可以更快地响应信号。

缺点:

它使编程更加困难。 使用可中断睡眠的内核代码必须始终检查它是否因信号而醒来:

  1. 如果是,则清理它正在做的任何事情并将 -EINTR 返回给用户空间;
  2. 用户空间端也必须意识到系统调用被中断并做出相应的响应;但是并非所有用户空间程序员都以会认真和正确的处理 -EINTR 信号;

TASK_UNINTERRUPTIBLE 状态的 Task

优点:

使睡眠不可中断可以消除​​“可中断休眠”带来的这些问题,它会忽略所有信号,不会将错误的处理抛到用户空间。

缺点:

代价是不可中断。如果预期的唤醒事件没有出现,该过程将永远等待,并且除了重新启动系统之外,通常没有任何人可以对此做任何事情。这是可怕的、无法杀死的进程的根源,它被 ps 显示为处于“D”状态

考虑到不可终止进程的高度令人讨厌的性质,人们会认为应该尽可能使用可中断睡眠。这个想法的问题在于,在许多情况下,引入可中断睡眠很可能会导致应用程序错误。正如 Alan Cox 最近指出的那样:

Unix 传统(因此几乎所有应用程序)认为文件存储写入是非信号中断的。更改该保证既不安全也不切实际。

Matthew Wilcox 意识到,如果应用程序无论如何都将被终止,那么许多关于应用程序错误的担忧并不适用。如果系统调用注定永远不会返回到用户空间,开发人员是否考虑过系统调用中断的可能性并不重要。所以Matthew创建了一个新的休眠状态,叫做TASK_KILLABLE;它的行为类似于 TASK_UNINTERRUPTIBLE,除了致命信号(fatal signals)会中断睡眠。

TASK_KILLABLE 带来了一组新的原语,用于等待事件和获取锁:

int wait_event_killable(wait_queue_t queue, condition);
long schedule_timeout_killable(signed long timeout);
int mutex_lock_killable(struct mutex *lock);
int wait_for_completion_killable(struct completion *comp);
int down_killable(struct semaphore *sem);

对于这些函数中的每一个,正常、成功返回的返回值将为零,或者在出现致命信号的情况下为负错误代码。在后一种情况下,内核代码应该清理并返回,使进程能够被终止。

TASK_KILLABLE 补丁已为 2.6.25 内核合并,但这并不意味着无法杀死的进程问题已经消失。内核中(从 2.6.26-rc8 开始)实际使用这种新状态的地方数量非常少——例如,人们不必担心在数手指时会用完手指。 NFS 客户端代码已经转换,这只能是一个可喜的发展。但是 TASK_KILLABLE 的其他用途很少,而且在设备驱动程序中根本没有,这通常是进程陷入困境的地方。

新的 API 在内核中得到广泛使用可能需要一些时间,尤其是当它补充了大多数情况下运行良好的现有功能时。此外,将现有代码大量转换为可终止睡眠的好处并不完全清楚。但几乎可以肯定,内核中的某些地方可以通过此更改得到改进,前提是用户和开发人员能够识别进程挂起(hung)的位置。在新代码中使用 killable sleep 也是有意义的,除非有一些紧迫的理由完全禁止中断。

关于更多 TASK_KILLABLE 的介绍:

宋宝华: 聊一聊进程深度睡眠的TASK_KILLABLE这个状态 - 腾讯云开发者社区-腾讯云

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zs.w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值