linux内核-中断下半部问题

为什么中断分为上半部和下半部

1. 中断处理程序以异步方式执行并且它有可能会打断其他重要代码(甚至包括其他中断处理程序)的执行。因此,为了避免被打断的代码停止时间过长,中断处理程序应该执行得越快越好。
2. 如果当前有一个中断处理程序正在执行,在最好的情况下,与该中断同级的其他的同级中断会被屏蔽,在最坏的情况下,当前处理器上所有其他中断都会被屏蔽。因此,任然应该让他们执行得越快越好。
3. 由于中断处理程序往往需要对硬件进行操作,所以他们通常有很高的时限要求。
4. 中断处理程序不在进程上下文运行,所以他们不能阻塞。这限制了他们所做的事情。
人为把中断划分上下半部, 因为中断处理时间过长, 关中断, 占用 CPU。
类似 tcp 通信编程,服务器端程序多线程。 1. 监听- accept 2. 处理-read
上半部: 接收中断,登记工作。
下半部:处理程序挂到发送中断请求的下半部来处理。

中断下半部实现有 2

1.tasklet 小任务

用软中断来实现是一种特殊运行模式。用内核线程 ksoftirqd。
中断上下文不能休眠,执行优先级非常高,适合很快执行完。
root@SOM-RK3399v2:/drv_code# ps -aux | grep ksoftirqd
root 3 0.0 0.0 0 0 ? S 08:44 0:00 [ksoftirqd/0]
root 13 0.0 0.0 0 0 ? S 08:44 0:00 [ksoftirqd/1]
root 18 0.0 0.0 0 0 ? S 08:44 0:00 [ksoftirqd/2]
root 23 0.0 0.0 0 0 ? S 08:44 0:00 [ksoftirqd/3]
root 28 0.0 0.0 0 0 ? S 08:44 0:00 [ksoftirqd/4]
root 33 0.0 0.0 0 0 ? S 08:44 0:00 [ksoftirqd/5]

 Tasklet 的实现

定义一个 tasklet 结构
struct tasklet_struct task;
定义一个处理函数
void my_tasklet_func(unsigned long data); // 下半部做的事
定义一个初始化函数
tasklet_init(&task,task_fun,data); // 将处理函数和 tasklet 对象绑定
调度 tasklet 执行
tasklet_schedule(&task);// 调度

2.workqueue 工作队列

交给一个内核线程 kworker 执行。
进程上下文中,可休眠。
root@SOM-RK3399v2:/drv_code# ps -aux | grep kworker
root 5 0.0 0.0 0 0 ? S< 08:44 0:00 [kworker/0:0H]
root 6 0.0 0.0 0 0 ? S 08:44 0:00 [kworker/u12:0]
root 15 0.0 0.0 0 0 ? S< 08:44 0:00 [kworker/1:0H]
root 20 0.0 0.0 0 0 ? S< 08:44 0:00 [kworker/2:0H]
root 24 0.0 0.0 0 0 ? S 08:44 0:00 [kworker/3:0]
root 25 0.0 0.0 0 0 ? S< 08:44 0:00 [kworker/3:0H]
root 30 0.0 0.0 0 0 ? S< 08:44 0:00 [kworker/4:0H]
root 35 0.0 0.0 0 0 ? S< 08:44 0:00 [kworker/5:0H]
root 45 0.0 0.0 0 0 ? S 08:44 0:00 [kworker/4:1]
什么是工作队列 ?
linux workqueue 机制为了简化内核线程的创建,通过调用 workqueue 接口就能创建内核线程
工作队列可以把工作推迟后交由一个内核线程去执行, 也就是说 , 这个工作队列的下半部可在进程上下文执行。(可以重新调度甚至休眠 ) 在一个独立的进程环境下运行的。
工作队列的实现
定义一个工作队列
struct work_struct my_wq;
定义一个处理函数
void my_wq_func(struct work_struct *work)
初始化工作队列并将其与处理函数 绑定
INIT_WORK(&my_wq, my_wq_func);
调度工作队列执行
schedule_work(&my_wq);
使用 create_singlethread_workqueue 函数
create_singlethread_workqueue create_workqueue 函数
对于多处理器系统, 使用 siglethread 会在系统的第一个 cpu 上产生工作队列和工作线程;
而非 singlethread 则为系统每个处理器都产生一个工作队列和工作线程;
内核在初始化阶段创建了一个非 singlethread 的工作队列, 驱动程序可使用该队列, 也可调用
create_singlethread_workqueue create_workqueue 函数创建属于自己的工作队列。
步骤如下:
定义一个工作队列
static struct workqueue_struct *my_workqueue = NULL;
定义一个工作任务
static struct work_struct my_work;//这个任务就是我们需要推后执行的动作
定义工作队列调用函数
void work_func(struct work_struct *work)
{
    /*推后执行的操作,比如数据处理等*/  
}
初始化工作队列
static int __init xxx_init(void)
{
    my_workqueue= create_workqueue("my_workqueue");
    /*创建工作队列workqueue_struct,该函数会为cpu创建内核线程*/ 
    INIT_WORK(&my_work,work_func);
    /*初始化工作work_struct,指定工作函数*/ 
    queue_work(test_wq, &work);
    /*将工作加入到队列中,最终唤醒内核线程(比较常见的使用场景是在中断上半部去唤醒内核线程)*/ 
    return 0;
}
static void __exit xxx_exit(void)
{
    if (my_workqueue)
        destroy_workqueue(my_workqueue);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宁静的海2006

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值