Linux驱动器中断服务程序技巧softirq/tasklet/workq

什么是上下文切换

上下文切换,有时也称为进程切换或任务切换,是CPU中央处理单元)从一个进程,或线程切换到另一个进程或线程。

进程,有时也称为任务,是程序的执行实例。在 Linux 中,线程是轻量级进程,可以并行运行,并与其父进程(即创建它们的进程)共享地址空间和其他资源。

上下文是指CPU寄存器和程序计数器在任何时间点的内容。寄存器是 CPU 内部的少量,非常快的内存,用于通过提供对常用值的快速访问,来加速计算机程序的执行,通常是计算的中间值。程序计数器是一种专用寄存器,用于指示 CPU 在其指令序列中的位置,并根据特定系统保存正在执行的指令的地址,或要执行的下一条指令的地址。

上下文切换可以更详细地描述为操作系统的核心对CPU上的进程(包括线程)执行以下活动:

  • 暂停一个进程的进程,并存储内存中某个进程的CPU状态(即上下文)
  • 从内存中检索下一个进程的上下文,并将其恢复到CPU的寄存器中
  • 返回到程序计数器指示的位置(即,返回到进程被中断的代码行)以恢复进程。

上下文切换有时被描述为内核暂停 CPU 上一个进程的执行,并恢复先前暂停的某个其他进程的执行。

上下文切换和模式切换

上下文切换只能在内核模式下发生。内核模式是CPU的一种特权模式,其中只有内核运行,并提供对所有内存位置和所有其他系统资源的访问。其他程序,包括应用程序,最初在用户模式下运行,但它们可以通过系统调用,运行部分内核代码。系统调用是类 Unix 操作系统中活动进程(即当前在 CPU 中运行的进程)对内核执行的服务(例如输入/输出 (I/O) 或进程创建)的请求(即,创建一个新进程)。 I/O 可以定义为进出 CPU 和主存储器(即 RAM)组合的任何信息移动,即该组合与计算机用户之间的通信(例如,通过键盘或鼠标),其存储设备(例如,磁盘或磁带驱动器)或其他计算机。

这两种模式在类 Unix 操作系统中的存在,意味着当系统调用导致 CPU 切换到内核模式时,类似但更简单的操作是必要的。这被称为模式切换,而不是上下文切换,因为它不会更改当前进程。

上下文切换是多任务操作系统的基本特征。多任务操作系统是多个进程在单个 CPU 上看似同时执行,且互不干扰的操作系统。这种并发错觉是通过快速连续(每秒数十或数百次)发生的上下文切换来实现的。这些上下文切换的发生是由于进程自愿放弃其在 CPU 中的时间,或调度程序在进程用完其CPU时间片时进行切换。

上下文切换也可以作为硬件中断的结果发生,硬件中断是从硬件设备(例如键盘、鼠标、调制解调器或系统时钟)到内核的信号,表明事件(例如,按键、鼠标移动)或来自网络连接的数据到达)发生。

Intel 80386 和更高版本的 CPU 包含对上下文切换的硬件支持。然而,大多数现代操作系统执行软件上下文切换,它可以在任何 CPU 上使用,而不是硬件上下文切换,以试图获得改进的性能。软件上下文切换首先在 Linux 中,为具有 2.4 内核的 Intel 兼容处理器实现。

软件上下文切换的一个主要优点是,虽然硬件机制保存了几乎所有的 CPU 状态,但软件可以更有选择性,只保存实际需要保存和重新加载的部分。然而,对于这在提高上下文切换效率方面究竟有多重要,存在一些问题。它的拥护者还声称,软件上下文切换允许改进切换代码的​​可能性,从而进一步提高效率,并允许更好地控制正在加载的数据的有效性。

运行上下文

在任何时候,系统中的每个 CPU 都可以:

  • 与任何进程无关,服务于硬件中断
  • 与任何进程都不相关联,服务于软中断或小任务
  • 与进程(用户上下文)相关联,在内核空间中运行

在用户空间运行进程

这之间有一个排序。最底层的两个可以互相抢占,但在其之上是一个严格的等级制度:每个只能被它上面的人抢占。
当一个软中断在 CPU 上运行时,没有其他软中断会抢占它,但硬件中断可以。但是,系统中的任何其他 CPU 都是独立执行的。

我们将看到用户上下文可以通过多种方式阻止中断,从而成为真正不可抢占的。

硬件中断(硬 IRQ)

计时器滴答声、网卡和键盘是可随时产生中断的真实硬件示例。内核运行中断处理程序,为硬件提供服务。内核保证此处理程序永远不会重新进入:如果相同的中断到达,则将其排队(或丢弃)。因为它禁用中断,所以这个处理程序必须很快:它经常简单地确认中断,标记一个“软件中断”以供执行并退出。

您可以判断您处于硬件中断状态,因为 in_irq() 返回 true。

软件中断/Tasklet上下文

  • 系统调用即将返回用户空间。
  • 硬件中断处理程序退出时,

任何标记为挂起(通常由硬件中断)的“软件中断”都会运行(kernel/softirq.c)。

许多真正的中断处理工作都在这里完成。在过渡到 SMP 的早期,只有“下半部分”(BH),它们没有利用多个CPU。

include/linux/interrupt.h 列出了不同的软中断。一个非常重要的软中断是,计时器软中断(包括/linux/timer.h)。

软中断通常很难处理,因为同一个软中断会在多个CPU上同时运行。出于这个原因,tasklet (include/linux/interrupt.h) 更常用:它们是可动态注册的(意味着您可以拥有任意数量的),并且它们还保证任何 tasklet 只能在一个 CPU 上运行任何时候,尽管不同的小任务可以同时运行。

Softirqs

Softirq Index (priority) Description
HI_SOFTIRQ 0 处理高优先级的tasklet
TIMER_SOFTIRQ 1 与定时器中断相关的tasklet
NET_TX_SOFTIRQ 2 将数据包发送到网卡
NET_RX_SOFTIRQ 3 从网卡接收数据包
SCSI_SOFTIRQ 4 SCSI 命令的中断后处理
TASKLET_SOFTIRQ 5 处理常规tasklet

HI_SOFTIRQ/TASKLET_SOFTIRQ

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);
}

TIMER_SOFTIRQ

计时器APIs

void add_timer(struct timer_list *timer)
void add_timer_on(struct timer_list *timer, int cpu)
int mod_timer(struct timer_list *timer, unsigned long expires)
int timer_reduce(struct timer_list *timer, unsigned long expires)
int del_timer_sync(struct timer_list *timer)
int try_to_del_timer_sync(struct timer_list *timer)
int del_timer(struct timer_list *timer)

在I2C驱动器中的应用

这是PNX010x/PNX4008板上的I2C驱动器的计时器应用。

计时器超时函数
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值