福州企业做网站,开发软件难吗,wordpress如何把导航栏一直固定,深圳正规网站开发团队Linux中的preempt_count - 知乎 https://www.cnblogs.com/hellokitty2/p/15652312.html LWN#xff1a;关于preempt_count()的四个小讨论#xff01;-CSDN博客 主要是参考这些文章 之前一直认为只要是in_interrupt()返回非0值#xff0c;那么就可以认为当前在中断上下文。即… Linux中的preempt_count - 知乎 https://www.cnblogs.com/hellokitty2/p/15652312.html LWN关于preempt_count()的四个小讨论-CSDN博客 主要是参考这些文章 之前一直认为只要是in_interrupt()返回非0值那么就可以认为当前在中断上下文。即使中一个内核线程里面in_interrupt返回非0值比如local_bh_disable
但是最近又有另外一种说法in_interrupt()in_softirq()这两个只有是真正的中断上半部以及软中断上下文才会返回非0值。在进程上下文中使用这两个函数一定会返回false或者是不应该在一个内核线程里面调用我这句话说的不太准确具体是啥记不清楚了。
其实我之前理解类似于in_interrupt这些函数最终都是通过preempt count来判断的。只要这变量的值不为0那就是非进程上下文并不会因为调用的地方实际是在一个内核线程里面还是软中断处理流程里面而改变。只要是改变了preempt count的值那就等同于改变了当前代码的一个上下文。
因此准备重新回顾一下preempt count的作用。 在像 Linux 这样的多任务系统中没有任何一个线程可以保证它每次想运行的时候都能独占处理器。内核总是有能力多数情况下抢占一个正在运行的线程而选择一个优先级更高的线程来执行。那个新线程可能是另一个不同的进程但也可能是一个硬件中断或者什么其他外部事件。为了正确地协调系统中所有任务能正确运行内核必须跟踪当前的执行状态execution state包括已经被抢占或可能阻止线程被抢占的各种情况。 用来进行这个追踪记录的基础就是在系统中每个任务里存储的 preemption counter。 这个 counter 可以用来指示当前线程的状态、它是否可以被抢占以及它是否被允睡眠。要实现这个功能的话就必须在这个 counter 里面记录若干种不同状态因此这个 preempt_count 也被分成了几个字段sub-fields #define PREEMPT_BITS 8
#define SOFTIRQ_BITS 8
#define HARDIRQ_BITS 4
#define NMI_BITS 1 preempt_count 这个成员被用来判断当前进程是否可以被抢占。如果 preempt_count 不等于0可能是代码调用preempt_disable显式的禁止了抢占也可能是处于中断上下文等说明当前不能进行抢占如果 preempt_count 等于0说明已经具备了抢占的条件。 我记得书上还有一种说法preempt_count可以看做当前进程加锁的次数。当该进程准备让出cpu时需要检查这个值是否为0。不能在只有锁的情况下进程任务调度(好像也不是很准确哈休眠锁不就可以在加锁情况下调度嘛) preemption disable count(低8bit)用于记录当前进程被显示禁用抢占的次数preempt_disable调用次数。最多嵌套调用2^8次
下面代码假设内核支持抢占。可以看到preempt_disable就是修改的最后一个字节
#define preempt_disable() \
do { \preempt_count_inc(); \barrier(); \
} while (0)#define __preempt_count_inc() __preempt_count_add(1)static __always_inline void __preempt_count_add(int val)
{*preempt_count_ptr() val;
}static __always_inline int *preempt_count_ptr(void)
{return current_thread_info()-preempt_count;
}
softirq_count:preempt_count中的第8到15个bit表示softirq count它记录了进入softirq的嵌套次数。
可以看到在进入软中断时。就会先标识其已经进入了软中断上下文 __local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);其修改的就是bit8开始的内容。
smlinkage __visible void __do_softirq(void)
{
..........................__local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);
.....................
}
#define PREEMPT_OFFSET (1UL PREEMPT_SHIFT)
#define SOFTIRQ_OFFSET (1UL SOFTIRQ_SHIFT)
#define HARDIRQ_OFFSET (1UL HARDIRQ_SHIFT)
#define NMI_OFFSET (1UL NMI_SHIFT)
static __always_inline void __local_bh_disable_ip(unsigned long ip, unsigned int cnt)
{preempt_count_add(cnt);barrier();
}
在同一个cpu上软中断是串行执行的。同一个软中断可以在不同的cpu上同时执行tasklet除外。因此如果只是为了标识是不是正在处理软中断1个bit就行了。那么其他7个bit是干什么的呢 另外的7bit为了记录防止进程被softirq所抢占关闭/禁止softirq的次数比如每使用一次local_bh_disable()softirq count高7个bits(bit 9到bit 15)的值就会加1使用local_bh_enable()则会让softirq count高7个bits的的值减1。 可以看到local_bh_disable其实是修改的bit10开始的内容。因此另外7个bit可以认为是显示禁止软中断的嵌套次数
static inline void local_bh_disable(void)
{__local_bh_disable_ip(_THIS_IP_, SOFTIRQ_DISABLE_OFFSET);
}#define SOFTIRQ_DISABLE_OFFSET (2 * SOFTIRQ_OFFSET)
因此为了分清楚是正在处理软中断还是说进程里面显示禁止了软中断我们只需要判断bit8就行(进入softirq是在softirq上下文关闭softirq抢占也是在softirq上下文如何区分)
//这个能够判断是否是正在处理软中断
#define in_serving_softirq() (softirq_count() SOFTIRQ_OFFSET)#define SOFTIRQ_OFFSET (1UL SOFTIRQ_SHIFT)// 1 8#define SOFTIRQ_SHIFT (PREEMPT_SHIFT PREEMPT_BITS)// 0 8#define PREEMPT_BITS 8
#define PREEMPT_SHIFT 0 hardirq count4bit用于表示硬件中断嵌套的次数最多可以嵌套16层参考的文章说现在linux不支持中断嵌套。当进入硬件中断时会将其1退出-1。
#define __irq_enter() \do { \account_irq_enter_time(current); \preempt_count_add(HARDIRQ_OFFSET); \trace_hardirq_enter(); \} while (0)
中断上下文不管是hardirq和softirq都称为中断上下文。下图是抄自知乎。 其中正在处理软中断进入do_softirq或者是关闭softirq抢占local_bh_enable都属于软中断上下文。
正在处理中断上半部属于hardirq上下文关闭中断属于中断上下文吗感觉不是呢local_irq_disable不会去修改preempt count的值呢不理解这个是什么意思
因此对于中断上下文的判断就是判断非bit0到bit7的值是否为非0
#define in_interrupt() (irq_count())#define irq_count() (preempt_count() (HARDIRQ_MASK | SOFTIRQ_MASK \| NMI_MASK))//就是判断高bit8-bit20是否为非0非0就是在中断上下文 是否在软中断上下文和硬中断上下文同样也是判断对应的值
#define hardirq_count() (preempt_count() HARDIRQ_MASK)
#define softirq_count() (preempt_count() SOFTIRQ_MASK)
进程上下文判断我的内核代码里面没有呢当前的内核版本是3.16后面下了个5.4的里面就有这个了。
#define in_task() (!(preempt_count() (HARDIRQ_MASK | SOFTIRQ_OFFSET | NMI_MASK))) 另外我感觉就是判断低8bit的值是否为0呢。如果我在一个进程里面显示禁用抢占那in_task不就返回0了嘛这个难道不是进程上下文后面找一个支持抢占的内核试试