在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_tasklettasklet并将其与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底半部机制的函数。

Logo

更多推荐