首页 > 代码库 > linux内核学习:中断中推后执行的部分

linux内核学习:中断中推后执行的部分

软中断-softirq

特点

  • 相同和不同的软中断都可以在不同处理器上同时执行
  • 一个软中断不会抢占另一个软中断

何时执行

  • 从中断程序返回时
  • ksoftirqd线程中
  • 显示调用

软中断最多有32个,一个32位的整型数据可以被用来标记刮起的软中断

使用策略

软中断应用于确实需要的场合,目前只有网络驱动和SCSI驱动中使用。另外,内核定时器和tasklet建立在软中断之上。

使用方法

注册软中断

void open_softirq(int nr, void (*action)(struct softirq_action *))

nr 静态指定于 linux/interrupt.h ,例如:

enum
{
 HI_SOFTIRQ=0,
 TIMER_SOFTIRQ,
 NET_TX_SOFTIRQ,
 NET_RX_SOFTIRQ,
 BLOCK_SOFTIRQ,
 BLOCK_IOPOLL_SOFTIRQ,
 TASKLET_SOFTIRQ,
 SCHED_SOFTIRQ,
 HRTIMER_SOFTIRQ,
 RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */
 NR_SOFTIRQS
 };
action为响应函数。

触发软中断

void raise_softirq(unsigned int nr);

会标记某个软中断为挂起状态,以便在何时的时机执行响应函数。

tasklet

关键数据

struct tasklet_struct
 {
 struct tasklet_struct *next;
 unsigned long state;
 atomic_t count;
 void (*func)(unsigned long);
 unsigned long data;
 };

执行原理

连个软中断 HI_SOFTIRQ和 TASKLED_SOFTIRQ,每个下面挂这一个tasklet链表,执行相应软中断的时候,会遍历这两个链表,执行可以执行的tasklet。

所以说tasklet其实是softirq的复用。

使用

DECLARE_TASKLET(name, func, data)
DECLARE_TASKLET_DISABLED(name, func, data)
void tasklet_schedule(struct tasklet_struct *t)
void tasklet_disable(struct tasklet_struct *t)
void tasklet_enable(struct tasklet_struct *t)

ksoftirqd

软中断辅助处理线程,每个CPU核心有一个,nice值为19,优先级最低。

 工作队列

 特点

每个处理器核心对应一个内核线程。

关键数据

struct workqueue_struct {
 struct list_head        pwqs;           /* WR: all pwqs of this wq */
 struct list_head        list;           /* PL: list of all workqueues */
 
 struct mutex            mutex;          /* protects this wq */
 int                     work_color;     /* WQ: current work color */
 int                     flush_color;    /* WQ: current flush color */
 atomic_t                nr_pwqs_to_flush; /* flush in progress */
 struct wq_flusher       *first_flusher; /* WQ: first flusher */
 struct list_head        flusher_queue;  /* WQ: flush waiters */
 struct list_head        flusher_overflow; /* WQ: flush overflow list */
 
 struct list_head        maydays;        /* MD: pwqs requesting rescue */
 struct worker           *rescuer;       /* I: rescue worker */
 
 int                     nr_drainers;    /* WQ: drain in progress */
 int                     saved_max_active; /* WQ: saved pwq max_active */
 
 struct workqueue_attrs  *unbound_attrs; /* WQ: only for unbound wqs */
 struct pool_workqueue   *dfl_pwq;       /* WQ: only for unbound wqs */
 
 #ifdef CONFIG_SYSFS
 struct wq_device        *wq_dev;        /* I: for sysfs interface */
 #endif
 #ifdef CONFIG_LOCKDEP
 struct lockdep_map      lockdep_map;
 #endif
 char                    name[WQ_NAME_LEN]; /* I: workqueue name */
 
 /* hot fields used during command issue, aligned to cacheline */
 unsigned int            flags ____cacheline_aligned; /* WQ: WQ_* flags */
 struct pool_workqueue __percpu *cpu_pwqs; /* I: per-cpu pwqs */
 struct pool_workqueue __rcu *numa_pwq_tbl[]; /* FR: unbound pwqs indexed by node */
 };
struct work_struct {
 atomic_long_t data;
 struct list_head entry;
 work_func_t func;
 #ifdef CONFIG_LOCKDEP
 struct lockdep_map lockdep_map;
 #endif
 };

使用

创建

DECLARE_WORK(n, f)
INIT_WORK

处理函数

void work_handler(void *)

触发

schedule_work(&work)
schedule_delayed_work(&work, delay)

 

比较

 

  软中断 tasklet 工作队列
并发性 同一软中断可同时运行于不同处理器 不同处理器同时只能运行不同的tasklet  
是否可阻塞 N N Y