preempt_count本质上是一个per-CPU的32位变量

,它在各种处理器架构下的存放位置和命名不尽相同,但其值都可以使用preempt_count()函数统一获取。preempt_count逻辑相关的核心代码位于include/linux/preempt.h,虽然只是一个32位变量,但由于其和中断、调度/抢占密切相关,因此在系统中发挥的作用不容小觑。

来看下preempt_count是怎样构成的:

hardirq相关

preempt_count中的第16到19个bit表示hardirq count,它记录了进入hardirq/top half的嵌套次数,在这篇文章介绍的do_IRQ()中,irq_enter()用于标记hardirq的进入,此时hardirq count的值会加1。irq_exit

()用于标记hardirq的退出,hardirq count的值会相应的减1。如果hardirq count的值为正数,说明现在正处于hardirq上下文中,代码中可借助in_irq()宏实现快速判断。注意这里的命名是"in_irq"而不是"in_hardirq"。

#define hardirq_count()   (preempt_count() & HARDIRQ_MASK)
#define in_irq()  (hardirq_count())

hardirq count占据4个bits,理论上可以表示16层嵌套,但现在Linux系统并不支持hardirq的嵌套执行,所以实际使用的只有1个bit。

之所以采用4个bits,一是历史原因,因为早期Linux并不是将中断处理的过程分为top half和bottom half,而是将中断分为fast interrupt handler和slow interrupt handler,而slow interrupt handler是可以嵌套执行的,二是某些 driver 代码可能在top half中重新使能hardirq。

softirq相关

preempt_count中的第8到15个bit表示softirq count

,它记录了进入softirq的嵌套次数,如果softirq count的值为正数,说明现在正处于softirq上下文中。由于softirq在单个CPU上是不会嵌套执行的,因此和hardirq count一样,实际只需要一个bit(bit 8)就可以了。但这里多出的7个bits并不是因为历史原因多出来的,而是另有他用。

这个"他用"就是表示在进程上下文中,为了防止进程被softirq所抢占,关闭/禁止softirq的次数,比如每使用一次local_bh_disable(),softirq count高7个bits(bit 9到bit 15)的值就会加1,使用local_bh_enable()则会让softirq count高7个bits的的值减1。

代码中可借助in_softirq()宏快速判断当前是否在softirq上下文:

#define softirq_count()  (preempt_count() & SOFTIRQ_MASK)
#define in_softirq()     (softirq_count())

这篇文章曾提到:进入softirq是在softirq上下文,关闭softirq抢占也是在softirq上下文,但还是有办法区分的。办法就是使用in_serving_softirq()宏来确切地表示现在是在处理softirq。

#define SOFTIRQ_OFFSET  (1UL << 8)
#define in_serving_softirq()  (softirq_count() & SOFTIRQ_OFFSET)

上下文

不管是hardirq上下文还是softirq上下文,都属于我们俗称的中断上下文(interrupt context)。

为此,有一个名为in_interrupt()的宏专门用来判断当前是否在中断上下文中。

#define irq_count()   (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK | NMI_MASK))
#define in_interrupt()  (irq_count())

与中断上下文相对应的就是俗称的进程上下文(process context)

#define in_task()  (!(preempt_count() & (HARDIRQ_MASK | SOFTIRQ_OFFSET | NMI_MASK)))

需要注意的是,并不是只有进程才会处在process context,内核线程

依然可以运行在process context。

在中断上下文中,调度是关闭的,不会发生进程的切换,这属于一种隐式的禁止调度,而在代码中,也可以使用preempt_disable

()来显示地关闭调度,关闭次数由第0到7个bits组成的preemption count(注意不是preempt count)来记录。每使用一次preempt_disable(),preemption count

的值就会加1,使用preempt_enable()则会让preemption count的值减1。preemption count占8个bits,因此一共可以表示最多256层调度关闭的嵌套。

处于中断上下文,或者显示地禁止了调度,preempt_count()的值都不为0,都不允许睡眠/调度的发生,这两种场景被统称为atomic上下文,可由in_atomic()宏给出判断。

#define in_atomic()  (preempt_count() != 0)

中断上下文、进程上下文和atomic上下文的关系大概可以表示成这样:

/** low level task data that entry.S needs immediate access to.* __switch_to() assumes cpu_context follows immediately after cpu_domain.*/
struct thread_info {unsigned long       flags;      /* low level flags */mm_segment_t       addr_limit; /* address limit */struct task_struct   *task;      /* main task structure */struct exec_domain *exec_domain;   /* execution domain */struct restart_block  restart_block;int           preempt_count;  /* 0 => preemptable, <0 => bug */int         cpu;        /* cpu */
};

在支持可抢占的系统中,一个进程的thread_info信息定义如上。其中preempt_count代表的是该进程是否可以被抢占,根据注释的说明当peermpt_count等于0的时候当前进程可以被抢占,当小于0存在bug,当大于0说明当前进程不可以被抢占。比如当前进程在中断上下文中或者使用了锁。

<linux/include/preempt_mask.h>
------------------------------------------
/** 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 could in theory be the same as the number of* interrupts in the system, but we run all interrupt handlers with* interrupts disabled, so we cannot have nesting interrupts. Though* there are a few palaeontologic drivers which reenable interrupts in* the handler, so we need more than one bit here.** PREEMPT_MASK:   0x000000ff* SOFTIRQ_MASK:   0x0000ff00* HARDIRQ_MASK:   0x000f0000*     NMI_MASK:   0x00100000* PREEMPT_ACTIVE: 0x00200000*/
#define PREEMPT_BITS    8
#define SOFTIRQ_BITS    8
#define HARDIRQ_BITS    4
#define NMI_BITS    1

结合上述的示图和代码的定义可知,bit0-7代表的是抢占的次数,最大抢占深度为256次,bit8-15代表的是软中断的次数,最大也是256次,bit16-19表示中断的次数,注释的大概意思是避免中断嵌套,但是也不能防止某些驱动中断嵌套使用中断,所以嵌套16层也是最大次数了。bit20代表的NMI中断,bit21代表当前抢占是否active。

Linux系统为了方便得出各个字段的值,提供了一系列宏定义如下:

#define PREEMPT_SHIFT    0
#define SOFTIRQ_SHIFT   (PREEMPT_SHIFT + PREEMPT_BITS)                        //0+8=8
#define HARDIRQ_SHIFT   (SOFTIRQ_SHIFT + SOFTIRQ_BITS)                        //8+8=16
#define NMI_SHIFT   (HARDIRQ_SHIFT + HARDIRQ_BITS)                        //16+4=20#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)#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT)                //1<<0
#define SOFTIRQ_OFFSET  (1UL << SOFTIRQ_SHIFT)                //1<<8
#define HARDIRQ_OFFSET  (1UL << HARDIRQ_SHIFT)                //1<<16
#define NMI_OFFSET  (1UL << NMI_SHIFT)                    //1<<20#define SOFTIRQ_DISABLE_OFFSET (2 * SOFTIRQ_OFFSET)           //16#define PREEMPT_ACTIVE_BITS  1
#define PREEMPT_ACTIVE_SHIFT    (NMI_SHIFT + NMI_BITS)
#define PREEMPT_ACTIVE  (__IRQ_MASK(PREEMPT_ACTIVE_BITS) << PREEMPT_ACTIVE_SHIFT)#define hardirq_count()  (preempt_count() & HARDIRQ_MASK)                                     //硬中断count
#define softirq_count() (preempt_count() & SOFTIRQ_MASK)                                     //软中断count
#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK \| NMI_MASK))                                                  //所有中断=硬+软+NMI

从上述的定义可以得出,如果想知道硬中断的次数就使用hardirq_count,如果想知道中断次数就使用softirq_count,如果想知道所有中断的次数就使用irq_count。

/** 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)

其中in_irq用于判断当前进程是否在硬中断中,in_softirq用于判断是否当前进程在软件中断或者有别的进程disable了软中断,in_interrupt用于判断当前进程是否在中断中,而in_serving_softirq用于判断当前进程是否在软件中断中,通过bit8这一位来判断。

#define in_atomic()  ((preempt_count() & ~PREEMPT_ACTIVE) != 0)

这个宏可以判断当前进程是否处于原子操作中。

Linux中的preempt_count相关推荐

  1. linux中的SGI(核间中断)IPI_RESCHEDULE详解

    1.SGI中断(核间通信中断) 在gicv2/gicv3中,SGI中断(中断号0-15)是software generate interrupt,用户核间中断. 我们一般将0-7划分给linux中使用 ...

  2. linux 内核页表 tlb,Linux中的mips64 tlb管理

    关于tlb的描述可参考mips run2以及mips64官方手册. tlb条目:tlb entry 寄存器:entryHi, entryLo0, entryLo1, mask, index, wire ...

  3. c++ 进程快照_如何在 Linux 中找出内存消耗最大的进程

    很多次,你可能遇见过系统消耗了过多的内存.如果是这种情况,那么最好的办法是识别出 Linux 机器上消耗过多内存的进程. -- Magesh Maruthamuthu(作者) 很多次,你可能遇见过系统 ...

  4. linux的tar中ztvf,linux中的tar命令(2)

    实例4:只将 /tar 内的 部分文件解压出来 命令: tar -zxvf /opt/soft/test/log30.tar.gz log2013.log 输出: [root@localhost te ...

  5. centos6.5 php5.2,Linux中PHP安装与配置(CentOS-6.5:php-5.2.13)

    1 PHP简介     PHP(PHP: Hypertext Preprocessor的缩写,中文名:"超文本预处理器")是一种通用开源脚本语言.语法吸收了C语言.Java和Per ...

  6. java 外部类似_[求指点] 如何用java 实现类似linux中管道调用外部程序的功能

    想写个小程序实现类似linux中管道的功能,创建一个外部子进程,然后主进程不断地写输入给子进程,而后把子进程的返回值取出. 如下的小代码就是从stdin读入一个字符串,调用子进程(cat)返回这个串, ...

  7. linux 修改java版本_Linux 有问必答:如何在 Linux 中改变默认的 Java 版本

    提问:当我尝试在Linux中运行一个Java程序时,我遇到了一个错误.看上去像程序编译所使用的Java版本与我本地的不同.我该如何在Linux上切换默认的Java版本? 当Java程序编译时,编译环境 ...

  8. linux ls 命令 路径,使用ls命令在Linux中使用完整路径列出文件

    许多人会发现,这是重复的问题,但我已经经历了所有问题,然后问及这个主题,但没有为我工作.使用ls命令在Linux中使用完整路径列出文件 我想打印使用ls命令的特定文件格式的完整路径名到目前为止,我发现 ...

  9. linux中的ln属性,linux 常用基础命令 ln 详细介绍

    命令介绍: ln是linux中又一个非常重要命令,ln是为某一个文件在另外一个位置建立一个同步的链接.当我们需要在不同的目录,用到相同的文件时,我们不需要在每一个需要的目录下都放一个必须相同的文件,我 ...

  10. linux内核定义的常用信号6,Linux中的信号

    在 Linux 中,理解信号的概念是非常重要的.这是因为,信号被用于通过 Linux 命令行所做的一些常见活动中.例如,每当你按 Ctrl+C 组合键来从命令行终结一个命令的执行,你就使用了信号.每当 ...

最新文章

  1. 干掉visio,这个画图神器真的绝了!!!
  2. PostgreSQL的xlog实验一
  3. 【知识积累】C#中结构体和类的区别
  4. ad16不能去除铺铜_净水处理-原水中的二氧化硅、铜和铁对锅炉运行的影响
  5. 了解零信任-SDP关系
  6. .net core ——微服务内通信Thrift和Http客户端响应比较
  7. Android pm 命令详解
  8. Flink的ProcessFunction API
  9. 那天,他无意间瞟了眼程序员的桌面……
  10. rebots css,我的robots.txt中涉及到.htaccss文件的奇怪https/http错误
  11. ai python 代码提示插件_Python 还能实现哪些 AI 游戏?附上代码一起来一把!
  12. c语言求素数_C语言 | 求100~200的素数
  13. 解决flash的虚框问题
  14. 边工作边刷题:70天一遍leetcode: day 26
  15. java调用linux系统命令_java 调用linux系统命令
  16. springboot+vue汽车4S店车辆销售维修管理系统java源码
  17. 中继器、集线器、网桥、交换机、路由器、网关的超全总结
  18. 如何创建Roadmap产品路线图
  19. 三年半经验,蚂蚁头条快手怎么选?网友:第一次看到头条比快手offer低的
  20. 服务器搭建网站完整教程

热门文章

  1. Log Parser 2.2 + Log Parser Lizard GUI 分析IIS日志示例
  2. Math.h 正态分布 C语言,C++与正态分布(示例代码)
  3. 联想y9000x做java_联想Y9000X装win7系统及BIOS设置教程(支持9代)
  4. html游戏加载不出图片吗,uc浏览器加载不出图片怎么办?uc浏览器加载不出图片的解决方法...
  5. 奋斗在美国湾区,码农的生活
  6. $.ajax返回报错,请求ajax报错 返回readyState0
  7. 神威太湖之光之超级计算机,中国研发的“神威·太湖之光”,超级计算机能给我们带来什么?...
  8. 计算机表格数据怎么样汇总,Excel 中如何汇总相同结构的多个工作表数据
  9. 牛客输入输出(依图科技)
  10. 机器视觉打光方式相关问题汇总