嵌入式内核及驱动开发之学习笔记(十一) 中断优化处理

举报
王建峰 发表于 2021/11/19 03:14:34 2021/11/19
【摘要】 ARM cortex-A系列的内核不支持中断嵌套。在内核中断函数中,如果中断处理时间过长,产生中断嵌套,重者系统崩溃,轻者也会影响其他事件处理。这也是中断中不能使用延时函数的原因。 但是有些高实时性设备(比如网卡),就是需要处理大量的业务。为了满足中断处理时间尽量短的原则,我们将一些简单的处理放在中断中实现,这个阶段叫做中断...

ARM cortex-A系列的内核不支持中断嵌套。在内核中断函数中,如果中断处理时间过长,产生中断嵌套,重者系统崩溃,轻者也会影响其他事件处理。这也是中断中不能使用延时函数的原因。

但是有些高实时性设备(比如网卡),就是需要处理大量的业务。为了满足中断处理时间尽量短的原则,我们将一些简单的处理放在中断中实现,这个阶段叫做中断的上半部;其他一些复杂、耗时间的操作丢给内核线程,让内核来调度其执行,这是中断的下半部。

中断事件 -->  跳转中断入口 -->  中断中执行简单处理 -->  并启动内核调度复杂的处理 -->  结束中断

 

处理方式

  1. softirq: 处理比较快,但是内核级别的机制,需要修改整个内核源码,不推荐也不常用
  2. tasklet: 内部实现实际调用了softirq
  3. workqueue: 工作队列

 

tasklet

启动"下半部"实际上就是把结构体描述的对象丢给内核线程的动作。

结构体


  
  1. struct tasklet_struct
  2. {
  3. struct tasklet_struct *next;
  4. unsigned long state;
  5. atomic_t count;
  6. void (*func)(unsigned long); // 下半部的实现逻辑
  7. unsigned long data; // 传递给func
  8. };

1.初始化对象


  
  1. struct tasklet_struct mytasklet;
  2. tasklet_init(struct tasklet_struct * t, void(* func)(unsigned long), unsigned long data)

 

2.构造"下半部"实现逻辑


  
  1. void key_tasklet_half_irq(unsigned long data)
  2. {
  3. //............
  4. //............
  5. }

 

3."上半部"启动"下半部"

tasklet_schedule(&key_dev->mytasklet);
 

 

4.(在模块卸载时)注销内核线程中的对象

tasklet_kill(&key_dev->mytasklet);
 

 

完成的例子(驱动程序)


  
  1. //key_drv.c
  2. #include <linux/init.h>
  3. #include <linux/module.h>
  4. #include <linux/of.h>
  5. #include <linux/of_irq.h>
  6. #include <linux/interrupt.h>
  7. #include <linux/slab.h>
  8. #include <linux/fs.h>
  9. #include <linux/device.h>
  10. #include <linux/wait.h>
  11. #include <linux/sched.h>
  12. #include <linux/poll.h>
  13. #include <asm/io.h>
  14. #include <asm/uaccess.h>
  15. irqreturn_t key_irq_handler(int irqno, void *devid);
  16. void key_tasklet_half_irq(struct work_struct *work);
  17. ssize_t key_drv_read (struct file *, char __user *, size_t, loff_t *);
  18. ssize_t key_drv_write (struct file *, const char __user *, size_t, loff_t *);
  19. int key_drv_open (struct inode *, struct file *);
  20. int key_drv_close (struct inode *, struct file *);
  21. #define GPXCON_REG 0x11000C20
  22. #define KEY_ENTER 28
  23. const struct file_operations key_fops = {
  24. .open = key_drv_open,
  25. .read = key_drv_read,
  26. .write = key_drv_write,
  27. .release = key_drv_close,
  28. };
  29. struct key_event{
  30. int code; // 按键的类型
  31. int value; // 状态
  32. };
  33. struct key_desc{
  34. unsigned int dev_major;
  35. struct class *cls;
  36. struct device *dev;
  37. int irqno;
  38. void *reg_base;
  39. int key_state; //表示是否有数据
  40. struct key_event event;
  41. struct tasklet_struct mytasklet;
  42. };
  43. struct key_desc *key_dev;
  44. int get_irqno_from_node(void)
  45. {
  46. //从设备树路径,查找节点
  47. struct device_node *np = of_find_node_by_path("/key_int_node");
  48. if(np){
  49. printk("find node ok\n");
  50. }else{
  51. printk("find node failed\n");
  52. }
  53. int irqno = irq_of_parse_and_map(np, 0);
  54. printk("irqno = %d\n", irqno);
  55. return irqno;
  56. }
  57. static int __init key_drv_init(void)
  58. {
  59. int ret;
  60. //对象实例化
  61. key_dev = kzalloc(sizeof(struct key_desc), GFP_KERNEL);
  62. //申请主设备号
  63. key_dev->dev_major = register_chrdev(0, "key_drv", &key_fops);
  64. //创建设备结点
  65. key_dev->cls = class_create(THIS_MODULE, "key_cls");
  66. key_dev->dev = device_create(key_dev->cls, NULL,
  67. MKDEV(key_dev->dev_major,0), NULL, "key0");
  68. //硬件初始化
  69. key_dev->irqno = get_irqno_from_node();
  70. ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
  71. "key3_eint10", NULL);
  72. if(ret != 0)
  73. {
  74. printk("request_irq error\n");
  75. return ret;
  76. }
  77. key_dev->reg_base = ioremap(GPXCON_REG, 8);
  78. //初始化tasklet
  79. tasklet_init(&key_dev->mytasklet, key_tasklet_half_irq, 45);
  80. return 0;
  81. }
  82. static void __exit key_drv_exit(void)
  83. {
  84. tasklet_kill(&key_dev->mytasklet);
  85. iounmap(key_dev->reg_base); //去映射
  86. free_irq(key_dev->irqno, NULL); //释放中断资源
  87. device_destroy(key_dev->cls, MKDEV(key_dev->dev_major,0)); //
  88. class_destroy(key_dev->cls); //
  89. unregister_chrdev(key_dev->dev_major, "key_drv"); //注销主设备号
  90. kfree(key_dev); //释放结构体内存
  91. }
  92. irqreturn_t key_irq_handler(int irqno, void *devid)
  93. {
  94. printk("-------%s-------------\n", __FUNCTION__);
  95. // 启动下半步
  96. tasklet_schedule(&key_dev->mytasklet);
  97. return IRQ_HANDLED;
  98. }
  99. void key_tasklet_half_irq(struct work_struct *work)
  100. {
  101. printk("-------%s-------------\n", __FUNCTION__);
  102. }
  103. ssize_t key_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
  104. {
  105. printk("-------%s-------------\n", __FUNCTION__);
  106. return count;
  107. }
  108. ssize_t key_drv_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
  109. {
  110. printk("-------%s-------------\n", __FUNCTION__);
  111. return 0;
  112. }
  113. int key_drv_open(struct inode *inode, struct file *filp)
  114. {
  115. printk("-------%s-------------\n", __FUNCTION__);
  116. return 0;
  117. }
  118. int key_drv_close (struct inode *inode, struct file *filp)
  119. {
  120. printk("-------%s-------------\n", __FUNCTION__);
  121. return 0;
  122. }
  123. module_init(key_drv_init);
  124. module_exit(key_drv_exit);
  125. MODULE_LICENSE("GPL");

 

演示

 

 

workqueue

结构体描述


  
  1. struct work_struct {
  2. atomic_long_t data;
  3. struct list_head entry;
  4. work_func_t func;
  5. };

 

1.初始化对象


  
  1. struct work_struct mywork;
  2. INIT_WORK(struct work_struct *work, work_func_t func);

 

2.构造"下半部"实现逻辑


  
  1. void work_irq_half(struct work_struct *work)
  2. {
  3. //...填入要实现的内容
  4. }

 

3."上半部"启动"下半部"

schedule_work(&key_dev->mywork);
 

 

代码(驱动)


  
  1. //key_drv.c
  2. #include <linux/init.h>
  3. #include <linux/module.h>
  4. #include <linux/of.h>
  5. #include <linux/of_irq.h>
  6. #include <linux/interrupt.h>
  7. #include <linux/slab.h>
  8. #include <linux/fs.h>
  9. #include <linux/device.h>
  10. #include <linux/wait.h>
  11. #include <linux/sched.h>
  12. #include <linux/poll.h>
  13. #include <asm/io.h>
  14. #include <asm/uaccess.h>
  15. irqreturn_t key_irq_handler(int irqno, void *devid);
  16. void work_irq_half(struct work_struct *work);
  17. ssize_t key_drv_read (struct file *, char __user *, size_t, loff_t *);
  18. ssize_t key_drv_write (struct file *, const char __user *, size_t, loff_t *);
  19. int key_drv_open (struct inode *, struct file *);
  20. int key_drv_close (struct inode *, struct file *);
  21. #define GPXCON_REG 0x11000C20
  22. #define KEY_ENTER 28
  23. const struct file_operations key_fops = {
  24. .open = key_drv_open,
  25. .read = key_drv_read,
  26. .write = key_drv_write,
  27. .release = key_drv_close,
  28. };
  29. struct key_event{
  30. int code; // 按键的类型
  31. int value; // 状态
  32. };
  33. struct key_desc{
  34. unsigned int dev_major;
  35. struct class *cls;
  36. struct device *dev;
  37. int irqno;
  38. void *reg_base;
  39. int key_state; //表示是否有数据
  40. struct key_event event;
  41. struct work_struct mywork;
  42. };
  43. struct key_desc *key_dev;
  44. int get_irqno_from_node(void)
  45. {
  46. //从设备树路径,查找节点
  47. struct device_node *np = of_find_node_by_path("/key_int_node");
  48. if(np){
  49. printk("find node ok\n");
  50. }else{
  51. printk("find node failed\n");
  52. }
  53. int irqno = irq_of_parse_and_map(np, 0);
  54. printk("irqno = %d\n", irqno);
  55. return irqno;
  56. }
  57. static int __init key_drv_init(void)
  58. {
  59. int ret;
  60. //对象实例化
  61. key_dev = kzalloc(sizeof(struct key_desc), GFP_KERNEL);
  62. //申请主设备号
  63. key_dev->dev_major = register_chrdev(0, "key_drv", &key_fops);
  64. //创建设备结点
  65. key_dev->cls = class_create(THIS_MODULE, "key_cls");
  66. key_dev->dev = device_create(key_dev->cls, NULL,
  67. MKDEV(key_dev->dev_major,0), NULL, "key0");
  68. //硬件初始化
  69. key_dev->irqno = get_irqno_from_node();
  70. ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
  71. "key3_eint10", NULL);
  72. if(ret != 0)
  73. {
  74. printk("request_irq error\n");
  75. return ret;
  76. }
  77. key_dev->reg_base = ioremap(GPXCON_REG, 8);
  78. //初始化work
  79. INIT_WORK(&key_dev->mywork, work_irq_half);
  80. return 0;
  81. }
  82. static void __exit key_drv_exit(void)
  83. {
  84. iounmap(key_dev->reg_base); //去映射
  85. free_irq(key_dev->irqno, NULL); //释放中断资源
  86. device_destroy(key_dev->cls, MKDEV(key_dev->dev_major,0)); //
  87. class_destroy(key_dev->cls); //
  88. unregister_chrdev(key_dev->dev_major, "key_drv"); //注销主设备号
  89. kfree(key_dev); //释放结构体内存
  90. }
  91. irqreturn_t key_irq_handler(int irqno, void *devid)
  92. {
  93. printk("-------%s-------------\n", __FUNCTION__);
  94. // 启动下半步
  95. schedule_work(&key_dev->mywork);
  96. return IRQ_HANDLED;
  97. }
  98. void work_irq_half(struct work_struct *work)
  99. {
  100. printk("-------%s-------------\n", __FUNCTION__);
  101. }
  102. ssize_t key_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
  103. {
  104. printk("-------%s-------------\n", __FUNCTION__);
  105. return count;
  106. }
  107. ssize_t key_drv_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
  108. {
  109. printk("-------%s-------------\n", __FUNCTION__);
  110. return 0;
  111. }
  112. int key_drv_open(struct inode *inode, struct file *filp)
  113. {
  114. printk("-------%s-------------\n", __FUNCTION__);
  115. return 0;
  116. }
  117. int key_drv_close (struct inode *inode, struct file *filp)
  118. {
  119. printk("-------%s-------------\n", __FUNCTION__);
  120. return 0;
  121. }
  122. module_init(key_drv_init);
  123. module_exit(key_drv_exit);
  124. MODULE_LICENSE("GPL");

 

演示

文章来源: blog.csdn.net,作者:hinzer,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/feit2417/article/details/84202564

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。