庆阳网站设计 贝壳下拉,医院网站站群建设方案,网站建设项目有哪些,wordpress小说自动采集在申请 GPIO 中断时使用 request_irq,但是request_irq绑定的中断服务程序指的是中断上文。在 Linux 内核中#xff0c;tasklet 是一种特殊的软中断机制#xff0c;被广泛用于处理中断下文相关的任务。它是一种常见且有效的方法#xff0c;在多核处理系统上可以避免并发问题。… 在申请 GPIO 中断时使用 request_irq,但是request_irq绑定的中断服务程序指的是中断上文。在 Linux 内核中tasklet 是一种特殊的软中断机制被广泛用于处理中断下文相关的任务。它是一种常见且有效的方法在多核处理系统上可以避免并发问题。Tasklet 绑定的函数在同一时间只能在一个 CPU 上运行因此不会出现并发冲突。然而需要注意的是tasklet 绑定的函数中不能调用可能导致休眠的函数否则可能引起内核异常。 在 Linux 内核中tasklet 结构体的定义位于 include/linux/interrupt.h 头文件中。其原型如下
struct tasklet_struct
{struct tasklet_struct *next;unsigned long state;atomic_t count;void (*func)(unsigned long);unsigned long data;
};tasklet_struct 结构体包含以下成员 next:指向下一个tasklet的指针用于形成链表结构以便内核中可以同时管理多个tasklet。 state:表示 tasklet 的当前状态。 count:用于引用计数用于确保 tasklet 在多个地方调度或取消调度时的正确处理。 func:指向 tasklet 绑定的函数的指针该函数将在 tasklet 执行时被调用。 data:传递给 tasklet 绑定函数的参数
一、tasklet 相关接口函数
1.1、静态初始化函数 在 Linux 内核中有一个用于静态初始化 tasklet 的宏函数DECLARE_TASKLET。这个宏函数可以帮助我们更方便地进行 tasklet 的静态初始化。宏函数的原型如下
#define DECLARE_TASKLET(name,func,data) \
struct tasklet_struct name { NULL,0,ATOMIC_INIT(0),func,data}其中name 是 tasklet 的名称func 是 tasklet 的处理函数data 是传递给处理函数的参数。初始化状态为使能状态。 如果 tasklet 初始化函数为非使能状态使用以下宏定义
#define DECLARE_TASKLET_DISABLED(name,func,data) \
struct tasklet_struct name { NULL,0,ATOMIC_INIT(1),func,data}其中name 是 tasklet 的名称func 是 tasklet 的处理函数data 是传递给处理函数的参数。初始化状态为非使能状态。
1.2、动态初始化函数 在 Linux 内核中可以使用 tasklet_init 函数对 tasklet 进行动态初始化。该函数原型为
void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data);其中t 是指向 tasklet 结构体的指针func 是 tasklet 的处理函数data 是传递给处理函数的参数
1.3、关闭函数 在 Linux 内核中可以使用 tasklet_disabled 函数来关闭一个已经初始化的tasklet。该函数的原型如下
void tasklet_disable(struct tasklet_struct *t);其中t 是指向 tasklet 结构体的指针。 关闭 tasklet 后即使调用 tasklet_schedule 函数触发 tasklettasklet 的处理函数也不会再被执行。这可以用于临时暂停或停止 tasklet 的执行直到再次启用通过调用tasklet_enable函数。 需要注意的是关闭 tasklet 并不会销毁 tasklet 结构体因此可以随时通过调用tasklet_enable 函数重新启用 tasklet或者调用 tasklet_kill 函数来销毁tasklet。
1.4、使能函数 在 Linux 内核中可以使用 tasklet_enable 函数来使能启用一个已经初始化的tasklet。该函数的原型如下
void tasklet_disable(struct tasklet_struct *t);使能 tasklet 后如果调用 tasklet_schedule 函数触发 tasklet则tasklet 的处理函数将会被执行。这样tasklet 将开始按计划执行其处理逻辑。 需要注意的是使能 tasklet 并不会自动触发 tasklet 的执行而是通过调用tasklet_schedule函数来触发。同时可以使用 tasklet_disable 函数来临时暂停或停止tasklet 的执行。如果需要永久停止 tasklet 的执行并释放相关资源则应调用 tasklet_kill 函数来销毁tasklet。
1.5、调度函数 在 Linux 内核中可以使用 tasklet_schedule 函数来调度触发一个已经初始化的tasklet执行。该函数的原型如下
void tasklet_schedule(struct tasklet_struct *t);需要注意的是调度 tasklet 只是将 tasklet 标记为需要执行并不会立即执行tasklet 的处理函数。实际的执行时间取决于内核的调度和处理机制。
1.6、销毁函数 在 Linux 内核中可以使用 tasklet_kill 函数来销毁一个已经初始化的tasklet释放相关资源。该函数的原型如下
void tasklet_kill(struct tasklet_struct *t);调用 tasklet_kill 函数会释放 tasklet 所占用的资源并将tasklet 标记为无效。因此销毁后的 tasklet 不能再被使用。需要注意的是在销毁 tasklet 之前应该确保该 tasklet 已经被停止通过调用tasklet_disable 函数。否则销毁一个正在执行的 tasklet 可能导致内核崩溃或其他错误。一旦销毁了 tasklet如果需要再次使用 tasklet需要重新进行初始化通过调用tasklet_init函数。在下一小节中我们将使用上述 tasklet 函数相关接口函数进行相应的实验。
二、代码示例
2.1、驱动层程序
#include linux/module.h
#include linux/init.h
#include linux/interrupt.h
#include linux/gpio.h
// #include linux/delay.hint irq;
struct tasklet_struct mytasklet;// 定义tasklet处理函数
void mytasklet_func(unsigned long data)
{printk(data is %ld\n, data);// msleep(3000);
}// 中断处理函数
irqreturn_t test_interrupt(int irq, void *args)
{printk(This is test_interrupt\n);tasklet_schedule(mytasklet); // 调度tasklet执行return IRQ_RETVAL(IRQ_HANDLED);
}
// 模块初始化函数
static int interrupt_irq_init(void)
{int ret;irq gpio_to_irq(101); // 将GPIO转换为中断号printk(irq is %d\n, irq);// 请求中断ret request_irq(irq, test_interrupt, IRQF_TRIGGER_RISING, test, NULL);if (ret 0){printk(request_irq is error\n);return -1;}// 初始化tasklettasklet_init(mytasklet, mytasklet_func, 1);return 0;
}
// 模块退出函数
static void interrupt_irq_exit(void)
{free_irq(irq, NULL);tasklet_enable(mytasklet); // 使能tasklet可选tasklet_kill(mytasklet); // 销毁taskletprintk(bye bye\n);
}module_init(interrupt_irq_init); // 指定模块的初始化函数
module_exit(interrupt_irq_exit); // 指定模块的退出函数2.2、linux中断下文之tasklet使用API要点
struct tasklet_struct mytasklet;// 初始化tasklettasklet_init(mytasklet, mytasklet_func, 1);tasklet_schedule(mytasklet); // 调度tasklet执行