《Linux内核设计与实现》读书笔记—中断下半部

下半部简介

  1. 下半部的任务就是执行与中断处理密切相关但中断处理程序本身不执行的工作。
  2. 如果一个任务对时间非常敏感,则应放到中断处理程序中执行。如果一个任务和硬件相关,将其放在中断处理程序中执行。如果一个任务要保证不被其他中断打断,将其放在中断处理程序中执行。其他所有任务,考虑放置在下半部中执行。
  3. Linux内核常用的下半部机制包括:任务队列,软中断,Tasklet和内核定时器
  4. 软中断是一组静态定义的接口,总共有32个,可以在所有处理器上同时执行,即使是同类型的也可以。
  5. Tasklet是一种基于软中断实现的可以动态创建,实现灵活的下半部接口。两个不同类型的Tasklet可以在不同的处理器上同时执行,而两个相同的Tasklet不能再。

软中断

  1. 软中断的结构体定义为softirq_action。系统中共有NR_SOFTIRQS个软中断,保存的结构体变量为softirq_vec。
  2. softirq_action只有一个成员,叫做action,其原型为void (action*) (struct softirq_action*)。
  3. 只有中断处理程序可以打断软中断,软中断之间不能互相打断
  4. 一个注册的软中断只有在被标记后才会执行,一般在下列地方,待处理的软中断会被检查和执行
  • 从一个硬件中断代码处返回
  • 在ksoftirqd内核线程中
  • 在那些显示检查和执行待处理的软中断的代码处,如网络子系统中
  1. 软中断的注册接口为open_softirq(),函数原型为void open_softirq(int nr, void (*action)(struct softirq_action *))。
  2. 触发软中的的接口为raise_softirq(),函数原型为void raise_softirq(unsigned int nr)。该函数在软中断处理之前会首先禁止这个处理器上的软中断,如果软中断已经被禁止了,则使用void raise_softirq_irqoff(unsigned int nr)会得到更好的性能。
  3. 软中断最常见的触发形式为在中断处理程序结束时,触发软中断,然后退出。内核会在中断处理程序结束后,调用do_softirq()函数。于是软中断便被触发起来完成其他的任务。

Tasklet

  1. 因为Tasklet是由软中断实现的,所以本身Tasklet本身也有中断号。Tasklet的中断号有两个:HI_SOFTIRQ和TASKLET_SOFTIRQ。前者和后者相比,优先级更高,优先于后者先执行。
  2. Tasklet由结构体tasklet_struct,每个结构体表示一个Tasklet。
  3. Tasklet的成员变量Func表示该Tasklet的处理程序。State为该Tasklet的状态,其值只能是0, TASKLET_STATE_SCHED, TASKLET_STATE_RUN。其中TASKLET_STATE_SCHED表示该Tasklet已被调度,准备运行。TASKLET_STATE_RUN表示该Tasklet正在执行。Count为该Tasklet的引用计数,如果它不为0,则被禁止不能允许。
  4. 已调度的Tasklet存放在tasklet_vec和tasklet_hi_vec链表中。Tasklet由tasklet_schedule()和tasklet_hi_schedule(),它们接收一个类型为tasklet_struct*的参数。
  5. 宏DECLARE_TASKLET(name, func, data)和DECLARE_TASKLET_DISABLED(name, func, data)用来静态的创建一个tasklet。还可以通过调用函数void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data)来动态创建一个tasklet。
  6. static inline void tasklet_enable(struct tasklet_struct *t)和static inline void tasklet_disable(struct tasklet_struct *t)用来激活去激活一个特定的tasklet。
  7. 可以调用tasklet_kill()函数从已挂起的tasklet队列中去掉一个tasklet。

ksoftirqd

  1. 由于软中断在执行过程中可以重新提交软中断,如果总是优先执行重新提交的软中断的话,则会导致用户进程得不到及时的执行而出现饿死的情况。但是如果固定将重新提交的软中断推迟到下一个中断返回的时机时,在系统轻载时重新提交的软中断由于无法及时执行而又得不到足够的性能。
  2. 针对这种问题,内核有一个辅助线程ksoftirqd,当内核中出线大量的软中断,内核会唤醒ksoftirqd线程,来调度执行软中断。
  3. ksoftirq线程的nice值为19,即在最低的优先级上执行。

工作队列

  1. 工作队列是另一种将工作推后执行的形式,工作队列可以把工作推后,交由一个内核线程去执行,这个下半部总是会在进程上下文中执行。这样,工作队列便允许重新调度和睡眠。
  2. 可以通过工作队列子系统自己创建一个工作者线程,也可以将自己的任务交给缺省的工作者线程来执行。缺省的工作者线程名为events/n,其中n代表处理器编号。
  3. 工作者线程的结构体为workqueque_struct,其内部的成员数组struct cpu_workqueue_struct cpu_wq[NR_CPUS]表示每个处理器上对应的线程任务。cpu_workqueue_struct中的成员变量struct list_head worklist表示当前的工作队列,其链表结构体为struct work_struct。
  1. 使用宏DECLARE_WORK(name,void (*func)(void*),void* data)可以静态创建一个工作任务。使用宏INIT_WORK(struct work_struct* work,void (*func)(void*),void* data)可以动态创建一个工作任务。
  2. schedule_work(&work)和schedule_delay_work(&work,delay)函数完成对创建好的work进行调度的处理,后者可以延迟指定delay的时间。这两个函数都将work调度在缺省的events/n线程中。
  3. void flush_scheduled_work(void),该函数会进入休眠,知道工作队列中的所有对象都被执行完以后才会返回。
  4. 函数struct workqueue_struct* create_workqueue(const char* name)用来创建一个新的工作队列。并且使用函数int queue_work(struct workqueue_struct *wq,struct work_struct *work)和函数int queue_delayed_work(struct workqueue_struct *wq,struct work_struct *work,unsigned long delay)来使得work调度在指定的工作队列线程中。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值