首页 > 系统相关 >【Linux中断】中断下半部-tasklet的原理与使用

【Linux中断】中断下半部-tasklet的原理与使用

时间:2023-06-17 22:46:47浏览次数:72  
标签:head struct 中断 void static TASKLET Linux tasklet

tasklet特性

(1)一种特定类型的tasklet只能运行在一个CPU上,不能并行,只能串行执行
(2)多个不同的类型的tasklet可以并行在多个CPU上
(3)软中断是静态分配的,在内核编译好后,就不能再改变了。但tasklet灵活很多,可以在运行时改变

tasklet是在两种软中断类型的基础上实现的,因此如果不需要软中断的并行也行,tasklet就是最好的选择。所以也可以说tasklet是软中断的一种特殊用法,即延迟情况下的串行执行。

tasklet的数据结构

tasklet描述符

struct tasklet_struct
{
    struct tasklet_struct *next;    // 将多个tasklet链接成单向循环链表
    unsigned long state;            //TASKLET_STATE_SCHED(Tasklet is scheduled for execution)  TASKLET_STATE_RUN(Tasklet is running (SMP only))
    atomic_t count;                 // 0:激活tasklet   非0:禁用tasklet
    void (*func)(unsigned long);    // 用户自定义函数
    unsigned long data;             // 函数入参
};

tasklet链表

static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec);   // 低优先级
static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec);// 高优先级

tasklet API接口

// 定义名字为name的非激活tasklet
#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }

// 定义名字为name的激活tasklet
#define DECLARE_TASKLET_DISABLED(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }

//动态初始化tasklet
void tasklet_init(struct tasklet_struct *t,
             void (*func)(unsigned long), unsigned long data);

// 函数暂时禁止给定的tasklet被tasklet_schedule调度,直到这个tasklet被再次被enable;若这个tasklet当前在运行, 这个函数忙等待直到这个tasklet退出
static inline void tasklet_disable(struct tasklet_struct *t);

// 函数暂时禁止给定的tasklet被tasklet_schedule调度,直到这个tasklet被再次被enable;若这个tasklet当前在运行, 这个函数忙等待直到这个tasklet退出
static inline void tasklet_enable(struct tasklet_struct *t);

// 调度 tasklet 执行,如果tasklet在运行中被调度, 它在完成后会再次运行; 这保证了在其他事件被处理当中发生的事件受到应有的注意. 这个做法也允许一个 tasklet 重新调度它自己
static inline void tasklet_schedule(struct tasklet_struct *t);

// 调度 tasklet 执行,如果tasklet在运行中被调度, 它在完成后会再次运行; 这保证了在其他事件被处理当中发生的事件受到应有的注意. 这个做法也允许一个 tasklet 重新调度它自己
static inline void tasklet_hi_schedule(struct tasklet_struct *t);

// 调度 tasklet 执行,如果tasklet在运行中被调度, 它在完成后会再次运行; 这保证了在其他事件被处理当中发生的事件受到应有的注意. 这个做法也允许一个 tasklet 重新调度它自己
void tasklet_kill(struct tasklet_struct *t);

tasklet原理

tasklet调度原理

static inline void tasklet_schedule(struct tasklet_struct *t)
{
    if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
        __tasklet_schedule(t);
}

void __tasklet_schedule(struct tasklet_struct *t)
{
    __tasklet_schedule_common(t, &tasklet_vec,
                  TASKLET_SOFTIRQ);
}

static void __tasklet_schedule_common(struct tasklet_struct *t,
                      struct tasklet_head __percpu *headp,
                      unsigned int softirq_nr)
{
    struct tasklet_head *head;
    unsigned long flags;

    local_irq_save(flags);
    head = this_cpu_ptr(headp);
    t->next = NULL;
    *head->tail = t;
    head->tail = &(t->next);             // 加入tasklet列表
    raise_softirq_irqoff(softirq_nr);    // 触发软中断
    local_irq_restore(flags);
}

tasklet执行过程

TASKLET_SOFTIRQ对应执行函数为tasklet_action,HI_SOFTIRQ为tasklet_hi_action,以tasklet_action为例:

static __latent_entropy void tasklet_action(struct softirq_action *a)
{
    tasklet_action_common(a, this_cpu_ptr(&tasklet_vec), TASKLET_SOFTIRQ);
}

static void tasklet_action_common(struct softirq_action *a,
                  struct tasklet_head *tl_head,
                  unsigned int softirq_nr)
{
    struct tasklet_struct *list;

    local_irq_disable();
    list = tl_head->head;
    tl_head->head = NULL;
    tl_head->tail = &tl_head->head;   // 获取tasklet链表
    local_irq_enable();

    while (list) {
        struct tasklet_struct *t = list;

        list = list->next;

        if (tasklet_trylock(t)) {
            if (!atomic_read(&t->count)) {
                // 执行tasklet
                if (!test_and_clear_bit(TASKLET_STATE_SCHED,
                            &t->state))
                    BUG();
                t->func(t->data);
                tasklet_unlock(t);
                continue;
            }
            tasklet_unlock(t);
        }

        // 如果t->count的值不等于0,说明这个tasklet在调度之后,被disable掉了,所以会将tasklet结构体重新放回到tasklet_vec链表,并重新调度TASKLET_SOFTIRQ软中断,在之后enable这个tasklet之后重新再执行它
        local_irq_disable();
        t->next = NULL;
        *tl_head->tail = t;
        tl_head->tail = &t->next;
        __raise_softirq_irqoff(softirq_nr);
        local_irq_enable();
    }
}

img



















参考文章:
https://zhuanlan.zhihu.com/p/265705850

标签:head,struct,中断,void,static,TASKLET,Linux,tasklet
From: https://www.cnblogs.com/Wangzx000/p/17488378.html

相关文章

  • [rk3568]linux strip后可执行程序太大
    查看GCC工具是否存在优化,或者未优化导致,$CC -Q--help=optimizers查看开启的程度,如果有很多disable未进行优化像,在makefile中增加-O0,极度优化状态进行Thefollowingoptionscontroloptimizations:-O<number>-Ofast-Og-Os-faggressive-loop-optimizations......
  • 转载-linux与soc-移植U-Boot思路和实践 | 基于RK3399
    原文链接:https://mp.weixin.qq.com/s/T1BmaP2-XbJIpLNsFxKeEQ0.背景介绍我们手里这块RK3399开发板出厂时带的是2017.09版本的U-Boot。 U-Boot 2017.09 (Sep 26 2021 - 08:53:15 +0000)   Model: Forlinx OK3399 Evaluation Board Pr......
  • neon linux安装matlab2023a的离线文档
    1.changetodirectorycd/media/munication/59A4D5FD759E19972.mountR2023a_Doc_Linux.isosudomount-oloopR2023a_Doc_Linux.isocdrom/3.changetodirectorycdcdrom/bin/glnxa64/4.installdocsudo./mpminstall-doc--matlabroot=/usr/local/......
  • Linux编译静态库、动态库
     一、Linux上编译静态库#1.编译成.o文件gcc-ca.cb.c//2.编译成静态库ar-rliba.aa.ob.o//3.链接成可执行文件gccmain.c-omain-L[库所在路径]-l[库名]g++main.cpp-omain-L./-la  二、Linux上编译动态库//1.把cpp封装成so库g++-fPIC-sh......
  • Linux启动时间优化-内核和用户空间启动优化实践
    启动时间的优化,分为两大部分,分别是内核部分和用户空间两大部分。从内核timestamp0.000000作为内核启动起点,到free_initmem()输出"Freeinginitmemory"作为内核启动的终点。借助于bootgraph.py对内核的kmsg进行分析,输出bootgraph.html和initcall耗时csv文件。在紧接着free_i......
  • 爆肝万字带你超级详细全面了解Linux命令大全
    ......
  • Centos7 linux定时任务
    1、参考CentOS7定时任务crontab入门Centos利用crontab定时执行任务及配置方法2、crontab-l#查看当前用户定时任务crontab-e#编辑当前用户定时任务#prodbackupdatabase02***cd/data/xxx&&/usr/bin/shxxx_backup.sh#prodautoupdatemanageco......
  • linux挂载新磁盘
    linux挂载新磁盘1.老系统查看1.1查看磁盘挂载情况#df-hFilesystemSizeUsedAvailUse%Mountedonudev3.8G03.8G0%/devtmpfs770M1.3M769M1%/run/dev/vda159G48G9.0G85%/tmpfs3.8G03.8G0%/dev/shmtmpfs5.0M05.0M0%/run/locktmpfs3.8G0......
  • Linux操作系统——主观题
    第一章——虚拟机UbuntuLinux'在安装系统时,应该建立几个分区?每个分区的大小大致是多少?建立四个分区,第一个主分区/dev/sad1512MB,第二个逻辑分区/dev/sda510240MB,第三个交换分区/dev/sda61024MB,第四个个人文件分区/dev/sda79696MB。在一台主机上只能安装一个VMware虚拟机软......
  • Linux下常用命令
    scp用于上传,下载,两台服务器文件复制上传文件到服务器:scpusername@servername:/path/filename/tmp/local_destination从服务器上下载文件到本地:scp/path/local_filenameusername@servername:/path从服务器上下载整个目录到本地:scp-rusername@servername:remote_dir/......