• 中断下半部之 tasklet


    简介

    tasklet 是利用软中断实现的一种下半部机制。
    选择到底是使用软中断还是 tasklet 其实很简单:通常你应该用 tasklet。
    引入软中断的主要原因是其可扩展性。如果不需要扩展到多个处理器,那么,就使用 tasklet 吧。tasklet 本质上也是软中断,只不过同一个处理程序的多个实例不能在多个处理器上同时运行。软中断只在那些执行频率很高和连续性要求很高的情况下才需要。
    而 tasklet 却有更广泛的用途。大多数情况下用 tasklet 效果都不错,而且它们还非常容易使用。
    tasklet 由两类软中断代表:HI_SOFTIRQ 和 TASKLET_SOFTIRQ。这两者之间唯一的区别在于前者优先级比后者高。

    使用

    ① 声明你自己的 tasklet
    静态创建

    DECLARE_TASKLET(name, _callback)
    DECLARE_TASKLET_DISABLED(name, _callback)
    
    • 1
    • 2

    动态创建

    tasklet_init()
    
    • 1

    ② 编写你自己的 tasklet 程序

    void tasklet_fn(unsigned long data)
    
    • 1

    注意,因为是靠软中断实现,所以 tasklet 不能睡眠。
    tasklet 运行时允许响应中断,所以你必须做好预防工作(比如屏蔽中断然后获取一个锁)。

    ③ 调度你自己的 tasklet

    tasklet 由以下两个函数进行调度。

    tasklet_schedule()
    tasklet_hi_schedule()
    
    • 1
    • 2

    实例

    #include 
    #include 
    #include 
    #include 
    
    #define BUTTON_PIN 12 /* GPIO 12 */
    
    int flag = 0;
    
    struct tasklet_struct tsklt;
    
    void tasklet_fn(unsigned long data)
    {
    	printk("%s(), %s\n", __func__, current->comm);
    }
    
    static irqreturn_t irq_handler(int irq, void *dev)	// 上半部
    {
    	printk("%s(): enter\n", __FUNCTION__);
    
    	tasklet_hi_schedule(&tsklt);
    
    	printk("%s(): exit\n", __FUNCTION__);
    
    	return IRQ_HANDLED;
    }
    
    static int led_init(void)
    {
    	int err;
    	int irq;
    
    	printk("%s()\n", __FUNCTION__);
    
    	tasklet_init(&tsklt, tasklet_fn, 0);
    
    	err = gpio_request_one(BUTTON_PIN, GPIOF_IN, "Button");
    	if (err)
    		return err;
    
    	irq = gpio_to_irq(BUTTON_PIN);
    	// enable_irq(irq); // why crash ?
    	err = request_irq(irq, irq_handler, IRQ_TYPE_EDGE_BOTH, "LED Test", NULL);
    	if (err < 0) {
    		printk("request irq (%d) failed!\n", irq);
    		return err;
    	}
    
    	printk("request irq (%d) success!\n", irq);
    
    	flag = 1;
    
    	return 0;
    }
    
    static void led_exit(void)
    {
    	printk("%s()\n", __FUNCTION__);
    
    	if (flag)
    		free_irq(gpio_to_irq(BUTTON_PIN), NULL);
    
    	gpio_free(BUTTON_PIN);
    
    	return;
    }
    
    module_init(led_init);
    module_exit(led_exit);
    
    MODULE_LICENSE("Dual BSD/GPL");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    其它相关源码参考上篇文章

    测试

    # tftp -gr irq_tasklet.ko 192.168.31.224
    # 
    # insmod irq_tasklet.ko 
    [39721.126865] led_init()
    [39721.130762] request irq (200) success!
    # 
    # ./gpioout.out 1
    [39724.639370] irq_handler(): enter
    [39724.644089] irq_handler(): exit
    [39724.648772] tasklet_fn(), swapper/0
    [39725.639452] irq_handler(): enter
    [39725.644161] irq_handler(): exit
    [39725.648807] tasklet_fn(), swapper/0
    # 
    # rmmod irq_tasklet.ko 
    [39732.248225] led_exit()
    # 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    执行中断服务程序后,紧接着执行 tasklet_fn(),符合预期。

  • 相关阅读:
    计算机网络重点概念整理-第二章 物理层【期末复习|考研复习】
    NLP冻手之路(5)——中文情感分类(以BERT为基础,由Hugging Face库支持,代码实践)
    数据库第三章相关习题记录-关系数据库标准语言SQL
    Java日期类汇总
    30、Nio(select(处理用户端断开(有read事件)))
    真正的成长没有速成剂,都是风吹雨打过来的
    Spring注解
    flutter瀑布式图文列表
    python替换word文件中的图片
    如何在本地 Linux 主机上实现 Yearning SQL 审核平台的远程访问?
  • 原文地址:https://blog.csdn.net/lyndon_li/article/details/127956615