linux中断机制分析
在linux设备驱动中,使用中断的设备需要申请和释放对应的中断,分别使用内核提供的request()和free()函数。1 申请IRQint request_irq(unsigned int irq, void (*handler)(int irq, void *dev_id ...), unsigned long irqflags, const ch
在linux设备驱动中,使用中断的设备需要申请和释放对应的中断,分别使用内核提供的request()和free()函数。
1 申请IRQ
int request_irq(unsigned int irq, void (*handler)(int irq, void *dev_id ...),
unsigned long irqflags, const char *devname, void *dev_id);
其中
irq:要申请的硬件中断号
irqflags是要处理的中断属性:
IRQF_SHARED:就是旧时代的SA_SHIRQ,共享中断号
IRQF_DISABLED:就是就时代的SA_INTERRUPT,设置了该标志,则执行ISR时关本地中断
IRQF_SAMPLE_RANDON:告诉内核,本中断源可以用作随机数发生器的熵池
dev_id:在中断共享时会用到,一般设置这个设备的设备结构体或者NULL
request_irq()返回0表示成功,返回-INVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY表示中断已经被占用且不能共享。
2 释放IRQ
void free_irq(unsigned int irq, void *dev_id);
3 使能和屏蔽中断
下列函数用于屏蔽和使能一个中断源
void disable_irq(int irq);
void disable_irq_nosync(int irq);
void enable_irq(int irq);
disable_irq与disable_irq_nosync的区别在于前者等待到irq的处理已经完成才返回,后者返回前并不保证irq的处理函数已经完成
这3个函数作用于可编程控制器,因此对所有的CPU都生效。
屏蔽本CPU的所有中断:
void local_irq_disable(void);
void local_irq_save(unsigned long flags);
恢复对应中断:
void local_irq_enable(void);
void local_irq_restore(unsigned long flags);
底半部机制
linux系统中实现底半部机制主要有:tasklet,工作队列和软中断。其中软中断和tasklet运行于中断上下文,而工作队列则运行于进程上下文。因此,软中断和tasklet处理函数中不能睡眠,而工作队列处理函数中允许睡眠”。
tasklet:
定义tasklet及其处理函数,并将两者关联:
void my_tasklet_func(unsigned long);
DECLARE_TASKLET(my_tasklet,my_tasklet_func,data);DECLARE_TASKLET定义名称为my_tasklet的tasklet并将其与my_tasklet_func函数绑定,传入这个函数的参数为data。在需要调度tasklet的时候引用tasklet_schedule函数就能使系统在适当时候运行:tasklet_schedule(&my_tasklet);
工作队列:
struct work_struct my_wq;//定义一个工作队列
void my_wq_func(struct work_struct * work);//定义一个处理函数
1) INIT_WORK(_work, _func, _data)
初始化指定工作,目的是把用户指定的函数_func及_func需要的参数_data赋给work_struct的func及data变量。
2) int schedule_work(struct work_struct *work)
对工作进行调度,即把给定工作的处理函数提交给缺省的工作队列和工作者线程。工作者线程本质上是一个普通的内核线程,在默认情况下,每个CPU均有一个类型为“events”的工作者线程,当调用schedule_work时,这个工作者线程会被唤醒去执行工作链表上的所有工作。
3) int schedule_delayed_work(struct work_struct *work, unsigned long delay)
延迟执行工作,与schedule_work类似。
4) void flush_scheduled_work(void)
刷新缺省工作队列。此函数会一直等待,直到队列中的所有工作都被执行。
5) int cancel_delayed_work(struct work_struct *work)
flush_scheduled_work并不取消任何延迟执行的工作,因此,如果要取消延迟工作,应该调用cancel_delayed_work。
以上均是采用缺省工作者线程来实现工作队列,其优点是简单易用,缺点是如果缺省工作队列负载太重,执行效率会很低,这就需要我们创建自己的工作者线程和工作队列。
1) struct workqueue_struct *create_workqueue(const char *name)
创建新的工作队列和相应的工作者线程,name用于该内核线程的命名。
2) int queue_work(struct workqueue_struct *wq, struct work_struct *work)
类似于schedule_work,区别在于queue_work把给定工作提交给创建的工作队列wq而不是缺省队列。
3) int queue_delayed_work(struct workqueue_struct *wq, struct work_struct *work, unsigned long delay)
延迟执行工作。
4) void flush_workqueue(struct workqueue_struct *wq)
刷新指定工作队列。
5) void destroy_workqueue(struct workqueue_struct *wq)
释放创建的工作队列。
下面一段代码可以看作一个简单的实作:
void my_func(void *data)
{
char *name = (char *)data;
printk(KERN_INFO “Hello world, my name is %s!/n”, name);
}
struct workqueue_struct *my_wq = create_workqueue(“my_wq”);
struct work_struct my_work;
INIT_WORK(&my_work, my_func, “Jack”);
queue_work(my_wq, &my_work);
destroy_workqueue(my_wq);
新版本中,定义了一个新的结构delayed_work用于处理延迟执行:
struct delayed_work {
struct work_struct work;
struct timer_list timer;
};
下面把API罗列一下,每个函数的解释可参考之前版本的介绍或者之后的实作:
1) INIT_WORK(struct work_struct *work, work_func_t func)
2) INIT_DELAYED_WORK(struct delayed_work *work, work_func_t func)
3) int schedule_work(struct work_struct *work)
4) int schedule_delayed_work(struct delayed_work *work, unsigned long delay)
5) struct workqueue_struct *create_workqueue(const char *name)
6) int queue_work(struct workqueue_struct *wq, struct work_struct *work)
7) int queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *work, unsigned long delay)
8) void flush_scheduled_work(void)
9) void flush_workqueue(struct workqueue_struct *wq)
10) int cancel_delayed_work(struct delayed_work *work)
11) void destroy_workqueue(struct workqueue_struct *wq)
软中断
在linux内核中,用softirq_action结构体表征一个软中断,这个结构体中包含软中断处理函数指针和传递给该函数的参数,使用open_softirq注册软中断对应的中断函数,raise_softirq函数出发一个软中断。
local_bh_disable和local_bh_enable是内核中用于禁止和使能软中断和tasklet底半部机制的函数。
更多推荐
所有评论(0)