先看一个 简单的 PR
下面是别人的 PR
作者 Joe Perches 说 :
Possible speed improvement of __do_softirq() by using ffs() instead of
using a while loop with an & 1 test then single bit shift
的确是的,ffs() 底层使用了汇编的实现.
好,那就来欣赏 Joe Perches 的思想吧.
asmlinkage __visible void __softirq_entry __do_softirq(void)
{
/*
#define MAX_SOFTIRQ_TIME msecs_to_jiffies(2)
有一个平衡系统的思想在这里面,
即 是否该需要唤醒 ksoftirqd 来进行处理软中断了.
什么时候需要 wake_up_process 这个per-cpu的内核线程 来处理,
在这里做了第一个上限条件
*/
unsigned long end = jiffies + MAX_SOFTIRQ_TIME;
/*
获取该TASK的标志.
....
#define PF_SIGNALED 0x00000400 /* Killed by a signal */
#define PF_MEMALLOC 0x00000800 /* Allocating memory */
下面会调用 current_restore_flags() 来更新存储该TASK的 flags.
*/
unsigned long old_flags = current->flags;
/*
和上面的 MAX_SOFTIRQ_TIME 一样,也是决定是否唤醒 ksoftirqd 的条件之一
ksoftirq 不到一定的程度 它不会轻易出手的. 😄 哈哈
*/
int max_restart = MAX_SOFTIRQ_RESTART;
/*
指向软中断描述符数组呗.
你应该记得 irq_desc[] 吧,这里还挺有对称性
不过 已经使用 radix tree 来表述 irq_desc 是更好的一种选择.
好,既然是一个 数组,
那么下面在处理的时候 却没有保护机制来保护它 是每个 per cpu 变量 ?
当然不需要保护的,下面在说明.
*/
struct softirq_action *h;
/*
trace 框架的一些逻辑,暂不分析.
*/
bool in_hardirq;
/*
存储有 __softirq_pending 的值.
什么时候会将 __softirq_pending 的某位置 1
这是softirq的框架问题,如 raise_softirq().
下面在进行具体处理的时候再说,这只是定义一下,暂不做过多的解释.
*/
__u32 pending;
/*
你应该知道了这个是局部变量是想要干什么了吧
no value !
*/
int softirq_bit;
/*
上面的变量已经定义完成了,下面来看它的核心部分
*/
/*
#define PF_MEMALLOC 0x00000800 /* Allocating memory */
屏蔽该Task 标记位: bit[11]
why ?
研究中 ...
*/
current->flags &= ~PF_MEMALLOC;
/*
查看是否有待处理的软中断, 干脆吧 !
如果使用 要用一个 delay flip-flop (即 Register ) 来替代这个 __softirq_pending
不使用 register 而是使用一个bitmap, 这样的架构方式也看出来softirq的地位
它并不像 IAR register 一样可以从中直接读取触发 Interrupt的 hwirq.
也许这就是下半部分对时间要求并不像 上半部分那样严格的一个例证吧 .
好了,问题出来了: 为什么不加锁来保护它
因为 它是 一个是per_cpu变量, 这是 Linux Kernel 的一个很好的特性.
看 :
struct irq_stat {
unsigned int irqs[NR_ARCH_IRQS];
};
DECLARE_PER_CPU_SHARED_ALIGNED(struct irq_stat, irq_stat);
#define local_softirq_pending_ref irq_stat.__softirq_pending
*/
pending = local_softirq_pending();
/*
一些时间的统计.
1 : 统计哪些时间呢 ?
2 : 为什么要在软中断中来进行这些时间的处理呢 ?
是否有这个必要 ?
3 : 与 时钟中断中的时间处理有哪些不同 ?
好.
1 : 为进程添加系统时间 why ?
p->stime += cputime;
2 : Add system time to cpustat
3 : Account for system time used
研究中 ...
*/
account_irq_enter_time(current);
/*
软中断数量 + 1.
它最多能增加到 256 ,
因为 bit[8:16] 表示软中断的数量.
不论是 bit[8:16] softirq
bit[17:20] hardirq
还是 bit[20] NMI
只要 preempt_count != 0 就关闭了 kernel 抢占.
为什么要关闭抢占 ?
中断上下文(hardirq context && softirq context ) 不允许被切换出去
为什么不允许被切换出去 ?
因为 它不是一个 调度实体 (可以想想什么是调度实体), CFS 管不了它呀 !
bit[17:20] 是没有意义的,因为hardirq不允许中断嵌套了.
一句话 :
The hardirq count could in theory be the same as the number of interrupts in the system
kernel中最强大的莫过于 NMI, 没有什么可以阻挡 !!!
*/
__local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);
/*
关于trace这块水还挺深 !
*/
in_hardirq = lockdep_softirq_start();
/*
你终于来了 !
为什么这里需要架构成 restart 处理
无非是softirq处理后 又有softirq被触发了,__soft_pending 又被置位了 还需要处理.
什么是hardirq ?
它就是系统中的一个事件!(Intel manual 中明确定义)
什么是softirq ?
它就是该 Event 处理的延续 !(我自己的理解 😄 哈哈)
*/
restart:
/*
为什么要清空 __softirq_pending
因为下面紧接着要enable hardirq,hardirq可能会被触发, 又有可能置位 __softirq_pending
且 pending 已经保存了 __softirq_pending 的值.
*/
set_softirq_pending(0);
/*
这一句话,改变了整个运行的环境 !
攘外必先安内策略历史上并没有成功吧,又要处理内部的又要盯着外部的,
在执行软中断的过程中(即 下面的 while 循环)是 enable hardirq的,处理完成后,又会再次disable hardirq,
深刻体会 hardirq 是要警惕的, 因为它会带来很多不确定性的东西, Kernel 中的搅局者 !
*/
local_irq_enable();
/*
好了,下面会按照优先级从高到底的顺序依次处理了.
看:
static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;
它是一个 全局的. 所有CPU共享的.
这能说明什么 ?
*/
h = softirq_vec;
}