|  |  |  |  |  |  |  | 243 struct softirq_action |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  | 244 { |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  | 245         void    (*action)(struct softirq_action *); |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  | 246         void    *data; |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  | 247 }; |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  | struct softirq_action softirq_vec[32]; |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  | local_softirq_pending |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  | softirq_vec |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  | 0 | 1 | 2 | …… |  | 30 | 31 |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  | h |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  | 遍历softirq_vec数组,依次执行action(data)函数 |  |  |  |  |  | 
|  |  |  | 229         do { |  |  |  |  |  | 其中有两个比较特殊的地方0和6指定的函数为tasklet_hi_action(NULL)和tasklet_action(NULL) |  | 
|  |  |  | 230                 if (pending & 1) { |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  | 231                         trace_irq_softirq_entry(h, softirq_vec); |  |  |  |  |  |  |  |  | 
|  |  |  | 232                         h->action(h); |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  | 233                         trace_irq_softirq_exit(h, softirq_vec); |  |  |  |  |  |  |  |  | 
|  |  |  | 234                         rcu_bh_qsctr_inc(cpu); |  |  |  |  |  | 首先找到tasklet_vec每cpu变量链表的头 |  | 
|  |  |  | 235                 } |  |  |  |  |  |  |  |  | 然后赋予listwhile循环从list指向的地方开始执行 | 
|  |  |  | 236                 h++; |  |  |  |  |  |  |  |  | 同时把count=1的(锁住的)再次放回到tasklet_vec链表中去 | 
|  |  |  | 237                 pending >>= 1; |  |  |  |  |  |  |  | 处理完返回; |  |  |  |  | 
|  |  |  | 238         } while (pending); |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  | 遍历软中断数组softirq_vec |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 系统初始化期间start_kernel函数中调用softirq_init对软中断进行初始化。 |  |  |  |  |  |  |  |  |  |  |  |  | 
| 471 void __init softirq_init(void) |  |  |  |  |  |  | 329 void open_softirq(int nr, void (*action)(struct softirq_action*), void *data) |  |  |  |  | 
| 472 { |  | 6 |  |  |  |  |  |  | 330 { |  |  |  |  |  |  |  |  |  |  | 
| 473         open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL); |  |  |  | 331         softirq_vec[nr].data = data; |  |  |  |  |  |  |  | 
| 474         open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL); |  |  |  | 332         softirq_vec[nr].action = action; |  |  |  |  |  |  |  | 
| 475 } |  | 0 |  |  |  |  |  |  | 333 } |  |  |  |  |  |  |  |  |  |  | 
| 将数组strcut softirq_action softirq_vec[32]对应的[TASKLET_SOFTIRQ]和[HI_SOFTIRQ]设置好; |  |  |  |  |  |  |  |  |  |  | 
| 222 /* PLEASE, avoid to allocate new softirqs, if you need not _really_ high |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 223    frequency threaded job scheduling. For almost all the purposes |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 224    tasklets are more than enough. F.e. all serial device BHs et |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 225    al. should be converted to tasklets, not to softirqs. |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 226  */ |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 228 enum |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 229 { |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 230         HI_SOFTIRQ=0, |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 231         TIMER_SOFTIRQ, |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 232         NET_TX_SOFTIRQ, |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 233         NET_RX_SOFTIRQ, |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 234         BLOCK_SOFTIRQ, |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 235         BLOCK_IOPOLL_SOFTIRQ, |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 236         TASKLET_SOFTIRQ |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 237 }; |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 软中断还有扩展的余地:struct softirq_action softirq_vec[32];数组只用了前6项 |  |  |  |  |  |  |  |  |  |  |  | 
| 处理软中断的时机do_IRQ完成后 也就是说硬中断完成后,立马去执行软中断,在软中断处理函数中遍历10次softirq_vec数组处理激活的软中断。(__softirq_pending相应位为 | 1 |  |  | 
| 320 void fastcall raise_softirq(unsigned int nr) |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| /* |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| Bitmap Bitmap Bitmap Bitmap Bitmap Bitmap Bitmap Bitmap |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 321 { |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 323 |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 324         local_irq_save(flags); |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 325         raise_softirq_irqoff(nr); |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 326         local_irq_restore(flags); |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 327 } |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 激活软中断。所谓的激活什么? |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| #define __raise_softirq_irqoff(nr) do { or_softirq_pending(1UL << (nr)); } while (0) |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| #define or_softirq_pending(x)  (local_softirq_pending() |= (x)) | //找到本地CPU的per_cpu_irq_stat变量找出其中的成员__softirq_pending|=(1<<(nr)) 设相应位为1; |  |  |  |  |  |  |  | 
| 7 typedef struct { |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 12 } ____cacheline_aligned irq_cpustat_t; |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| DECLARE_PER_CPU(irq_cpustat_t, irq_stat); | extern irq_cpustat_t per_cpu_irq_stat; |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| do_softirq遍历softirq_vec数组 |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 443 void tasklet_init(struct tasklet_struct *t, |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| /* |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| Bitmap Bitmap Bitmap Bitmap Bitmap Bitmap Bitmap Bitmap |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 444                   void (*func)(unsigned long), unsigned long data) |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 445 { |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 446         t->next = NULL; |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 447         t->state = 0; |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 448         atomic_set(&t->count, 0); |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 449         t->func = func; |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 450         t->data = data; |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 451 } |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 336 struct tasklet_head |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 337 { |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 338         struct tasklet_struct *list; |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 339 }; |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 343 static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec) = { NULL }; |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 344 static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec) = { NULL }; |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 277 struct tasklet_struct |  |  |  |  |  | do_softirq会扫描这个数组 |  |  |  |  |  |  |  |  |  | 
| 278 { |  |  |  |  |  |  |  | softirq_vec |  |  |  |  |  |  |  |  |  |  |  | 
| 279         struct tasklet_struct *next; |  |  |  |  | 0 | 1 | 2 | …… |  | 6 | …… | 30 | 31 |  |  |  | 
| 280         unsigned long state; |  |  |  |  |  |  |  |  | 
| 281         atomic_t count; |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 282         void (*func)(unsigned long); |  |  |  |  | h |  |  |  |  |  |  |  | 
| 283         unsigned long data; | per-cpu variable |  |  |  |  |  |  |  |  |  |  |  | 
| 284 }; |  |  |  |  | … |  |  |  |  |  |  |  |  | 
|  |  | cpu1 |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  | … |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  | cpu0 |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  | t |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  | cpu1 |  | tasklet_struct | tasklet_struct | tasklet_struct |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  | NULL |  |  |  |  |  |  |  |  |  | 
|  |  | cpu0 |  | state |  | state |  | state |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  | count |  | count |  | count |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  | func |  | func |  | func |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  | data |  | data |  | data |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 286 #define DECLARE_TASKLET(name, func, data) \ |  | 443 void tasklet_init(struct tasklet_struct *t, |  |  |  |  |  |  |  |  |  |  | 
| 287 struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data } | /* |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 288 |  |  |  |  |  | Bitmap Bitmap Bitmap Bitmap Bitmap Bitmap |  | Bitmap Bitmap |  |  |  |  |  |  |  |  |  |  |  | 
| 289 #define DECLARE_TASKLET_DISABLED(name, func, data) \ | 444                   void (*func)(unsigned long), unsigned long data) |  |  |  |  |  |  |  |  |  |  |  | 
| 290 struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data } | 445 { |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  | 446         t->next = NULL; |  |  |  |  |  |  |  |  |  |  |  | 
| 静态的定义一个名字为name的truct tasklet_struct |  | 447         t->state = 0; |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  | 448         atomic_set(&t->count, 0); |  |  |  |  |  |  |  |  |  |  |  | 
| 323 static inline void tasklet_schedule(struct tasklet_struct *t) |  | 449         t->func = func; |  |  |  |  |  |  |  |  |  |  |  |  | 
| /* |  |  |  |  |  | 450         t->data = data; |  |  |  |  |  |  |  |  |  |  |  | 
| Bitmap Bitmap Bitmap Bitmap Bitmap Bitmap Bitmap Bitmap |  |  |  |  |  | 451 } | 动态的初始化一个tasklet_struct结构体,现在还有插入到taslet_vec或者tasklet_hi_let链表中 所以还没有机会执行 |  |  |  |  | 
| 324 { |  |  |  |  |  |  | 那么在哪里插入到tasklet_struct链表中的呢?tasklet_schedule()函数中 |  |  |  |  |  |  |  | 
| 325         if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) | //测试t指向的tasklet_struct是否已经在per-cpu链表中 |  |  |  |  |  |  |  |  |  | 
| 326                 __tasklet_schedule(t); |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 327 } |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 372 static void tasklet_action(struct softirq_action *a) |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| /* |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| Bitmap Bitmap Bitmap Bitmap Bitmap Bitmap Bitmap Bitmap |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 373 { |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 374         struct tasklet_struct *list; |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 375 |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 377         list = __get_cpu_var(tasklet_vec).list; |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 378         __get_cpu_var(tasklet_vec).list = NULL; |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 380 |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 382                 struct tasklet_struct *t = list; |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 383 |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 384                 list = list->next; |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 385 |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 386                 if (tasklet_trylock(t)) { |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 387                         if (!atomic_read(&t->count)) { |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 388                                 if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)) |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 391                                 t->func(t->data); |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 393                                 tasklet_unlock(t); |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 394                                 continue; |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 395                         } |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 396                         tasklet_unlock(t); |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 397                 } |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 398 |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 400                 t->next = __get_cpu_var(tasklet_vec).list; |  | //将count=1锁住的tasklet继续留在softirq_vec per-cpu变量中。 |  |  |  |  |  |  |  |  |  | 
| 401                 __get_cpu_var(tasklet_vec).list = t; |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 402                 __raise_softirq_irqoff(TASKLET_SOFTIRQ); |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 404         } |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 405 } |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 455 void tasklet_kill(struct tasklet_struct *t) |  |  |  |  |  |  | 346 void fastcall __tasklet_schedule(struct tasklet_struct *t) |  |  |  |  |  | 
| /* [previous][next][first][last][top][bottom][index][help] */ |  |  |  |  |  | /* [previous][next][first][last][top][bottom][index][help] */ |  |  |  |  |  | 
| 456 { |  |  |  |  |  |  |  |  |  | 347 { |  |  |  |  |  |  |  |  |  | 
| 457         if (in_interrupt()) |  |  |  |  |  |  |  | 348         unsigned long flags; |  |  |  |  |  |  |  | 
| 458                 printk("Attempt to kill tasklet from interrupt\n"); |  |  |  |  | 349 |  |  |  |  |  |  |  |  |  | 
| 459 |  |  |  |  |  |  |  |  |  | 350         local_irq_save(flags); |  |  |  |  |  |  |  | 
| 460         while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) { |  |  |  | 351         t->next = __get_cpu_var(tasklet_vec).list; |  |  |  |  |  |  | 
| 461                 do |  |  |  |  |  |  |  |  | 352         __get_cpu_var(tasklet_vec).list = t; |  |  |  |  |  |  | 
| 462                         yield(); |  |  |  |  |  |  |  | 353         raise_softirq_irqoff(TASKLET_SOFTIRQ); |  |  |  |  |  | 
| 463                 while (test_bit(TASKLET_STATE_SCHED, &t->state)); |  |  |  |  | 354         local_irq_restore(flags); |  |  |  |  |  |  |  | 
| 464         } |  |  |  |  |  |  |  |  | 355 } |  |  |  |  |  |  |  |  |  | 
| 465         tasklet_unlock_wait(t); |  |  |  |  |  |  |  | //将tasklet_struct插入到softirq_vec链表的第一个位置 |  |  |  |  |  | 
| 466         clear_bit(TASKLET_STATE_SCHED, &t->state); |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 467 } |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| //自杀不是他杀~ |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| static LIST_HEAD(workqueues); |  |  |  | 63 struct workqueue_struct { |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  | 64         struct cpu_workqueue_struct *cpu_wq; |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  | 65         const char *name; |  |  |  |  |  |  |  |  |  |  |  | 
| #define create_workqueue(name) __create_workqueue((name), 0) |  | 66         struct list_head list;  /* Empty if single thread */ |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  | 67 }; |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  | struct workqueue_struct *wq |  | alloc_percpu分配 |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  | struct cpu_workqueue_struct |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  | lock |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  | name |  | remove_sequeuece |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  | list |  | insert_sequeue |  | work_struct |  | work_struct |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  | pending |  | pending |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  | more_work |  |  |  | entry |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  | work_done |  | func |  | func |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  | wq |  | data |  | data |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  | thread |  | wq_data |  | wq_data |  |  |  | 
| struct workqueue_struct *wq |  |  |  |  |  |  |  |  |  |  | run_depth |  | timer |  | timer |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| struct list_head workqueues; |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| prev |  | cpu_wq |  | cpu_wq |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| next |  | name |  | name |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  | NULL |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 346         if (is_single_threaded(wq)) |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 347                 p = kthread_create(worker_thread, cwq, "%s", wq->name); |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 348         else |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 349                 p = kthread_create(worker_thread, cwq, "%s/%d", wq->name, cpu); |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 进程描述符中的thread_info字段中有一个32位的preempt_counter字段,0-7位为抢占计数器,用于记录显式禁用内核抢占的次数;8-15位为软中断计数器,记录可延迟函数被禁用的次数;16-27为硬中断计数器,表示中断处理程序的嵌套数(irq_enter()递增它,irq_exit()递减它);28位为PREEMPT_ACTIVE标志。只要内核检测到preempt_counter整体不为0,就不会进行内核抢占,这个简单的探测一下子保证了对众多不能抢占的情况的检测。 |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  | IRQ handler type mismatch for IRQ 0 |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  | [] setup_irq+0x176/0x18a |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  | [] irq_handle+0x0/0xc [per_cpu_3_1] |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  | [] request_irq+0x7c/0x98 |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  | [] hello_init+0x63f/0x64c [per_cpu_3_1] |  |  |  |  |  |  |  |  |  |  |  |  | 
|  | [] sys_init_module+0x1aed/0x1caa |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  | [] audit_syscall_entry+0x15a/0x18c |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  | [] syscall_call+0x7/0xb |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  | ======================= |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
| 调用setup_irq进行中断初始化时,需要手动的分配一个irqaction的结构体空间,并把irq_desc |  |  |  |  |  |  |  |  |  |  |  | 
| 中的action指针指向这个结构体。 |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | 
|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |