1. 前言

 内核版本:linux 4.9.225。内核版本:linux 4.9.225。对于内核常用的might_sleep函数,如果没有调试的需要(没有定义CONFIG_DEBUG_ATOMIC_SLEEP),这个宏/函数什么事情都不,might_sleep就是一个空函数,所以平常看code的时候可以忽略。内核只是用它来提醒开发人员,调用该函数的函数可能会sleep。

2. might_sleep的定义

# include/linux/kernel.
#ifdef CONFIG_PREEMPT_VOLUNTARY
extern int _cond_resched(void);
# define might_resched() _cond_resched()
#else
# define might_resched() do { } while (0)
#endif #ifdef CONFIG_DEBUG_ATOMIC_SLEEP
void ___might_sleep(const char *file, int line, int preempt_offset);
void __might_sleep(const char *file, int line, int preempt_offset);
/**
* might_sleep - annotation for functions that can sleep
*
* this macro will print a stack trace if it is executed in an atomic
* context (spinlock, irq-handler, ...).
*
* This is a useful debugging help to be able to catch problems early and not
* be bitten later when the calling function happens to sleep when it is not
* supposed to.
*/
# define might_sleep() \
do { __might_sleep(__FILE__, __LINE__, 0); might_resched(); } while (0)
#else
# define might_sleep() do { might_resched(); } while (0)
#endif

2.1 未开启CONFIG_DEBUG_ATOMIC_SLEEP选项

在没有调试需求,即在选项 CONFIG_DEBUG_ATOMIC_SLEEP和 CONFIG_PREEMPT_VOLUNTARY(自愿抢占,代码中增加抢占点,在中断退出后遇到抢占点时进行抢占切换)未打开的情况下:

# define might_resched() do { } while (0)
# define might_sleep() do { might_resched(); } while (0)

可以看到,这里什么事情都没做。其中内核源码对此也有明确的注释:might_sleep - annotation for functions that can sleep。所以对于release版的kernel 而言,might_sleep函数仅仅是一个annotation,用来提醒开发人员,一个使用might_sleep的函数在其后的代码执行中可能会sleep。

2.2 开启CONFIG_DEBUG_ATOMIC_SLEEP选项

如果有调试需求的话,就必须打开内核的 CONFIG_DEBUG_ATOMIC_SLEEP选项。 此时might_sleep定义如下:

# define might_resched() _cond_resched()
# define might_sleep() \
do { __might_sleep(__FILE__, __LINE__, 0); might_resched(); } while (0)

CONFIG_DEBUG_ATOMIC_SLEEP选项主要用来排查是否在一个ATOMIC操作的上下文中有函数发生sleep行为,关于什么是 ATOMIC操作,内核源码在might_sleep函数前也有一段注释:this macro will print a stack trace if it is executed in an atomic context (spinlock, irq-handler, ...)

所以,一个进程获得了spinlock之后(进入所谓的atomic context),或者是在一个irq-handle中(也就是一个中断上下文中)。这两种情况下理论上不应该让当前的execution path进入sleep状态(虽然不是强制规定,换句话说,一个拥有spinlock的进程进入sleep并不必然意味着系统就一定会deadlock 等,但是对内核编程而言,还是应该尽力避开这个雷区)。

在CONFIG_DEBUG_ATOMIC_SLEEP选项打开的情形下,might_sleep具体实现的功能分析如下:

# kernel/sched/core.c
void ___might_sleep(const char *file, int line, int preempt_offset)
{
static unsigned long prev_jiffy; /* ratelimiting */
unsigned long preempt_disable_ip; rcu_sleep_check(); /* WARN_ON_ONCE() by default, no rate limit reqd. */
if ((preempt_count_equals(preempt_offset) && !irqs_disabled() &&!is_idle_task(current)) ||
system_state != SYSTEM_RUNNING || oops_in_progress) /* 核心判断 */
return;
if (time_before(jiffies, prev_jiffy + HZ) && prev_jiffy)
return;
prev_jiffy = jiffies; /* Save this before calling printk(), since that will clobber it */
preempt_disable_ip = get_preempt_disable_ip(current); printk(KERN_ERR
"BUG: sleeping function called from invalid context at %s:%d\n",
file, line);
printk(KERN_ERR
"in_atomic(): %d, irqs_disabled(): %d, pid: %d, name: %s\n",
in_atomic(), irqs_disabled(),
current->pid, current->comm); if (task_stack_end_corrupted(current))
printk(KERN_EMERG "Thread overran stack, or stack corrupted\n"); debug_show_held_locks(current);
if (irqs_disabled())
print_irqtrace_events(current);
if (IS_ENABLED(CONFIG_DEBUG_PREEMPT)
&& !preempt_count_equals(preempt_offset)) {
pr_err("Preemption disabled at:");
print_ip_sym(preempt_disable_ip);
pr_cont("\n");
}
dump_stack();
add_taint(TAINT_WARN, LOCKDEP_STILL_OK);
}
EXPORT_SYMBOL(___might_sleep);

在当前CONFIG_DEBUG_ATOMIC_SLEEP选项使能的前提下, 可以看到__might_sleep还是干了不少事情的,最主要的工作是在第一个if语句那里,尤其是preempt_count_equals和 irqs_disabled,都是用来判断当前的上下文是否是一个atomic context,因为我们知道,只要进程获得了spin_lock的任一个变种形式的lock,那么无论是单处理器系统还是多处理器系统,都会导致 preempt_count发生变化,而irq_disabled则是用来判断当前中断是否开启。__might_sleep正是根据这些信息来判断当前正在执行的代码上下文是否是个atomic,如果不是,那么函数就直接返回了,因为一切正常。如果是,那么代码下行。

所以让CONFIG_DEBUG_ATOMIC_SLEEP选项打开,可以捕捉到在一个atomic context中是否发生了sleep,如果你的代码不小心在某处的确出现了这种情形,那么might_sleep会通过后续的printk以及dump_stack来协助你发现这种情形。

至于__might_sleep函数中的system_state,它是一个全局性的enum型变量,在 /init/main.c中声明,主要用来记录当前系统的状态:

# init/main.c
enum system_states system_state __read_mostly;
EXPORT_SYMBOL(system_state);

注意system_state已经被export出来,所以内核模块可以直接读该值来判断当前系统的运行状态,具体定义及常见的状态如下:

# include\linux\kernel.h
/* Values used for system_state */
extern enum system_states {
SYSTEM_BOOTING,
SYSTEM_RUNNING,
SYSTEM_HALT,
SYSTEM_POWER_OFF,
SYSTEM_RESTART,
} system_state;

最常见的状态是SYSTEM_RUNNING了,当系统正常起来之后就处于这个状态。。

内核常用的might_sleep函数的更多相关文章

  1. 常用的WinAPI函数整理

    常用的WinAPI函数整理 一.进程  创建进程:    CreateProcess("C:\\windows\\notepad.exe",0,0,0,0,0,0,0,&s ...

  2. Windows内核-7-IRP和派遣函数

    Windows内核-7-IRP和派遣函数 IRP以及派遣函数是Windows中非常重要的概念.IRP 是I/O Request Pocket的简称,意思是I/O操作的请求包,Windows中所有Use ...

  3. 最常用的截取函数有left,right,substring

    最常用的截取函数有left,right,substring 1.LEFT ( character_expression , integer_expression ) 返回从字符串左边开始指定个数的字符 ...

  4. Appium常用的API函数

    在学习应用一个框架之前,应该了解一下这个框架的整体结构或是相应的API函数.这篇文章还不错:http://blog.sina.com.cn/s/blog_68f262210102vzf9.html,就 ...

  5. MYSQL常用内置函数详解说明

    函数中可以将字段名当作变量来用,变量的值就是该列对应的所有值:在整理98在线字典数据时(http://zidian.98zw.com/),有这要一个需求,想从多音字duoyinzi字段值提取第一个拼音 ...

  6. 常用的Sql 函数

    常用的Sql 函数 1: replace 函数,替换字符. 语法 replace (original-string, search-string, replace-string ) 第一个参数你的字符 ...

  7. 【python游戏编程之旅】第四篇---pygame中加载位图与常用的数学函数。

    本系列博客介绍以python+pygame库进行小游戏的开发.有写的不对之处还望各位海涵. 在上一篇博客中,我们学习了pygame事件与设备轮询.http://www.cnblogs.com/msxh ...

  8. 常用的sql函数

    常用的sql函数 concat('hello','world') 结果:helloworld  作用:拼接 substr('helloworld',1,5)      hello           ...

  9. python学习笔记-day4笔记 常用内置函数与装饰器

    1.常用的python函数 abs             求绝对值 all               判断迭代器中所有的数据是否为真或者可迭代数据为空,返回真,否则返回假 any          ...

  10. Python基础学习笔记(九)常用数据类型转换函数

    参考资料: 1. <Python基础教程> 2. http://www.runoob.com/python/python-variable-types.html 3. http://www ...

随机推荐

  1. KingbaseES例程之拥有大量索引的表导入数据

    概述 如何快速插入大量数据比如几千万上亿的带索引的数据表. 数据准备 准备一个拥有二十个索引的数据表. kingbase=# \d+ bigtab Table "kingbase.bigta ...

  2. KingbaseES启动数据库失败后如何分析

    关键字: KingbaseES.sys_ctl.启动日志 一.KingbaseES数据库服务启动 1.1 数据库启动机制 1) 数据库通过sys_ctl工具手工启动数据库服务kingbase. 2) ...

  3. mybatis 输出sql日志

    logging.level.com.dsmp.server.core.pgsqldao=debug com.dsmp.server.core.pgsqldao 为包名

  4. 用【Unity】中的【3D Object】画【数学函数】图形 —— 正弦函数... { }

    效果 场景搭建 创建一个空物体,并将其命名为 "GameManager",并[Reset]它 创建一个 "Sphere",并将其[Scale]设置为(0.1,0 ...

  5. 助力培养高质量AI人才,璞公英乐学平台在日本深受好评!

    璞公英乐学平台(原名"璞睿魔数")自进入日本市场以来,受到日本用户的广泛好评.近日,日本AI门户网站AIsmiley在发刊的杂志<AI人才育成指南>中对璞公英乐学平台做 ...

  6. Java代码审计之不安全的Java代码

    Java代码审计之不安全的Java代码 ​ 在打靶场的同时,需要想一下如果你是开发人员你会怎样去防御这种漏洞,而作为攻击方你又怎么去绕过开发人员的防御. 环境搭建 https://github.com ...

  7. Java中关键的知识点

    JVM,运行是内存模型 Java 反射 Java 注解 函数式接口 lambda表达式/流式计算 动态代理

  8. Elasticsearch索引生命周期管理探索

    文章转载自: https://mp.weixin.qq.com/s?__biz=MzI2NDY1MTA3OQ==&mid=2247484130&idx=1&sn=454f199 ...

  9. 【C++】从零开始的CS:GO逆向分析1——寻找偏移与基址的方法

    [C++]从零开始的CS:GO逆向分析1--寻找偏移与基址的方法   前言:此文章主要用于提供方法与思路,fps游戏基本都能如此找偏移,文章里找的偏移比较少,主要用来演示寻找思路,文章的后记中会附一个 ...

  10. C#/VB.NET 读取条码类型及条码在图片中的坐标位置

    我们在创建条形码时,如果以图片的方式将创建好的条码保存到指定文件夹路径,可以在程序中直接加载图片使用:已生成的条码图片,需要通过读取图片中的条码信息,如条码类型.条码绘制区域在图片中的四个顶点坐标位置 ...