LInux中ThreadInfo中的preempt_count字段

最近看各种上下文,发现和ThreadInfo中的preemption字段密切,于是便调查了下。

看下Linux源码中的注释:

/*
* We put the hardirq and softirq counter into the preemption
* counter. The bitmask has the following meaning:
*
* - bits 0-7 are the preemption count (max preemption depth: 256)
* - bits 8-15 are the softirq count (max # of softirqs: 256)
*
* The hardirq count can in theory reach the same as NR_IRQS.
* In reality, the number of nested IRQS is limited to the stack
* size as well. For archs with over 1000 IRQS it is not practical
* to expect that they will all nest. We give a max of 10 bits for
* hardirq nesting. An arch may choose to give less than 10 bits.
* m68k expects it to be 8.
*
* - bits 16-25 are the hardirq count (max # of nested hardirqs: 1024)
* - bit 26 is the NMI_MASK
* - bit 27 is the PREEMPT_ACTIVE flag
*
* PREEMPT_MASK: 0x000000ff
* SOFTIRQ_MASK: 0x0000ff00
* HARDIRQ_MASK: 0x03ff0000
* NMI_MASK: 0x04000000
*/

这里其实把preempt_count划分成了四部分:抢占计数器、软中断计数、硬件中断计数、NMI计数。

抢占计数器:0-7位

软中断计数器:8-15位

硬中断计数器:16-25位

NMI标识:26位

基于上面的信息,看下Linux内核中判断各个上下文的宏:

#define hardirq_count()    (preempt_count() & HARDIRQ_MASK)
#define softirq_count()    (preempt_count() & SOFTIRQ_MASK)
#define irq_count()    (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK   | NMI_MASK))

可以看到这里判断硬件中断/软中断/中断上下文的实质,就是根据thread_info结构中的preempt_count字段,额忘记列出preempt_count()的实现了:#define preempt_count() (current_thread_info()->preempt_count),这时明白了吧!!!

不过后面几个掩码的计算有点绕,参考下源码:

#define __IRQ_MASK(x)    ((1UL << (x))-1)

#define PREEMPT_MASK    (__IRQ_MASK(PREEMPT_BITS) << PREEMPT_SHIFT)
#define SOFTIRQ_MASK    (__IRQ_MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT)
#define HARDIRQ_MASK    (__IRQ_MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT)
#define NMI_MASK    (__IRQ_MASK(NMI_BITS)     << NMI_SHIFT)

实质上就是取对应的位。

*
 * Are we doing bottom half or hardware interrupt processing?
 * Are we in a softirq context? Interrupt context?
 * in_softirq - Are we currently processing softirq or have bh disabled?
 * in_serving_softirq - Are we currently processing softirq?
 */
#define in_irq()        (hardirq_count())
#define in_softirq()        (softirq_count())
#define in_interrupt()        (irq_count())
#define in_serving_softirq()    (softirq_count() & SOFTIRQ_OFFSET)

而这里只是对上面各种计数的包装,只要对应的计数不为0,则表示在对应的上下文中。in_interrupt表示中断上下文,包括了硬件中断、软中断和NMI中断。in_serving_softirq判断是否当前正在处理软中断。

#define in_nmi()    (preempt_count() & NMI_MASK)

in_nmi判断当前是否在NMI中断上下文。

既然如此,咱们再结合内核抢占调度的情况,看下抢占调度函数

asmlinkage void __sched notrace preempt_schedule(void)
{
    struct thread_info *ti = current_thread_info();

    /*
     * If there is a non-zero preempt_count or interrupts are disabled,
     * we do not want to preempt the current task. Just return..
     */
    if (likely(ti->preempt_count || irqs_disabled()))
        return;

    do {
        add_preempt_count_notrace(PREEMPT_ACTIVE);
        __schedule();
        sub_preempt_count_notrace(PREEMPT_ACTIVE);

        /*
         * Check again in case we missed a preemption opportunity
         * between schedule and now.
         */
        barrier();
    } while (need_resched());
}

可以看到这里进入函数起始,便对当前线程的preempt_count进行了判断,根据上面的介绍,当内核处理任何一个上下文中时,preempt_count均不可能为0,所以我们也可以根据此发现在软中断、硬件中断、禁止内核抢占、NMI上下文中均不允许调度。当然,我们看到后面后又个irqs_disabled,意味着如果当前禁用了硬件中断,则同样不会发生抢占调度。

补充:

关闭中断的实质是设置EFLAGS寄存器的IF标志位,STI指令可执行这一操作,当标志位被设置为1时,表示当前关闭中断状态。这里的中断指的是可屏蔽的硬件中断。

参考资料:

LInux3.10.1内核源码

时间: 2024-11-29 06:01:50

LInux中ThreadInfo中的preempt_count字段的相关文章

数据库中导出表中相应字段到指定文件中

数据库中导出某一个表中需要的字段到文件中是公司中经常要做到的事,那怎么实现呢? 比如你要查询enterpriseaics中的所有字段的值到d盘的aa.txt中去,注:aa.txt不能存在,否则会报错. <span style="font-size:18px;">select * from enterpriseaics into outfile 'd:\\aa.txt' ;</span> 当然如果你想规范一下格式,例如:每个字段的值之间以逗号分开,每一行换行,每个

Linux基础--系统启动中grub功能

本文主要讲解grub原理和系统启动过程中grub的作用过程. grub是多数Linux发行版中使用的boot loader系统引导程序, 其主要作用是计算机从boot sequence中选取指定设备之后, 由grub找到系统内核kernel文件并加载内核文件的作用. grub执行过程 grub作用过程中主要分为三个阶段的作用, 如下图所示: stage1: 启动grub程序, 位于MBR的前446Byte中; stage1.5: 识别内核所在分区的文件系统类型, 位于内核文件所在分区的boot

linux bash shell中for的用法and示例

关于linux bash shell中的for语句 在linux中shell是必不可少的一部分,但是在bash shell中有while,for,until等循环命令,今天就介绍一下关于for的一些用法.文中任何错误希望大佬们一一指出,不胜感激. bash shell中提供了for命令,用于创建通过一系列值重复的循环,每次重复使用系列中的一个值执行一个定义的命令集. for语句的基本格式如下: for---in list do commands done 在参数list中提供一系列用于迭代(上一

Linux设备驱动中的阻塞和非阻塞I/O

[基本概念] 1.阻塞 阻塞操作是指在执行设备操作时,托不能获得资源,则挂起进程直到满足操作所需的条件后再进行操作.被挂起的进程进入休眠状态(不占用cpu资源),从调度器的运行队列转移到等待队列,直到条件满足. 2.非阻塞 非阻塞操作是指在进行设备操作是,若操作条件不满足并不会挂起,而是直接返回或重新查询(一直占用CPU资源)直到操作条件满足为止. 当用户空间的应用程序调用read(),write()等方法时,若设备的资源不能被获取,而用户又希望以阻塞的方式来访问设备,驱动程序应当在设备驱动层的

在共享文件夹中解压到linux其他目录中,千万注意软连接的问题

这几天一直在练习驱动,搭建一个简单的交叉编译环境,交叉编译工具链和开发板的源码都导进去linux的目录中,并且解压,但是执行一个简单的交叉编译命令总会出现一个奇怪的错误提示: as:unrecongnized option 'mcpu=XXX'; 百思不得其解,后来经过一个实验室的师兄的提示,终于明白了.这里有一个背景需要交代一下: 我是在windows下面的unbuntu虚拟机中进行的驱动开发练习. 首先,我建立了一个共享文件夹,将交叉编译链的压缩文件拷贝进去,这里我的压缩文件的名字为XXX.

浅析Linux驱动模型中的底层数据结构kobject和kset

1.kobject Linux内核用kobject来表示一个内核对象.它和Sysfs文件系统联系密切,在内核中注册到系统中的每个kobject对象在sysfs文件系统中对对应着一个文件目录.kobject数据结构通常的用法是嵌入到其对他的数据结构中(即容器,比如cdev结构),用于实现内核对该类数据结构对象的管理.这些数据结构(容器)通过kobject连接起来,形成了一个树状结构. 它在源码中的定义为: /*<include/linux/kobject.h>*/ struct kobject

Linux shell脚本中shift的用法说明

Linux shell脚本中shift的用法说明 shift命令用于对参数的移动(左移),通常用于在不知道传入参数个数的情况下依次遍历每个参数然后进行相应处理(常见于Linux中各种程序的启动脚本). 示例1:依次读取输入的参数并打印参数个数: run.sh: #!/bin/bash while [ $# != 0 ];do echo "第一个参数为:$1,参数个数为:$#" shift done 输入如下命令运行:run.sh a b c d e f 结果显示如下: 第一个参数为:a

eImage(仅两行代码实现输出从数据库中查询到的二进制字段)标签

功能: 专门用于向浏览器输出从数据库中查询到的二进制字段.支持通用的几十种文件类型 别名为edoc 使用方法: <chtml><eimage id=书包名type=类型>key</eimage></chtml>或 <chtml><edocid==书包名 type=类型>key</edoc></chtml> ·        id:书包名.ESql标签返回的书包名称 ·        type:类型.输出文件类型

[书接上一回]在Oracle Enterprise Linux (v5.7) 中安装DB - (4/4)

选择自己创建的安装数据库路径. Sample Schemas 打钩. 调整内存大小. 选择官方建议的字符集编码. 是否创建创建的脚本,如需要请打钩. 脚本生成成功. 创建成功,如需要,则可以管理数据库中用户的密码. 使用数据库. 此时,从虚拟机中,安装OEL系统到安装Oracle数据库全部完结. 以上的资料都从网络寻找,如查询未果,请联系我,我可以发以上需要的资源. [好好学习]在VMware中安装Oracle Enterprise Linux (v5.7) - (1/5)http://www.