宋宝华: 聊一聊进程深度睡眠的TASK_KILLABLE这个状态

2023-10-24 09:51

本文主要是介绍宋宝华: 聊一聊进程深度睡眠的TASK_KILLABLE这个状态,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

众所周知,Linux的进程睡眠有两种常规状态:

  • TASK_INTERRUPTIBLE(浅度睡眠):可以被等待的资源唤醒,也能被signal唤醒;

  • TASK_UNINTERRUPTIBLE(深度睡眠):可以被等待的资源唤醒,但是不能被signal唤醒。

简单来说,深度睡眠的进程必须等待资源来了才能醒,在此之前,甚至你给它发任何的信号,它都不可能醒来。

浅度睡眠的进程,则可以被信号唤醒,对于常规的键盘、串口、触摸屏等等这些I/O设备,显然符合此类模型。所以Linux内核的代码里面经常看到这样的代码模板,笔者在《Linux设备驱动开发详解》一书中也花了大篇幅解释如下模板:

调用__set_current_state(TASK_INTERRUPTIBLE)并schedule()出去的进程,醒来第一件事往往就是通过signal_pending(current)查看信号是否存在,如果存在,就跳出去处理信号,无需等待I/O的完成(大不了信号处理完了再重新read)。

TASK_INTERRUPTIBLE看起来很理想,不至于在I/O没完成的时候,连CTRL+C都不响应(当然也不会响应其他SIGIO、SIGUSR1等信号)。

那么,有的童鞋就会问,既然浅度睡眠这么好,那么还要TASK_UNINTERRUPTIBLE这种完全不响应信号的深度睡眠干什么?

正在读本文的你,可能都有过这样的悲催经历,在NFS文件系统上面运行程序,但是NFS服务器挂了,你怎么都ctrl + c不掉那个进程,因为它就是个深度睡眠的场景。你徘徊,你迷茫,你问能不能直接都改为TASK_INTERRUPTIBLE,彻底删除TASK_UNINTERRUPTIBLE呢?

对此,祖师爷Linus的答复是:不可能。请看他2002年的邮件:

对于磁盘读等场景,如果读还没完成,就跳出去响应信号,application可能break,所以深度睡眠必须存在是一个客观的冷酷的现实(code fact)

祖师爷还有更猛的一锤定音:

但是,如果application break已经不再重要?如果干脆就是一个致命的信号,本身就是杀死应用的信号(SIGKILL),那么application break这个就显得无关紧要了,因为我们本身就不打算继续玩下去了!这样就使得深度睡眠的进程,还可以被杀死,妈妈再也不用担心NFS服务器挂了后,我痛苦,我孤独,我精分了!

Linux因此推出了一个特殊的深度睡眠状态,叫做

  • TASK_KILLABLE(可杀的深度睡眠):可以被等到的资源唤醒,不能被常规信号唤醒,但是可以被致命信号唤醒。

TASK_KILLABLE状态的定义是:

#define TASK_KILLABLE           (TASK_WAKEKILL | TASK_UNINTERRUPTIBLE)

所以它显然是属于TASK_UNINTERRUPTIBLE的,只是可以被TASK_WAKEKILL。

什么叫致命信号呢?talk is cheap,show me the code。

所以,足够致命的信号就是SIGKILL。SIGKILL何许人也,就是传说中的信号9,无法阻挡无法被应用覆盖的终极杀器:

仅仅从这个代码可以看出来,只有信号9才属于fatal signals。那么是不是只有信号9,才可以杀死TASK_KILLABLE的进程,信号2(CTRL+C)是否无能为力呢?

猜想再多,不如玩一个真实的代码,我们下面来改造下,把globalfifo.c的read改造为TASK_KILLABLE。

加载这个driver后,我们来读取它:

# insmod globalfifo.ko 

# insmod globalfifo-dev.ko 

# cat /dev/globalfifo 

这个时候,我们ps命令看一下,可以清楚到看到cat进程处于D状态:

root      7658  0.0  0.0  16800   752 pts/1    D+   19:21   0:00 cat /dev/globalfifo

从前面的代码可以看出,CTRL+C是不应该可以杀死这个cat进程的,因为它不是SIGKILL。但是我们来实际测试一下:

# cat /dev/globalfifo 

^C

#

实际却是可以杀死!!!

我们查看一下我们加的那个内核打印代码,看一下signal pending的情况:

# dmesg

[ 4670.082548] wake-up by fatal signal 100

明明我们发的是信号2,但是被置上的就是信号9(0x100的1对应SIGKILL的位)。这里发生了神奇的化学反应!!!

这踏马到底是怎么回事?这说明kernel把其他的可能杀死这个进程的信号,譬如SIGINT,也转化为了致命的SIGKILL信号。我们现在把代码改一行,要求kernel不要把SIGINT转换为SIGKILL:

这个时候,我们用CTRL+C就杀不死它了:

# cat /dev/globalfifo 

^C^C^C^C

但是它还是可以被9杀死:

$ sudo kill -9 8792

看看allow_signal()的代码:

(END)

Linux阅码场原创精华文章汇总

更多精彩,尽在"Linux阅码场",扫描下方二维码关注

点一点右下角”在看”,为阅码场打Call~

这篇关于宋宝华: 聊一聊进程深度睡眠的TASK_KILLABLE这个状态的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/274348

相关文章

一文解密Python进行监控进程的黑科技

《一文解密Python进行监控进程的黑科技》在计算机系统管理和应用性能优化中,监控进程的CPU、内存和IO使用率是非常重要的任务,下面我们就来讲讲如何Python写一个简单使用的监控进程的工具吧... 目录准备工作监控CPU使用率监控内存使用率监控IO使用率小工具代码整合在计算机系统管理和应用性能优化中,监

Linux进程CPU绑定优化与实践过程

《Linux进程CPU绑定优化与实践过程》Linux支持进程绑定至特定CPU核心,通过sched_setaffinity系统调用和taskset工具实现,优化缓存效率与上下文切换,提升多核计算性能,适... 目录1. 多核处理器及并行计算概念1.1 多核处理器架构概述1.2 并行计算的含义及重要性1.3 并

Linux下进程的CPU配置与线程绑定过程

《Linux下进程的CPU配置与线程绑定过程》本文介绍Linux系统中基于进程和线程的CPU配置方法,通过taskset命令和pthread库调整亲和力,将进程/线程绑定到特定CPU核心以优化资源分配... 目录1 基于进程的CPU配置1.1 对CPU亲和力的配置1.2 绑定进程到指定CPU核上运行2 基于

Javaee多线程之进程和线程之间的区别和联系(最新整理)

《Javaee多线程之进程和线程之间的区别和联系(最新整理)》进程是资源分配单位,线程是调度执行单位,共享资源更高效,创建线程五种方式:继承Thread、Runnable接口、匿名类、lambda,r... 目录进程和线程进程线程进程和线程的区别创建线程的五种写法继承Thread,重写run实现Runnab

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Java进程异常故障定位及排查过程

《Java进程异常故障定位及排查过程》:本文主要介绍Java进程异常故障定位及排查过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、故障发现与初步判断1. 监控系统告警2. 日志初步分析二、核心排查工具与步骤1. 进程状态检查2. CPU 飙升问题3. 内存

深度解析Java DTO(最新推荐)

《深度解析JavaDTO(最新推荐)》DTO(DataTransferObject)是一种用于在不同层(如Controller层、Service层)之间传输数据的对象设计模式,其核心目的是封装数据,... 目录一、什么是DTO?DTO的核心特点:二、为什么需要DTO?(对比Entity)三、实际应用场景解析

深度解析Java项目中包和包之间的联系

《深度解析Java项目中包和包之间的联系》文章浏览阅读850次,点赞13次,收藏8次。本文详细介绍了Java分层架构中的几个关键包:DTO、Controller、Service和Mapper。_jav... 目录前言一、各大包1.DTO1.1、DTO的核心用途1.2. DTO与实体类(Entity)的区别1

深度解析Python装饰器常见用法与进阶技巧

《深度解析Python装饰器常见用法与进阶技巧》Python装饰器(Decorator)是提升代码可读性与复用性的强大工具,本文将深入解析Python装饰器的原理,常见用法,进阶技巧与最佳实践,希望可... 目录装饰器的基本原理函数装饰器的常见用法带参数的装饰器类装饰器与方法装饰器装饰器的嵌套与组合进阶技巧

深度解析Spring Boot拦截器Interceptor与过滤器Filter的区别与实战指南

《深度解析SpringBoot拦截器Interceptor与过滤器Filter的区别与实战指南》本文深度解析SpringBoot中拦截器与过滤器的区别,涵盖执行顺序、依赖关系、异常处理等核心差异,并... 目录Spring Boot拦截器(Interceptor)与过滤器(Filter)深度解析:区别、实现