当前位置: 首页 > news >正文

Linux延迟操作

一、软中断

Linux内核中定义了如下几种软中断:

enum
{HI_SOFTIRQ=0,TIMER_SOFTIRQ,NET_TX_SOFTIRQ,NET_RX_SOFTIRQ,BLOCK_SOFTIRQ,IRQ_POLL_SOFTIRQ,TASKLET_SOFTIRQ,SCHED_SOFTIRQ,HRTIMER_SOFTIRQ,RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */NR_SOFTIRQS
};

软中断的初始化

另外软中断必须在编译的时候静态注册! Linux 内核启动时在start_kernel()时会调用 softirq_init 函数初始化软中断,

void __init softirq_init(void)
{int cpu;for_each_possible_cpu(cpu) {per_cpu(tasklet_vec, cpu).tail =&per_cpu(tasklet_vec, cpu).head;per_cpu(tasklet_hi_vec, cpu).tail =&per_cpu(tasklet_hi_vec, cpu).head;}open_softirq(TASKLET_SOFTIRQ, tasklet_action);open_softirq(HI_SOFTIRQ, tasklet_hi_action);
}

由此可以看出, softirq_init 函数默认会打开 TASKLET_SOFTIRQ 和HI_SOFTIRQ。从这里也可以看出虽然软中断通常是由内核开发者来设计的,但是内核开发者专门保留了一个软中断来给驱动者使用,它就是TASKLET_SOFTIRQ和HI_SOFTIRQ,相应的软中断处理函数是tasklet_action和tasklet_hi_action。

如何使用软中断

一般来说, 驱动的编写者不会也不宜直接使用softirq。在内核中要使用软中断,必须:

先使用 open_softirq 函数注册对应的软中断处理函数,其中参数nr就是要开启的软中断,是以上定义的软中断之一;action为软中断对应的处理函数。

void open_softirq(int nr, void (*action)(struct softirq_action *))
{softirq_vec[nr].action = action;
}

raise_softirq()函数在触发一个软中断之前要先禁止中断,触发之后再恢复原来的状态;如果中断本来就已经被禁止了,那么可以调用raise_softirq_irqoff()函数,将会带来一些优化效果。


inline void raise_softirq_irqoff(unsigned int nr)
{__raise_softirq_irqoff(nr);/** If we're in an interrupt or softirq, we're done* (this also catches softirq-disabled code). We will* actually run the softirq once we return from* the irq or softirq.* Otherwise we wake up ksoftirqd to make sure we* schedule the softirq soon.*/if (!in_interrupt() && should_wake_ksoftirqd())wakeup_softirqd();
}void raise_softirq(unsigned int nr)
{unsigned long flags;local_irq_save(flags);raise_softirq_irqoff(nr);local_irq_restore(flags);
}

raise_softirq()函数可以将一个软中断设置为挂起状态,让它在下次调用do_softirq()函数时投入运行;

在中断处理程序中触发软中断是最常见的形式,在这种情况下,中断处理程序执行硬件设备的相关操作,然后触发相应的软中断,最后退出;内核在执行完中断处理程序以后,马上就会调用do_softirq()函数;于是软中断开始执行中断处理程序留给它去完成的剩余任务。

二、tasklet

tasklet 是利用软中断来实现的另外一种下半部机制。在驱动开发中使用tasklet比较多,它的执行上下文是软中断, 执行时机通常是顶半部返回的时候。 在使用时只需要定义tasklet及其处理函数, 并将两者关联则可, 使用模板为:

/*中断处理底半部*/
void xxx_do_tasklet(unsigned long)
{......
}/*中断处理顶半部*/
irqreturn_t xxx_interrupt(int irq, void *dev_id)
{......tasklet_schedule(&xxx_tasklet);......
}/*定义tasklet和底半部函数并将它们关联*/
DECLARE_TASKLET(xxx_tasklet, xxx_do_tasklet, 0);/*设备驱动模块加载函数*/
int __init xxx_init(void)
{....../*申请中断*/result = request_irq(xxx_irq, xxx_interrupt, 0, "xxx", NULL);......return IRQ_HANDLED;
}/*设备驱动模块卸载函数*/
void __exit xxx_exit(void)
{....../*释放中断*/free_irq(xxx_irq, xxx_interrupt);
}

不管是软中断还是tasklet都有一个限制,它们都是在中断上下文中执行。这也就是导致了不能将特别耗时的工作放在这里,否则系统会很卡;并且不能直接或间接地调用调度器。另外在执行的过程中应用程序是无法执行的。

三、work_struct

1)工作队列的应用场景:要做的事情比较耗时,甚至可能需要休眠,那么可以使用工作队列。

2)工作队列的缺点:在多个工作(函数)是在某个内核线程中依序执行的,钱买你函数执行很慢,就会影响后面的函数。在多CPU的系统下,一个工作队列可以有多个内核线程,可以在一定程度上缓解这个问题。

3)工作队列使用示例(以下是在中断中的示例,当然也可也在在非中断程序中用)如下。

struct work_struct xxx_wq;
void xxx_do_work(struct work_struct *work);/*中断处理底半部*/
void xxx_do_work(struct work_struct *work)
{......
}
/*中断处理顶半部*/
irqreturn_t xxx_interrupt(int irq, void *dev)
{....../*其中schedual_work是用来将work_struct放入系统默认的工作队列并且唤醒内核线程,当内核线程被唤醒之后就会从这个队列里把work_strct取出来然后执行它的函数。*/schedule_work(&xxx_wq);......return IRQ_HANDLED;
}int xxx_int(void)
{......result = request_irq(xxx_irq, xxx_interrupt, 0, "xxx", NULL);......INIT_WORK(&xxx_wq, xxx_do_work);......
}void xxx_exit(void)
{......free_irq(xxx_irq, xxx_interrupt);......
}

这里需要注意在内核中我们并不需要自己去创建内核线程,内核初始化工作队列就是为它创建了内核线程。以后我们要使用工作队列,只需要把“工作” 放入工作队列,对应的内核线程就会取出“工作”,执行里面的函数。

四、delayed_work

https://www.cnblogs.com/zafu/p/7400579.html

reference:

https://www.icode9.com/content-3-534083.html

http://blog.chinaunix.net/uid-23916171-id-2653116.html

http://www.lryc.cn/news/34551.html

相关文章:

  • np.insert()函数用法
  • 学习笔记-架构的演进之容器的封装-3月day06
  • Gorm根据关系模型中的属性查询原模型数据
  • 车载技术【USB接口】—Android配件协议AOA【AOA连接】
  • SpringBoot的基本概念和使用
  • 基于计算机软件技术的化工设计特点
  • Nativefier把网页打包成exe
  • STM32U5开发(1)----通过 USART1 发送数据
  • 20230308 Apdl lsdyna两杆撞击案例学习笔记
  • 互相关延时估计 Matlab仿真
  • 谷歌插件Fetch在不同页面之间Cookie携带情况详解
  • Vue学习笔记(8)
  • 知道一个服务器IP应该怎么进入
  • 【计算机基础】Socket IO
  • mingw编译opencv
  • 数据结构(八)排序
  • 函数习题:用函数实现判断一个整数是否能被n整除
  • SAP 创建会计冲销凭证
  • Jetson(Ubuntu18.04)设备无法ping通百度能ping通局域网错误集合,(神奇的是这样的情况下Todesk等远程确没有问题)
  • Spring的@Conditional注解
  • 剑指 Offer 67 把字符串转换成整数
  • 【教学典型案例】18.开门小例子理解面向对象
  • Linux环境ENV的概念
  • AcWing数据结构 - 数据结构在算法比赛中的应用(下)
  • 基于嵌入式libxml2的ARM64平台的移植(aarch64)
  • 8. 字符串转换整数 (atoi)
  • [Tomcat]解决IDEA中的Tomcat中文乱码问题
  • python之dataclasses
  • 【MapGIS精品教程】007:MapGIS投影变换案例教程
  • list数据根据属性字段去重