第10章:中断处理-10: Tasklets

In continuation of the previous text 第10章:中断处理-9: Top and Bottom Halves let's GO ahead.

Tasklets

Remember that tasklets are a special function that may be scheduled to run, in soft ware interrupt context, at a system-determined safe time. They may be scheduled to run multiple times, but tasklet scheduling is not cumulative; the tasklet runs only once, even if it is requested repeatedly before it is launched. No tasklet ever runs in parallel with itself, since they run only once, but tasklets can run in parallel with other tasklets on SMP systems. Thus, if your driver has multiple tasklets, they must employ some sort of locking to avoid conflicting with each other.

回想一下,tasklets 是一种特殊函数,可被调度在软中断上下文中,于系统确定的 “安全时机” 运行。它们可以被多次调度,但调度不会累积 —— 即使在执行前被多次请求,tasklet 也只会运行一次。tasklet 绝不会与自身并行运行(因为仅执行一次),但在 SMP 系统中,不同 tasklet 之间可以并行运行。因此,若驱动有多个 tasklet,必须使用某种锁机制避免相互冲突。

Tasklets are also guaranteed to run on the same CPU as the function that first sched ules them. Therefore, an interrupt handler can be secure that a tasklet does not begin executing before the handler has completed. However, another interrupt can cer tainly be delivered while the tasklet is running, so locking between the tasklet and the interrupt handler may still be required.

Tasklets must be declared with the DECLARE_TASKLET macro:

tasklets 还保证在首次调度它的 CPU 上运行。因此,中断处理程序可以确保:在自身执行完毕前,对应的 tasklet 不会开始运行。但需注意,tasklet 运行期间可能会收到其他中断,因此 tasklet 与中断处理程序之间仍可能需要加锁。tasklets 必须通过 DECLARE_TASKLET 宏声明:

  • name:tasklet 的名称;

  • function:执行 tasklet 逻辑的函数(参数为 unsigned long,返回值为 void);

  • data:传递给 tasklet 函数的 unsigned long 类型参数。

 DECLARE_TASKLET(name, function, data);

name is the name to be given to the tasklet, function is the function that is called to execute the tasklet (it takes one unsigned long argument and returns void), and data is an unsigned long value to be passed to the tasklet function.

The short driver declares its tasklet as follows:

通过 tasklet_schedule 函数调度 tasklet 运行。若 short 模块以 tasklet=1 加载,会安装一个不同的中断处理程序:仅保存数据并调度 tasklet,代码如下:

 void short_do_tasklet(unsigned long);
 DECLARE_TASKLET(short_tasklet, short_do_tasklet, 0);

The function tasklet_schedule is used to schedule a tasklet for running. If short is loaded with tasklet=1, it installs a different interrupt handler that saves data and schedules the tasklet as follows:

通过 tasklet_schedule 函数调度 tasklet 运行。若 short 模块以 tasklet=1 加载,会安装一个不同的中断处理程序:仅保存数据并调度 tasklet,代码如下:

irqreturn_t short_tl_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
    // 保存当前时间到缓冲区(强制类型转换消除 volatile 警告)
    do_gettimeofday((struct timeval *) tv_head);
    short_incr_tv(&tv_head);  // 更新时间戳缓冲区写指针
    tasklet_schedule(&short_tasklet);  // 调度 tasklet 执行
    short_wq_count++;  // 记录中断发生次数
    return IRQ_HANDLED;
}

The actual tasklet routine, short_do_tasklet, will be executed shortly (so to speak) at the system’s convenience. As mentioned earlier, this routine performs the bulkof the work of handling the interrupt; it looks like this:

实际的 tasklet 处理函数 short_do_tasklet 会在系统方便时尽快执行。如前所述,该函数承担中断处理的主要工作,代码如下:

void short_do_tasklet(unsigned long unused)
{
    int savecount = short_wq_count, written;
    short_wq_count = 0;  // 重置计数(已从队列中取出待处理中断)

    /*
     * 下半部读取上半部填充的 tv 数组,
     * 将其打印到循环文本缓冲区,供读进程消费
     */

    // 首先写入本次下半部处理前累积的中断数
    written = sprintf((char *)short_head, "bh after %6i\n", savecount);
    short_incr_bp(&short_head, written);  // 更新文本缓冲区写指针

    // 然后写入时间戳,每次恰好写入 16 字节(与 PAGE_SIZE 对齐)
    do {
        written = sprintf((char *)short_head, "%08u.%06u\n",
                (int)(tv_tail->tv_sec % 100000000),  // 秒的后 8 位
                (int)(tv_tail->tv_usec));            // 微秒
        short_incr_bp(&short_head, written);
        short_incr_tv(&tv_tail);  // 更新时间戳缓冲区读指针
    } while (tv_tail != tv_head);  // 处理完所有累积的时间戳

    wake_up_interruptible(&short_queue);  // 唤醒等待数据的读进程
}

Among other things, this tasklet makes a note of how many interrupts have arrived since it was last called. A device such as short can generate a great many interrupts in a brief period, so it is not uncommon for several to arrive before the bottom half is executed. Drivers must always be prepared for this possibility and must be able to determine how much workthere is to perform from the information left by the top half.

关键说明:

  1. 累积中断的处理

    tasklet 记录了自上次执行以来到达的中断数(short_wq_count)。对于 short 这类设备,短时间内可能产生大量中断,因此在下半部执行前有多个中断累积是常见现象。驱动必须始终做好这种准备,并能根据上半部留下的信息确定需要处理的工作量。

  2. 软中断上下文的限制

    ​​​​​​​tasklet 运行在软中断上下文,因此:

    1. 执行时间应尽可能短,避免阻塞其他软中断。

    2. 不可调用可能睡眠的函数(如 kmalloc 不指定 GFP_ATOMIC 标志);

    3. 不可睡眠(不能调用 schedulewait_event 等函数);

  3. 与中断处理程序的同步

    ​​​​​​​虽然 tasklet 与调度它的中断处理程序运行在同一 CPU,但中断处理程序可能在 tasklet 运行期间再次被触发(处理新的中断)。因此,对共享数据(如 tv_headshort_wq_count)的访问需要通过原子操作或锁机制保护(示例中通过缓冲区指针的原子更新实现同步)。

  4. tasklet 的优势

    ​​​​​​​相比直接在中断处理程序中执行所有工作,tasklet 允许中断快速返回,同时保证后续工作在安全时机(中断启用状态)执行,提高了系统响应性。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DeeplyMind

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值