• linux内核工作延迟机制


    工作延迟机制

    延迟时将所有要做的工作安排在将来执行的一种方法。

    类型使用场景
    SoftIRQ执行在原子上下文
    Tasklet执行在原子上下文
    工作队列执行在进程上下文

    Softirq 和 Ksoftirqd

    软中断用于快速处理,在使用软中断时禁用内核的调度器,处于中断上下文中。softirq在实际场景中很少使用,只有网络和块设备子系统使用softirq,一般在硬件中断中被调度,中断发生很快,快过对他们的处理速度,内核会对他们进行排队以便稍后处理,ksoftirqd负责后期执行(进程上下文),ksoftirqd是单CPU内核线程。

    使用top命令可以看到系统中有多个ksoftirqd内核线程,ksoftirqd/n,其中的n代表的是cpu的编号,每一个cpu有一个ksoftirqd线程。
    在这里插入图片描述
    特别注意:如果CPU资源被ksoftirqd大量消耗,说明此时系统产生了大量的中断。

    Tasklet

    Tasklet是软中断的一个实例,在需要软中断的情况下,一般使用tasklet就可以满足需求。tasklet本质上是不可以再入的,如果代码执行期间随处可中断,并且之后能够被再次安全的调用,就称其为可再入。tasklet只能运行在一个cpu上,也就是调度它的cpu,不同的tasklet只能在不同的cpu上运行。

    Tasklet

    /* Tasklets --- multithreaded analogue of BHs.
    
       This API is deprecated. Please consider using threaded IRQs instead:
       https://lore.kernel.org/lkml/20200716081538.2sivhkj4hcyrusem@linutronix.de
    
       Main feature differing them of generic softirqs: tasklet
       is running only on one CPU simultaneously.
    
       Main feature differing them of BHs: different tasklets
       may be run simultaneously on different CPUs.
    
       Properties:
       * If tasklet_schedule() is called, then tasklet is guaranteed
         to be executed on some cpu at least once after this.
       * If the tasklet is already scheduled, but its execution is still not
         started, it will be executed only once.
       * If this tasklet is already running on another CPU (or schedule is called
         from tasklet itself), it is rescheduled for later.
       * Tasklet is strictly serialized wrt itself, but not
         wrt another tasklets. If client needs some intertask synchronization,
         he makes it with spinlocks.
     */
    
    struct tasklet_struct
    {
    	struct tasklet_struct *next;
    	unsigned long state;
    	atomic_t count;
    	bool use_callback;
    	union {
    		void (*func)(unsigned long data);
    		void (*callback)(struct tasklet_struct *t);
    	};
    	unsigned long data;
    };
    
    
    • 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
    tasklet声明
    • 动态声明
    void tasklet_init(struct tasklet_struct *t,
    		  void (*func)(unsigned long), unsigned long data);
    
    • 1
    • 2
    • 静态声明
    #define DECLARE_TASKLET(name, _callback)		\
    struct tasklet_struct name = {				\
    	.count = ATOMIC_INIT(0),			\
    	.callback = _callback,				\
    	.use_callback = true,				\
    }
    
    #define DECLARE_TASKLET_DISABLED(name, _callback)	\
    struct tasklet_struct name = {				\
    	.count = ATOMIC_INIT(1),			\
    	.callback = _callback,				\
    	.use_callback = true,				\
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    DECLARE_TASKLET创建tasklet已经启用,并准备好在没有任何其他函数调用的情况下被调度,通过count字段设置为0来实现。
    DECLARE_TASKLET_DISABLED创建的tasklet被禁用,通过将count的值设置为1来实现,需要在调用tasklet_enable之后,才可以调度这个tasklet。

    • 启用Tasklet
    static inline void tasklet_enable(struct tasklet_struct *t)
    {
    	smp_mb__before_atomic();
    	atomic_dec(&t->count);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 禁用tasklet
    // 同步
    static inline void tasklet_disable(struct tasklet_struct *t)
    {
    	tasklet_disable_nosync(t);
    	tasklet_unlock_wait(t);
    	smp_mb();
    }
    
    //异步,执行函数后立即返回
    static inline void tasklet_disable_nosync(struct tasklet_struct *t)
    {
    	atomic_inc(&t->count);
    	smp_mb__after_atomic();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    tasklet调度

    以下两个为tasklet调度函数,具体取决于tasklet是具有正常优先级还是更高优先级。

    void tasklet_schedule(struct tasklet_struct *t);
    void tasklet_hi_schedule(struct tasklet_struct *t);
    
    • 1
    • 2

    内核将普通优先级和高优先级的Tasklet维护在两个不同的链表中

    • tasklet_schedule
      将tasklet添加到普通优先级链表中,用TASKLET_SOFTIRQ标志调度相关的softirq。

    • tasklet_hi_schedule
      将tasklet添加到高优先级链表中,并用HI_SOFTIRQ标志调度相关softirq,高优先级旨在用于具有低延时要求的软件中断处理程序。

    • 在已经被调度但尚未开始执行的tasklet上调用task_schedule将不会执行任何操作,该tasklet最终也仅执行一次。

    • 可以在tasklet中调用tasklet_schedule,意味着tasklet可以重新调度自己。

    • 高优先级tasklet总是在正常优先级的tasklet之前执行,滥用高优先级任务会增加系统延迟,一定要在真正需要快速执行时再使用。

    • tasklet_kill

    void tasklet_kill(struct tasklet_struct *t)
    {
    	if (in_interrupt())
    		pr_notice("Attempt to kill tasklet from interrupt\n");
    
    	while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
    		wait_var_event(&t->state, !test_bit(TASKLET_STATE_SCHED, &t->state));
    
    	tasklet_unlock_wait(t);
    	tasklet_clear_sched(t);
    }
    EXPORT_SYMBOL(tasklet_kill);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    调用task_kill可以停止一个tasklet,这个函数的主要作用时防止tasklet再次运行或者该tasklet当前计划运行时,会等待其执行完成后再杀掉它。

    • 使用实例
    // tasklet.c
    #include 
    #include 
    #include 
    
    char tasklet_data[] = "We use a string, but it could be pointer to a structure";
    
    void tasklet_function(unsigned long data)
    {
    	printk("%s\n", (char *)data);
    }
    
    DEFINE_TASKLET(test_tasklet, tasklet_function, (unsgined long)tasklet_data);
    
    static int __init test_init(void)
    {
    	tasklet_schedule(&test_tasklet);
    	return 0;
    }
    
    void test_exit(void)
    {
            printk("Waitqueue example cleanup!\n");
    }
    
    module_init(test_init);
    module_exit(test_exit);
    MODULE_LICENSE("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
  • 相关阅读:
    取模和取余
    安全学习_开发相关_Java第三方组件Log4j&FastJSON及相关安全问题简介
    ROS OpenCV 级联分类器
    Vue绑定style和class 对象写法
    Floyd算法基础
    外包干了3个月,技术倒退2年。。。
    风格迁移篇---U-GAT-IT:图像到图像翻译的无监督生成注意网络
    动态规划之区间DP详解
    小样本学习导论
    java计算机毕业设计家教到家平台源码+mysql数据库+系统+lw文档+部署
  • 原文地址:https://blog.csdn.net/qq_42931917/article/details/127896770