《Linux内核设计与实现》学习【4】—— 中断

1 上/下半部

将中断处理程序分为两部分:
  1)上半部:做严格时限的工作
  2)下半部:允许稍后执行的工作

2 使用

注册

/*
 * irg     - 表示要分配的中断号
 * handler - 实际的中断处理程序
 * flags   - 标志位,表示此中断的具有特性
 * name    - 中断设备名称的ASCII 表示,这些会被/proc/irq和/proc/interrupts文件使用
 * dev     - 用于共享中断线,多个中断程序共享一个中断线时(共用一个中断号),依靠dev来区别各个中断程序
 * 返回值:
 * 执行成功:0
 * 执行失败:非0
 */
int request_irq(unsigned int irq,
                irq_handler_t handler,
                unsigned long flags,
                const char* name,
                void *dev)

释放

void free_irq(unsigned int irq, void *dev)

3 上半部流程

在这里插入图片描述

4 下半部

  下半部的3种机制:软中断、tasklet和工作队列。

4.1 软中断

  在编译期间静态分配,不能动态注册或注销
  软中断由softirq_action表示。

struct softirq_action
{
	void	(*action)(struct softirq_action *);
};

  在kernel/softirq.c中,维护了softirq_action的数组

static struct softirq_action softirq_vec[NR_SOFTIRQS];

  在include/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 open_softirq(int nr, void (*action)(struct softirq_action *))
{
    softirq_vec[nr].action = action;
}

  触发软中断

void raise_softirq(unsigned int nr)

  之后将在合适的时间执行,在下列地方检查和执行:
  1)从一个硬中断返回时
  2)在ksoftirqd内核线程中被执行
  3)在那些显式检查或执行待处理软中断的代码中,如网络子系统中
  最终都要在do_softirq()中执行

asmlinkage void do_softirq(void)
{
	__u32 pending;
	unsigned long flags;

	if (in_interrupt())//在中断上下文中,或者是在禁止了软中断后调用了
		return;

	local_irq_save(flags);

	pending = local_softirq_pending();//待处理软中断的位图

	if (pending)
		__do_softirq();

	local_irq_restore(flags);
}

asmlinkage void __do_softirq(void)
{
	struct softirq_action *h;
	...
	pending = local_softirq_pending();
	...
	h = softirq_vec;
	do {
		if (pending & 1) {//对应标志位被设置了
			int prev_count = preempt_count();
			kstat_incr_softirqs_this_cpu(h - softirq_vec);

			trace_softirq_entry(h, softirq_vec);
			h->action(h);                       //执行action
			trace_softirq_exit(h, softirq_vec);
			if (unlikely(prev_count != preempt_count())) {
				printk(KERN_ERR "huh, entered softirq %td %s %p"
				       "with preempt_count %08x,"
				       " exited with %08x?\n", h - softirq_vec,
				       softirq_to_name[h - softirq_vec],
				       h->action, prev_count, preempt_count());
				preempt_count() = prev_count;
			}

			rcu_bh_qs(cpu);
		}
		h++;
		pending >>= 1;
	} while (pending);
	...
}

4.2 tasklet

  通过软中断实现,两类:HI_SOFTIRQ和TASKLET_SOFTIRQ
  初始化时,start_kernel()–>softirq_init

void __init softirq_init(void)
{
	...
	open_softirq(TASKLET_SOFTIRQ, tasklet_action);
	open_softirq(HI_SOFTIRQ, tasklet_hi_action);
}

  tasklet由tasklet_struct结构体表示

struct tasklet_struct
{
    struct tasklet_struct *next; /* 链表中的下一个tasklet */
    unsigned long state;         /* tasklet状态 */
    atomic_t count;              /* 引用计数器 */
    void (*func)(unsigned long); /* tasklet处理函数 */
    unsigned long data;          /* tasklet处理函数的参数 */
};

  已调度的tasklet存放在单处理器数据结构:tasklet_vec和tasklet_hi_vec
  调度:tasklet_schedule()和tasklet_hi_schedule()
  tasklet_schedule()–>__tasklet_schedule()–>raise_softirq_irqoff(TASKLET_SOFTIRQ),唤起对应的软中断,之后在do_softirq()中调用对应的action,即tasklet_action

static void tasklet_action(struct softirq_action *a)
{
	struct tasklet_struct *list;

	local_irq_disable();   //禁止中断
	list = __get_cpu_var(tasklet_vec).head;
	__get_cpu_var(tasklet_vec).head = NULL;//清空,防止SMP时影响
	__get_cpu_var(tasklet_vec).tail = &__get_cpu_var(tasklet_vec).head;
	local_irq_enable();

	while (list) { //遍历
		struct tasklet_struct *t = list;

		list = list->next;

		if (tasklet_trylock(t)) { //检查并设置TASKLET_STATE_RUN
			if (!atomic_read(&t->count)) {//检查是否被禁止
				if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
					BUG();
				t->func(t->data);//执行对应的func
				tasklet_unlock(t);
				continue;
			}
			tasklet_unlock(t);
		}

		local_irq_disable();
		t->next = NULL;
		*__get_cpu_var(tasklet_vec).tail = t;
		__get_cpu_var(tasklet_vec).tail = &(t->next);
		__raise_softirq_irqoff(TASKLET_SOFTIRQ);
		local_irq_enable();
	}
}

4.3 工作队列

  把工作交由内核线程取执行,允许重新调度、睡眠。
  相关数据结构

struct work_struct {
    atomic_long_t data;             /* 这个并不是处理函数的参数,而是表示此work是否pending等状态的flag */
#define WORK_STRUCT_PENDING 0        /* T if work item pending execution */
#define WORK_STRUCT_FLAG_MASK (3UL)
#define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK)
    struct list_head entry;         /* 中断下半部处理函数的链表 */
    work_func_t func;               /* 处理中断下半部工作的函数 */
#ifdef CONFIG_LOCKDEP
    struct lockdep_map lockdep_map;
#endif
};

struct cpu_workqueue_struct {

    spinlock_t lock;                   /* 锁保护这种结构 */

    struct list_head worklist;         /* 工作队列头节点 */
    wait_queue_head_t more_work;
    struct work_struct *current_work;

    struct workqueue_struct *wq;       /* 关联工作队列结构 */
    struct task_struct *thread;        /* 关联线程 */
} ____cacheline_aligned;


struct workqueue_struct {
    struct cpu_workqueue_struct *cpu_wq;  /* 工作者线程 */
    struct list_head list;
    const char *name;
    int singlethread;
    int freezeable;        /* Freeze threads during suspend */
    int rt;
#ifdef CONFIG_LOCKDEP
    struct lockdep_map lockdep_map;
#endif
};

  workqueue_struct:工作者线程workqueue_struct用表示
  cpu_workqueue_struct:每个处理器对应的工作者线程表示
  work_struct:工作处理,组成链表
  关系
在这里插入图片描述

4.4 总结

软中断tasklet工作队列
上下文中断中断进程
顺序执行保障同类型不能同时执行

选择时考虑:
  1)是否需要休眠
  2)性能

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值