前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Linux内核中的软中断、tasklet和工作队列具体解释

Linux内核中的软中断、tasklet和工作队列具体解释

作者头像
全栈程序员站长
发布于 2022-07-20 04:54:40
发布于 2022-07-20 04:54:40
2.4K00
代码可运行
举报
运行总次数:0
代码可运行

大家好,又见面了,我是全栈君。

[TOC] 本文基于Linux2.6.32内核版本号。

引言

软中断、tasklet和工作队列并非Linux内核中一直存在的机制,而是由更早版本号的内核中的“下半部”(bottom half)演变而来。

下半部的机制实际上包含五种,但2.6版本号的内核中。下半部和任务队列的函数都消失了,仅仅剩下了前三者。 介绍这三种下半部实现之前。有必要说一下上半部与下半部的差别。

上半部指的是中断处理程序,下半部则指的是一些尽管与中断有相关性可是能够延后运行的任务。

举个样例:在网络传输中。网卡接收到数据包这个事件不一定须要立即被处理,适合用下半部去实现;可是用户敲击键盘这种事件就必须立即被响应,应该用中断实现。 两者的主要差别在于:中断不能被同样类型的中断打断。而下半部依旧能够被中断打断;中断对于时间很敏感,而下半部基本上都是一些能够延迟的工作。由于二者的这种差别,所以对于一个工作是放在上半部还是放在下半部去运行,能够參考以下4条:

  1. 假设一个任务对时间很敏感。将其放在中断处理程序中运行。
  2. 假设一个任务和硬件相关,将其放在中断处理程序中运行。
  3. 假设一个任务要保证不被其它中断(特别是同样的中断)打断,将其放在中断处理程序中运行。
  4. 其它全部任务,考虑放在下半部去运行。 有写内核任务须要延后运行。因此才有的下半部,进而实现了三种实现下半部的方法。这就是本文要讨论的软中断tasklet工作队列

下表能够更直观的看到它们之间的关系。

软中断

软中断作为下半部机制的代表,是随着SMP(share memory processor)的出现应运而生的,它也是tasklet实现的基础(tasklet实际上仅仅是在软中断的基础上加入了一定的机制)。软中断通常是“可延迟函数”的总称,有时候也包含了tasklet(请读者在遇到的时候依据上下文判断是否包含tasklet)。它的出现就是由于要满足上面所提出的上半部和下半部的差别,使得对时间不敏感的任务延后运行,而且能够在多个CPU上并行运行。使得总的系统效率能够更高。

它的特性包含:

  • 产生后并非立即能够运行,必须要等待内核的调度才干运行。软中断不能被自己打断(即单个cpu上软中断不能嵌套运行)。仅仅能被硬件中断打断(上半部)。
  • 能够并发运行在多个CPU上(即使同一类型的也能够)。所以软中断必须设计为可重入的函数(同意多个CPU同一时候操作),因此也须要使用自旋锁来保其数据结构

相关数据结构

  • 软中断描写叙述符 struct softirq_action{ void (*action)(struct softirq_action *);}; 描写叙述每一种类型的软中断,其中void(*action)是软中断触发时的运行函数。
  • 软中断全局数据和类型
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;  
    enum  
    {  
       HI_SOFTIRQ=0, /*用于高优先级的tasklet*/  
       TIMER_SOFTIRQ, /*用于定时器的下半部*/  
       NET_TX_SOFTIRQ, /*用于网络层发包*/  
       NET_RX_SOFTIRQ, /*用于网络层收报*/  
       BLOCK_SOFTIRQ,  
       BLOCK_IOPOLL_SOFTIRQ,  
       TASKLET_SOFTIRQ, /*用于低优先级的tasklet*/  
       SCHED_SOFTIRQ,  
       HRTIMER_SOFTIRQ,  
       RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */  
       NR_SOFTIRQS  
   };

相关API

  • 注冊软中断
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void open_softirq(int nr, void (*action)(struct softirq_action *))

即注冊相应类型的处理函数到全局数组softirq_vec中。比如网络发包相应类型为NET_TX_SOFTIRQ的处理函数net_tx_action.

  • 触发软中断
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void raise_softirq(unsigned int nr)

实际上即以软中断类型nr作为偏移量置位每cpu变量irq_stat[cpu_id]的成员变量__softirq_pending。这也是同一类型软中断能够在多个cpu上并行运行的根本原因。

  • 软中断运行函数
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
do_softirq-->__do_softirq

运行软中断处理函数__do_softirq前首先要满足两个条件: (1)不在中断中(硬中断、软中断和NMI) 。1 (2)有软中断处于pending状态。

系统这么设计是为了避免软件中断在中断嵌套中被调用,而且达到在单个CPU上软件中断不能被重入的目的。对于ARM架构的CPU不存在中断嵌套中调用软件中断的问题,由于ARM架构的CPU在处理硬件中断的过程中是关闭掉中断的。

仅仅有在进入了软中断处理过程中之后才会开启硬件中断,假设在软件中断处理过程中有硬件中断嵌套,也不会再次调用软中断,because硬件中断是软件中断处理过程中再次进入的,此时preempt_count已经记录了软件中断!

对于其它架构的CPU,有可能在触发调用软件中断前,也就是还在处理硬件中断的时候,就已经开启了硬件中断,可能会发生中断嵌套,在中断嵌套中是不同意调用软件中断处理的。Why?我的理解是,在发生中断嵌套的时候,表明这个时候是系统突发繁忙的时候,内核第一要务就是赶紧把中断中的事情处理完毕,退出中断嵌套。避免多次嵌套,哪里有时间处理软件中断。所以把软件中断推迟到了全部中断处理完毕的时候才干触发软件中断。

实现原理和实例

软中断的调度时机:

  1. do_irq完毕I/O中断时调用irq_exit。
  2. 系统使用I/O APIC,在处理完本地时钟中断时。
  3. local_bh_enable,即开启本地软中断时。
  4. SMP系统中。cpu处理完被CALL_FUNCTION_VECTOR处理器间中断所触发的函数时。
  5. ksoftirqd/n线程被唤醒时。 以下以从中断处理返回函数irq_exit中调用软中断为例详细说明。 触发和初始化的的流程如图所看到的:

软中断处理流程

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
asmlinkage void __do_softirq(void)
{
    struct softirq_action *h;
    __u32 pending;
    int max_restart = MAX_SOFTIRQ_RESTART;
    int cpu;

    pending = local_softirq_pending();
    account_system_vtime(current);

    __local_bh_disable((unsigned long)__builtin_return_address(0));
    lockdep_softirq_enter();

    cpu = smp_processor_id();
restart:
    /* Reset the pending bitmask before enabling irqs */
    set_softirq_pending(0);

    local_irq_enable();

    h = softirq_vec;

    do {
        if (pending & 1) {
            int prev_count = preempt_count();
            kstat_incr_softirqs_this_cpu(h - softirq_vec);

            trace_softirq_entry(h, softirq_vec);
            h->action(h);
            trace_softirq_exit(h, softirq_vec);
            if (unlikely(prev_count != preempt_count())) {
                printk(KERN_ERR "huh, entered softirq %td %s %p"
                       "with preempt_count %08x,"
                       " exited with %08x?

\n"

, h - softirq_vec, softirq_to_name[h - softirq_vec], h->action, prev_count, preempt_count()); preempt_count() = prev_count; } rcu_bh_qs(cpu); } h++; pending >>= 1; } while (pending); local_irq_disable(); pending = local_softirq_pending(); if (pending && --max_restart) goto restart; if (pending) wakeup_softirqd(); lockdep_softirq_exit(); account_system_vtime(current); _local_bh_enable(); }

  1. 首先调用local_softirq_pending函数取得眼下有哪些位存在软件中断。
  2. 调用__local_bh_disable关闭软中断,事实上就是设置正在处理软件中断标记,在同一个CPU上使得不能重入__do_softirq函数。
  3. 又一次设置软中断标记为0,set_softirq_pending又一次设置软中断标记为0,这样在之后又一次开启中断之后硬件中断中又能够设置软件中断位。
  4. 调用local_irq_enable。开启硬件中断。
  5. 之后在一个循环中。遍历pending标志的每一位,假设这一位设置就会调用软件中断的处理函数。在这个过程中硬件中断是开启的,随时能够打断软件中断。这样保证硬件中断不会丢失。
  6. 之后关闭硬件中断(local_irq_disable),查看是否又有软件中断处于pending状态。假设是,而且在本次调用__do_softirq函数过程中没有累计反复进入软件中断处理的次数超过max_restart=10次,就能够又一次调用软件中断处理。假设超过了10次,就调用wakeup_softirqd()唤醒内核的一个进程来处理软件中断。设立10次的限制。也是为了避免影响系统响应时间。
  7. 调用_local_bh_enable开启软中断。

软中断内核线程

之前我们分析的触发软件中断的位置事实上是中断上下文中,而在软中断的内核线程中实际已经是进程的上下文。 这里说的软中断上下文指的就是系统为每一个CPU建立的ksoftirqd进程。 软中断的内核进程中主要有两个大循环,外层的循环处理有软件中断就处理。没有软件中断就休眠。内层的循环处理软件中断,每循环一次都试探一次是否过长时间占领了CPU,须要调度就释放CPU给其它进程。详细的操作在凝视中做了解释。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    set_current_state(TASK_INTERRUPTIBLE);
    //外层大循环。
    while (!kthread_should_stop()) {
        preempt_disable();//禁止内核抢占,自己掌握cpu
        if (!local_softirq_pending()) {
            preempt_enable_no_resched();
            //假设没有软中断在pending中就让出cpu
            schedule();
            //调度之后又一次掌握cpu
            preempt_disable();
        }

        __set_current_state(TASK_RUNNING);

        while (local_softirq_pending()) {
            /* Preempt disable stops cpu going offline. If already offline, we'll be on wrong CPU: don't process */
            if (cpu_is_offline((long)__bind_cpu))
                goto wait_to_die;
            //有软中断则開始软中断调度
            do_softirq();
            //查看是否须要调度,避免一直占用cpu
            preempt_enable_no_resched();
            cond_resched();
            preempt_disable();
            rcu_sched_qs((long)__bind_cpu);
        }
        preempt_enable();
        set_current_state(TASK_INTERRUPTIBLE);
    }
    __set_current_state(TASK_RUNNING);
    return 0;

wait_to_die: preempt_enable();
    /* Wait for kthread_stop */
    set_current_state(TASK_INTERRUPTIBLE);
    while (!kthread_should_stop()) {
        schedule();
        set_current_state(TASK_INTERRUPTIBLE);
    }
    __set_current_state(TASK_RUNNING);
    return 0;

tasklet

由于软中断必须使用可重入函数,这就导致设计上的复杂度变高。作为设备驱动程序的开发人员来说,添加了负担。而假设某种应用并不须要在多个CPU上并行运行,那么软中断事实上是没有必要的。

因此诞生了弥补以上两个要求的tasklet。它具有以下特性: a)一种特定类型的tasklet仅仅能运行在一个CPU上,不能并行,仅仅能串行运行。 b)多个不同类型的tasklet能够并行在多个CPU上。

c)软中断是静态分配的。在内核编译好之后,就不能改变。但tasklet就灵活很多,能够在运行时改变(比方加入模块时)。

tasklet是在两种软中断类型的基础上实现的。因此假设不须要软中断的并行特性,tasklet就是最好的选择。也就是说tasklet是软中断的一种特殊使用方法。即延迟情况下的串行运行

相关数据结构

  • tasklet描写叙述符
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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链表
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec);//低优先级
static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec);//高优先级

相关API

  • 定义tasklet
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#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 } 
//定义名字为name的激活tasklet
void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long data)
//动态初始化tasklet
  • tasklet操作
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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)
//使能一个之前被disable的tasklet。若这个tasklet已经被调度, 它会很快运行。

tasklet_enable和tasklet_disable必须匹配调用, 由于内核跟踪每一个tasklet的"禁止次数"

static inline void tasklet_schedule(struct tasklet_struct *t) //调度 tasklet 运行,假设tasklet在运行中被调度, 它在完毕后会再次运行; 这保证了在其它事件被处理其中发生的事件受到应有的注意. 这个做法也同意一个 tasklet 又一次调度它自己 tasklet_hi_schedule(struct tasklet_struct *t) //和tasklet_schedule相似,仅仅是在更高优先级运行。当软中断处理运行时, 它处理高优先级 tasklet 在其它软中断之前,仅仅有具有低响应周期要求的驱动才应使用这个函数, 可避免其它软件中断处理引入的附加周期. tasklet_kill(struct tasklet_struct *t) //确保了 tasklet 不会被再次调度来运行,通常当一个设备正被关闭或者模块卸载时被调用。假设 tasklet 正在运行, 这个函数等待直到它运行完毕。

若 tasklet 又一次调度它自己,则必须阻止在调用 tasklet_kill 前它又一次调度它自己,如同使用 del_timer_sync

实现原理

  • 调度原理
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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)
{
    unsigned long flags;

    local_irq_save(flags);
    t->next = NULL;
    *__get_cpu_var(tasklet_vec).tail = t;
    __get_cpu_var(tasklet_vec).tail = &(t->next);//加入低优先级列表
    raise_softirq_irqoff(TASKLET_SOFTIRQ);//触发软中断
    local_irq_restore(flags);
}
  • tasklet运行过程 TASKLET_SOFTIRQ相应运行函数为tasklet_action。HI_SOFTIRQ为tasklet_hi_action,以tasklet_action为例说明。tasklet_hi_action大同小异。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static void tasklet_action(struct softirq_action *a)
{
    struct tasklet_struct *list;

    local_irq_disable();
    list = __get_cpu_var(tasklet_vec).head;
    __get_cpu_var(tasklet_vec).head = NULL;
    __get_cpu_var(tasklet_vec).tail = &__get_cpu_var(tasklet_vec).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;
        *__get_cpu_var(tasklet_vec).tail = t;
        __get_cpu_var(tasklet_vec).tail = &(t->next);
        __raise_softirq_irqoff(TASKLET_SOFTIRQ);
        local_irq_enable();
    }
}

工作队列

从上面的介绍看以看出,软中断运行在中断上下文中。因此不能堵塞和睡眠。而tasklet使用软中断实现。当然也不能堵塞和睡眠。但假设某延迟处理函数须要睡眠或者堵塞呢?没关系工作队列就能够如您所愿了。 把推后运行的任务叫做工作(work),描写叙述它的数据结构为work_struct ,这些工作以队列结构组织成工作队列(workqueue),其数据结构为workqueue_struct 。而工作线程就是负责运行工作队列中的工作。系统默认的工作者线程为events。 工作队列(work queue)是第二种将工作推后运行的形式。

工作队列能够把工作推后,交由一个内核线程去运行—这个下半部分总是会在进程上下文运行,但由于是内核线程,其不能訪问用户空间。最重要特点的就是工作队列同意又一次调度甚至是睡眠

通常。在工作队列和软中断/tasklet中作出选择很easy。可使用以下规则: – 假设推后运行的任务须要睡眠,那么仅仅能选择工作队列。

– 假设推后运行的任务须要延时指定的时间再触发,那么使用工作队列。由于其能够利用timer延时(内核定时器实现)。 – 假设推后运行的任务须要在一个tick之内处理。则使用软中断或tasklet。由于其能够抢占普通进程和内核线程。同一时候不可睡眠。

– 假设推后运行的任务对延迟的时间没有不论什么要求。则使用工作队列,此时通常为无关紧要的任务。 实际上。工作队列的本质就是将工作交给内核线程处理,因此其能够用内核线程替换。

可是内核线程的创建和销毁对编程者的要求较高,而工作队列实现了内核线程的封装,不易出错,所以我们也推荐使用工作队列。

相关数据结构

  • 正常工作结构体
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct work_struct {
    atomic_long_t data; //传递给工作函数的參数
#define WORK_STRUCT_PENDING 0 /* T if work item pending execution */
#define WORK_STRUCT_FLAG_MASK (3UL)
#define WORK_STRUCT_WQ_DATA_MASK (~WORK_STRUCT_FLAG_MASK)
    struct list_head entry; //链表结构。链接同一工作队列上的工作。
    work_func_t func; //工作函数,用户自己定义实现
#ifdef CONFIG_LOCKDEP
    struct lockdep_map lockdep_map;
#endif
};
//工作队列运行函数的原型:
void (*work_func_t)(struct work_struct *work);
//该函数会由一个工作者线程运行,因此其在进程上下文中。能够睡眠也能够中断。但仅仅能在内核中运行,无法訪问用户空间。
  • 延迟工作结构体(延迟的实现是在调度时延迟插入相应的工作队列)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct delayed_work {
    struct work_struct work;
    struct timer_list timer; //定时器。用于实现延迟处理
};
  • 工作队列结构体
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct workqueue_struct {
    struct cpu_workqueue_struct *cpu_wq; //指针数组,其每一个元素为per-cpu的工作队列
    struct list_head list;
    const char *name;
    int singlethread; //标记是否仅仅创建一个工作者线程
    int freezeable;     /* Freeze threads during suspend */
    int rt;
#ifdef CONFIG_LOCKDEP
    struct lockdep_map lockdep_map;
#endif
};
  • 每cpu工作队列(每cpu都相应一个工作者线程worker_thread)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct cpu_workqueue_struct {
    spinlock_t lock;
    struct list_head worklist;
    wait_queue_head_t more_work;
    struct work_struct *current_work;
    struct workqueue_struct *wq;
    struct task_struct *thread;
} ____cacheline_aligned;

相关API

  • 缺省工作队列
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
静态创建 
DECLARE_WORK(name,function); //定义正常运行的工作项
DECLARE_DELAYED_WORK(name,function);//定义延后运行的工作项

动态创建
INIT_WORK(_work, _func) //创建正常运行的工作项
INIT_DELAYED_WORK(_work, _func)//创建延后运行的工作项

调度默认工作队列
int schedule_work(struct work_struct *work)

//对正常运行的工作进行调度,即把给定工作的处理函数提交给缺省的工作队列和工作者线程。工作者线程本质上是一个普通的内核线程。在默认情况下。每一个CPU均有一个类型为“events”的工作者线程,当调用schedule_work时,这个工作者线程会被唤醒去运行工作链表上的全部工作。

系统默认的工作队列名称是:keventd_wq,默认的工作者线程叫:events/n。这里的n是处理器的编号,每一个处理器相应一个线程。比方。单处理器的系统仅仅有events/0这样一个线程。

而双处理器的系统就会多一个events/1线程。

默认的工作队列和工作者线程由内核初始化时创建: start_kernel()-->rest_init-->do_basic_setup-->init_workqueues 调度延迟工作 int schedule_delayed_work(struct delayed_work *dwork,unsigned long delay) 刷新缺省工作队列 void flush_scheduled_work(void) //此函数会一直等待。直到队列中的全部工作都被运行。 取消延迟工作 static inline int cancel_delayed_work(struct delayed_work *work) //flush_scheduled_work并不取消不论什么延迟运行的工作,因此。假设要取消延迟工作,应该调用cancel_delayed_work。

以上均是採用缺省工作者线程来实现工作队列。其长处是简单易用,缺点是假设缺省工作队列负载太重。运行效率会很低。这就须要我们创建自己的工作者线程和工作队列。

  • 自己定义工作队列
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
create_workqueue(name) 
//宏定义 返回值为工作队列,name为工作线程名称。创建新的工作队列和相应的工作者线程,name用于该内核线程的命名。

int queue_work(struct workqueue_struct *wq, struct work_struct *work)
//相似于schedule_work。差别在于queue_work把给定工作提交给创建的工作队列wq而不是缺省队列。

int queue_delayed_work(struct workqueue_struct *wq,struct delayed_work *dwork, unsigned long delay)
//调度延迟工作。

void flush_workqueue(struct workqueue_struct *wq)
//刷新指定工作队列。

void destroy_workqueue(struct workqueue_struct *wq) //释放创建的工作队列。

实现原理

  1. 工作队列的组织结构 即workqueue_struct、cpu_workqueue_struct与work_struct的关系。 一个工作队列相应一个work_queue_struct。工作队列中每cpu的工作队列由cpu_workqueue_struct表示,而work_struct为其上的详细工作。 关系例如以下图所看到的:

2.工作队列的工作过程

  1. 应用实例 linux各个接口的状态(up/down)的消息须要通知netdev_chain上感兴趣的模块同一时候上报用户空间消息。这里使用的就是工作队列。 详细流程图例如以下所看到的:

  1. 是否处于中断中在Linux中是通过preempt_count来判断的,详细例如以下: 在linux系统的进程数据结构里,有这么一个数据结构: #define preempt_count() (current_thread_info()->preempt_count) 利用preempt_count能够表示是否处于中断处理或者软件中断处理过程中,例如以下所看到的: # define hardirq_count() (preempt_count() & HARDIRQ_MASK) #define softirq_count() (preempt_count() & SOFTIRQ_MASK) #define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK | NMI_MASK)) #define in_irq() (hardirq_count()) #define in_softirq() (softirq_count()) #define in_interrupt() (irq_count())

preempt_count的8~23位记录中断处理和软件中断处理过程的计数。 假设有计数,表示系统在硬件中断或者软件中断处理过程中。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/108223.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年3月6,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Linux内核硬中断 / 软中断的原理和实现
从本质上来讲,中断是一种电信号,当设备有某种事件发生时,它就会产生中断,通过总线把电信号发送给中断控制器。
秃头哥编程
2019/10/09
23K1
Linux内核硬中断 / 软中断的原理和实现
有种中断是软的
Workqueue 工作队列是利用内核线程来异步执行工作任务的通用机制,利用进程上下文来执行中断处理中耗时的任务,因此它允许睡眠。而 Softirq 和 Tasklet 在处理任务时不能睡眠。Softirq 是内核中常见的一种下半部机制,适合系统对性能和实时响应要求很高的场合,比如网络子系统,块设备,高精度定时器,RCU 等。
刘盼
2021/11/15
9220
Linux内核中断顶半部和底半部的理解
  设备的中断会打断内核进程中的正常调度和运行,系统对更高吞吐率的追求势必要求中断服务程序尽量短小精悍。但是,这个良好的愿望往往与现实并不吻合。在大多数真实的系统中,当中断到来时,要完成的工作往往并不会是短小的,它可能要进行较大量的耗时处理。   下图描述了Linux内核的中断处理机制。为了在中断执行时间尽量短和中断处理需完成的工作尽量大之间找到一个平衡点,Linux将中断处理程序分解为两个半部:顶半部和底半部。
嵌入式与Linux那些事
2021/05/20
1.9K0
Linux内核中断顶半部和底半部的理解
Linux内核22-软中断和tasklet
在之前的文章中,讲解中断处理相关的概念的时候,提到过有些任务不是紧急的,可以延后一段时间执行。因为中断服务例程都是顺序执行的,在响应一个中断的时候不应该被打断。相反,这些可延时任务执行时,可以使能中断。那么,将这些任务从中断处理程序中剥离出来,可以有效地保证内核对于中断响应时间尽可能短。这对于时间苛刻的应用来说,这是一个很重要的属性,尤其是那些要求中断请求必须在毫秒级别响应的应用。
Tupelo
2022/08/15
1.7K0
Linux的中断下半部机制的对比
 中断服务程序一般都是在中断请求关闭的条件下执行的,以避免嵌套而使中断控制复杂化。但是,中断是一个随机事件,它随时会到来,如果关中断的时间太长,CPU就不能及时响应其他的中断请求,从而造成中断的丢失。因此,Linux内核的目标就是尽可能快的处理完中断请求,尽其所能把更多的处理向后推迟。例如,假设一个数据块已经达到了网线,当中断控制器接受到这个中断请求信号时,Linux内核只是简单地标志数据到来了,然后让处理器恢复到它以前运行的状态,其余的处理稍后再进行(如把数据移入一个缓冲区,接受数据的进程就可以在缓冲区找到数据)。因此,内核把中断处理分为两部分:上半部(tophalf)和下半部(bottomhalf),上半部(就是中断服务程序)内核立即执行,而下半部(就是一些内核函数)留着稍后处理。
灯珑LoGin
2024/02/06
4890
Linux的中断下半部机制的对比
Linux调度系统全景指南(上篇)
| 导语 本文主要是讲Linux的调度系统, 由于全部内容太多,分三部分来讲,调度可以说是操作系统的灵魂,为了让CPU资源利用最大化,Linux设计了一套非常精细的调度系统,对大多数场景都进行了很多优化,系统扩展性强,我们可以根据业务模型和业务场景的特点,有针对性的去进行性能优化,在保证客户网络带宽前提下,隔离客户互相之间的干扰影响,提高CPU利用率,降低单位运算成本,提高市场竞争力。欢迎大家相互交流学习!
刘盼
2021/03/10
1.6K0
Linux调度系统全景指南(上篇)
硬中断和软中断_软中断和硬中断的优先级
从本质上来讲,中断是一种电信号,当设备有某种事件发生时,它就会产生中断,通过总线把电信号发送给中断控制器。
全栈程序员站长
2022/11/03
2.8K0
软中断SOFTIRQ
软中断的出现和linux系统对中断的划分是分不开的。linux系统将整个中断处理过程分为了两部分,分别为上半部(Top Half)和下半部(Bottom Half),之所以要这样分是因为关闭中断的时间不能过长,也就是在关闭中断期间尽可能少干事,否则影响整个系统的性能。所以linux系统将中断处理分为两部分,在上半部全程关闭中断,下半部打开中断。而在上半部主要干一些和硬件有关的操作,速度快,在下部分做一些耗时的操作。这样一来既能保证系统效率又能处理各种中断。
DragonKingZhu
2020/03/24
2.4K0
Linux设备驱动workqueue(工作队列)案例实现
工作队列(work queue)是另外一种将工作推后执行的形式,tasklet(小任务机制)有所不同。工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个下半部分可以在进程上下文中执行。这样,通过工作队列执行的代码能占尽进程上下文的所有优势。最重要的就是工作队列允许被重新调度甚至是睡眠。
杨源鑫
2019/07/04
5.5K0
Linux内核23-工作队列
Linux2.6版本中引入了工作队列概念,代替Linux2.4版本中的任务队列。用以实现注册激活某些函数,留待稍后由工作线程执行(与tasklet的处理类似)。
Tupelo
2022/08/15
1.2K0
【深度】韦东山:一文看看尽linux对中断处理的前世今生
从2005年我接触Linux到现在15年了,Linux中断系统的变化并不大。比较重要的就是引入了threaded irq:使用内核线程来处理中断。
韦东山
2020/09/30
9420
Linux 软中断机制分析
软中断分析最近工作繁忙,没有时间总结内核相关的一些东西。上次更新博客到了linux内核中断子系统。这次总结一下软中断,也就是softirq。之后还会总结一些tasklet、工作队列机制。 1.为什么要软中断 编写驱动的时候,一个中断产生之后,内核在中断处理函数中可能需要完成很多工作。但是中断处理函数的处理是关闭了中断的。也就是说在响应中断时,系统不能再次响应外部的其它中断。这样的后果会造成有可能丢失外部中断。于是,linux内核设计出了一种架构,中断函数需要处理的任务分为两部分,一部分在中断处理函数中执
小小科
2018/05/03
8.9K0
Linux 软中断机制分析
一文完全读懂 | Linux中断处理
中断 是为了解决外部设备完成某些工作后通知CPU的一种机制(譬如硬盘完成读写操作后通过中断告知CPU已经完成)。早期没有中断机制的计算机就不得不通过轮询来查询外部设备的状态,由于轮询是试探查询的(也就是说设备不一定是就绪状态),所以往往要做很多无用的查询,从而导致效率非常低下。由于中断是由外部设备主动通知CPU的,所以不需要CPU进行轮询去查询,效率大大提升。
用户7686797
2021/11/05
3.2K0
Linux 中断处理浅析
最近在研究异步消息处理, 突然想起linux内核的中断处理, 里面由始至终都贯穿着”重要的事马上做, 不重要的事推后做”的异步处理思想. 于是整理一下~ 第一阶段 获取中断号 每个CPU都有响应中断的
小小科
2018/05/04
7.6K0
Linux 中断处理浅析
Linux驱动实践:中断处理中的【工作队列】 workqueue 是什么鬼?
大家好,我是道哥,今天我为大伙儿解说的技术知识点是:【中断处理中的下半部分机制-工作队列】。
IOT物联网小镇
2021/12/28
2.1K0
Linux驱动实践:中断处理中的【工作队列】 workqueue 是什么鬼?
Linux中断下半部实现机制
由于内核中中断不允许嵌套,在程序进入中断后,系统会关闭中断接收,这段时间内,其他中断都无法处理导致中断无法响应,因此需要当前进入的中断子服务函数越快越好。但是在一些特殊情况下,中断要处理的事情可能是复杂且冗长的,为解决这种问题, 中断上下半部的概念顺势而生。将中断拆成两部分,上半部用来处理紧急的事情;下半部用来处理不紧急的事情。
开源519
2020/07/23
3.3K0
Linux中断处理
由于 APIC中断控制器 有点小复杂,所以本文主要通过 8259A中断控制器 来介绍Linux对中断的处理过程。
用户7686797
2020/08/25
6.9K0
扒开 Linux 中断的底裤之 workqueue
workqueue 是除了 softirq 和 tasklet 以外最常用的下半部机制之一。workqueue 的本质是把 work 交给一个内核线程,在进程上下文调度的时候执行。因为这个特点,所以 workqueue 允许重新调度和睡眠,这种异步执行的进程上下文,能解决因为 softirq 和 tasklet 执行时间长而导致的系统实时性下降等问题。
刘盼
2021/08/25
2.3K0
扒开 Linux 中断的底裤之 workqueue
workqueue
在之前的softirq中提到过,内核在中断的bottom half引入了softirq, tasklet, workqueue。 而softirq和tasklet只能用在中断上下文中,而且不可以睡眠。所以内核引入了workqueue,工作队列运行在进程上下文,同时可以睡眠。在以前版本的内核中workqueue的代码比较简单。在linux2.6.30代码量在1000行左右,而在linux3.18代码量在5000行左右。其中巨大的变化就是引入了Concurrency Managed Workqueue (cmwq)概念。不过本篇先学习以前版本的workqueue,因为它简单。在了解了简单版本的存在问题之后在学习cmwq就有更好的认识。
DragonKingZhu
2020/03/24
1.2K0
吐血整理 | 肝翻 Linux 中断所有知识点
GIC,Generic Interrupt Controller。是ARM公司提供的一个通用的中断控制器。主要作用为:接受硬件中断信号,并经过一定处理后,分发给对应的CPU进行处理。
刘盼
2021/08/25
4.1K1
吐血整理 | 肝翻 Linux 中断所有知识点
相关推荐
Linux内核硬中断 / 软中断的原理和实现
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验