软中断&tasklet&工作队列
软中断
软中断的分配时静态的(即在编译时定义),而tasklet的分配和初始化能够在执行时进行。
软中断(即便是同一种类型的软中断)能够并发地运行在多个CPU上。
因此,软中断是可重入函数并且必须明白地使用自旋锁保护其数据结构。
tasklet不必操心这些问题。由于内核对tasklet的运行进行了更加严格的控制。
同样类型的tasklet总是被串行运行。
换句话说就是:不能在两个CPU上同一时候执行同样类型的tasklet。可是,类型不同的tasklet能够在几个CPU上并发执行。
tasklet的串行化使tasklet函数不必是可重入的
软中断 下标 说明
HI_SOFTIRQ 0 处理高优先级的tasklet
TIMER_SOFTIRQ 1 和时钟中断相关的tasklet
NET_TX_SOFTIRQ 2 把数据包传送到网卡
NET_RX_SOFTIRQ 3 从网卡接收数据包
SCSI_SOFTIRQ 4 SCSI命令的后台中断处理
TASKLET_SOFTIRQ 5 处理常规tasklet
thread_info->preempt_count
preempt_count字段
位 描写叙述
0~7 抢占计数器(max value=255)
8~15 软中断计数器(max value=255)
16~27 硬中断计数器(max value=255)
28 PREEMPT_ACTIVE标志
第一个计数器记录显式禁用本地CPU内核抢占的次数。值等于0表示同意内核抢占。
第二个计数器表示可延迟函数被禁用的程度(值为0表示可延迟函数处于激活状态)。
第三个计数器表示在本地CPU上中断处理程序的嵌套数。
处理软中断
open_softirq() 函数处理软中断的初始化
raise_softirq() 函数用来激活软中断
inline void raise_softirq_irqoff(unsigned int nr)
{
__raise_softirq_irqoff(nr); /*
* If we're in an interrupt or softirq, we're done
* (this also catches softirq-disabled code). We will
* actually run the softirq once we return from
* the irq or softirq.
*
* Otherwise we wake up ksoftirqd to make sure we
* schedule the softirq soon.
*/
if (!in_interrupt())
wakeup_softirqd();
} void raise_softirq(unsigned int nr)
{
unsigned long flags; local_irq_save(flags);
raise_softirq_irqoff(nr);
local_irq_restore(flags);
}
raise_softirq()函数运行以下的操作:
1、运行local_irq_save宏以保存eflags寄存器IF标志状态并禁用本地CPU上的中断。
2、把软中断标记为挂起状态。这是通过设置本地CPU的软中断掩码中与下标nr相关的位来实现的
3、假设in_interrupt()产生为1的值,则跳转到5。
这样的情况说明:要么已经在中断上下文中调用了raise_softirq()。要么当前禁用了软中断。
4、否则,就在须要的时候去调用wakeup_softirqd()以唤醒本地CPU的ksoftirqd内核线程。
5、运行local_irq_restore宏。恢复在第1步保存的IF标志的状态。
在下面几种情况周期性地检查是否有软中断要运行:
a、当内核调用local_bh_enable()函数激活本地CPU的软中断时
b、当do_IRQ()完毕了I/O中断的处理时或调用irq_exit()宏时
c、假设系统使用I/O APIC。则当smp_apic_timer_interrupt()函数处理完本地定时器中断时
d、在多处理器系统中。当CPU处理完被CALL_FUNCTION_VECTOR处理器间中断所触发的函数时
e、当一个特殊的ksoftirqd/n内核线程被唤醒时
ksoftirqd内核线程
每一个ksoftirqd/n内核线程都执行ksoftirqd()函数,该函数实际上执行下列的循环:
static int ksoftirqd(void * __bind_cpu)
{
set_user_nice(current, 19);
current->flags |= PF_NOFREEZE; set_current_state(TASK_INTERRUPTIBLE); while (!kthread_should_stop()) {
if (!local_softirq_pending())
schedule(); __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 */
preempt_disable();
if (cpu_is_offline((long)__bind_cpu))
goto wait_to_die;
do_softirq();
preempt_enable();
cond_resched();
} 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;
}
当内核线程被唤醒时,就检查local_softirq_pending()中的软中断位掩码并在必要时调用do_softirq()。假设没有挂起的软中断。函数把当前进程状态置为TASK_INTERRUPTIBLE,随后。假设当前进程须要(当前thread_info的TIF_NEED_RESCHED标志被设置)就调用con_resched()函数来实现进程切换。
tasklet
tasklet是I/O驱动程序中实现可延迟函数的首选方法。
tasklet建立在两个叫做HI_SOFTIRQ和TASKLET_SOFTIRQ的软中断之上。
几个tasklet能够与同一个软中断相关联,每一个tasklet运行自己的函数。
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsigned long);
unsigned long data;
};
struct tasklet_head
{
struct tasklet_struct *head;
struct tasklet_struct **tail;
};
static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec);
static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec);
tasklet描写叙述符的字段
next 指向链表中下一个描写叙述符的指针
state tasklet的状态
count 锁计数器
func 指向tasklet函数的指针
data 一个无符号长整数。能够由tasklet函数使用
tasklet描写叙述的state字段含有两个标志:
TASKLET_STATE_SCHED
该标志被设置时,表示tasklet是挂起的。也意味着tasklet描写叙述符被插入到tasklet_vec和tasklet_hi_vec数组的当中一个链表
TASKLET_STATE_RUN
该标志被设置时。表示tasklet正在被执行;在单处理器系统上不使用这个标志,由于没有必要检查特定的tasklet是否在执行
为了激活tasklet,依据tasklet的优先级,调用tasklet_schedule()或tasklet_hi_schedule()函数。
inline void raise_softirq_irqoff(unsigned int nr)
{
__raise_softirq_irqoff(nr); /*
* If we're in an interrupt or softirq, we're done
* (this also catches softirq-disabled code). We will
* actually run the softirq once we return from
* the irq or softirq.
*
* Otherwise we wake up ksoftirqd to make sure we
* schedule the softirq soon.
*/
if (!in_interrupt())
wakeup_softirqd();
} 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);
}
static inline void tasklet_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
__tasklet_schedule(t);
}
这两个函数很类似,当中每一个运行下列操作:
1、检查TASKLET_STATE_SCHED标志;假设设置则返回
2、调用local_irq_save保存IF标志的状态并禁用本地中断
3、在tasklet_vec或者tasklet_hi_vec指向的链表的起始处添加tasklet描写叙述符
4、调用raise_softirq_irqoff()激活TASKLET_SOFTIRQ或HI_SOFTIRQ类型的软中断
5、调用local_irq_restore恢复IF标志的状态
工作队列
可延迟函数和工作队列很相似。它们的差别在于:可延迟函数执行在中断上下文,而工作队列中的函数执行在进程上下文中。执行可堵塞函数(比如:须要訪问磁盘数据块的函数)的唯一方式是在进程上下文中执行。由于,在中断上下文中不可能发生进程切换。可延迟函数和工作队列中的函数都不能訪问进程的用户态地址空间。
struct cpu_workqueue_struct {
spinlock_t lock;
long remove_sequence; /* Least-recently added (next to run) */
long insert_sequence; /* Next to add */
struct list_head worklist;
wait_queue_head_t more_work;
wait_queue_head_t work_done;
struct workqueue_struct *wq;
task_t *thread;
int run_depth; /* Detect run_workqueue() recursion depth */
} ____cacheline_aligned;
/*
* The externally visible workqueue abstraction is an array of
* per-CPU workqueues:
*/
struct workqueue_struct {
struct cpu_workqueue_struct cpu_wq[NR_CPUS];
const char *name;
struct list_head list; /* Empty if single thread */
};
cpu_workqueue_struct类型的描写叙述符,字段描写叙述例如以下:
lock 保护该数据结构的自旋锁
remove_sequence flush_workqueue()使用的序列号
insert_sequence flush_workqueue()使用的序列号
worklist 挂起链表的头节点
more_work 等待队列。当中的工作者线程因等待很多其它的工作而处于睡眠状态
work_done 等待队列,当中的进程因为等待工作队列被刷新而处于睡眠状态
wq 指向workqueue_struct结构的指针,当中包括该描写叙述符
thread 指向结构中工作线程的进程描写叙述符指针
run_depth run_workqueue()当前的运行深度
struct work_struct {
unsigned long pending;
struct list_head entry;
void (*func)(void *);
void *data;
void *wq_data;
struct timer_list timer;
};
pending 假设函数已经在工作队列链表中。该字段值设为1,否则设为0
entry 指向挂起函数链表前一个或后一个元素的指针
func 挂起函数的地址
data 传递给挂起函数的參数,是一个指针
wq_data 一般是指向cpu_workqueue_struct描写叙述符的父节点的指针
timer 用于延迟挂起函数运行的软定时器
工作队列函数
create_workqueue() 函数接收一个字符串作为參数,返回新创建工作队列的workqueue_struct描写叙述符的地址。该函数还创建n个工作者线程,并根 据传递给函数的字符串为工作者线程命名,如foo/0,foo/1等等
destory_workqueue() 函数撤销工作队列
queue_work() 把函数插入工作队列,该函数主要运行以下步骤:
1、检查要插入的函数是否已经在工作队列中,假设是就结束
2、把work_struct描写叙述符加到工作队列链表中。然后把work->pending置为1
3、假设工作者线程在本地CPU的cpu_workqueue_struct描写叙述符的more_work等待队列上睡眠。该函数唤醒这个线程
每一个工作线程在worker_thread()函数内部不断地运行循环操作,因而。线程在绝大多数时间里处于睡眠状态并等待某些工作被插入队列。
工作线程一旦被唤醒就调用run_workqueue()函数,该函数从工作者线程的工作队列链表中删除全部work_struct描写叙述符并运行对应的挂起函数。
因为工作队列函数能够堵塞。因此,能够让工作者线程睡眠,甚至能够让它迁移到还有一个CPU上恢复运行
flush_workqueue()函数接收workqueue_struct描写叙述符的地址,而且在工作队列中的全部挂起函数结束之前使调用进程一直处于堵塞状态。
软中断&tasklet&工作队列的更多相关文章
- Linux内核中的软中断、tasklet和工作队列具体解释
[TOC] 本文基于Linux2.6.32内核版本号. 引言 软中断.tasklet和工作队列并非Linux内核中一直存在的机制,而是由更早版本号的内核中的"下半部"(bottom ...
- Linux软中断、tasklet和工作队列
Linux内核中的软中断.tasklet和工作队列详解 引言 软中断.tasklet和工作队列并不是Linux内核中一直存在的机制,而是由更早版本的内核中的“下半部”(bottom half)演变而来 ...
- [Linux内核]软中断、tasklet、工作队列
转自:http://www.cnblogs.com/li-hao/archive/2012/01/12/2321084.html 软中断.tasklet和工作队列并不是Linux内核中一直存在的机制, ...
- Linux中断管理 (2)软中断和tasklet
目录: <Linux中断管理> <Linux中断管理 (1)Linux中断管理机制> <Linux中断管理 (2)软中断和tasklet> <Linux中断管 ...
- linux内核--软中断与tasklet
硬件中断通常都需要在最短的时间内执行完毕,如果将所有硬件中断相关的处理都放在硬件中断处理程序中,那么就达不到这个目的. 通过linux提供的软中断和tasklet,可以将硬件中断处理程序中可以延迟处理 ...
- 软中断和tasklet介绍
今天看了下tasklet,重点分析了其和软中断的关系,特此记录 关于软中断,在之前的中断文章中已经有所介绍,这里就不多说了,只是说明下,系统中默认支持32种软中断,而实际上系统定义的软中断仅有以下几种 ...
- 内核软中断之tasklet机制
1. 软中断IRQ简介 软中断(SoftIRQ)是内核提供的一种基于中断的延时机制, Linux内核定义的软中断有以下几种: enum { HI_SOFTIRQ=0, /*高优先级的tasklet*/ ...
- Linux中断分层--软中断和tasklet
1. Linux中断分层 (1)上半部:当中断发生时,它进行相应的硬件读写,并“登记”该中断.通常由中断处理程序充当上半部.(一般情况下,上半部不可被打断) (2)下半部:在系统空闲的时候,对上半部“ ...
- tasklet和工作队列
tasklet机制和工作队列 http://blog.chinaunix.net/uid-28236237-id-3450753.html tasklet原理 http://www.kuqin.com ...
随机推荐
- Android Service的生命周期
service的生命周期,从它被创建开始,到它被销毁为止,可以有两条不同的路径: A started service 被开启的service通过其他组件调用 startService()被创建. 这种 ...
- 关于linq
其实从08年的时候,我就已经知道了linq,开始的时候也并没有注意,我说过很多次,我不是一个有心人,只是在新建立一个工程的时候,程序会自动引入linq这个玩意,怀着好奇的心去找了点资料,有的时候,看一 ...
- 坑爹的UICollectionView
最近用UICoolectionView的时候遇到一个很DT的问题,我往VC里加12个视图,结果显示成这样(右边是期待的样子): 研究了一下午,终于发现了问题: @interface FpL ...
- 【HDOJ】1088 Write a simple HTML Browser
题目其实不难,但是要注意题目的要求,当前字数(>0)+当前单词长度+1若超过80则需要回车后,输出当前word,并且重新计数.这道题目的数据感觉比较水,不过测试的时候,最后使用fprintf输出 ...
- Case swapping
Case swapping Description: Given a string, swap the case for each of the letters. e.g. CodEwArs --&g ...
- mapreduce: 揭秘InputFormat--掌控Map Reduce任务执行的利器
随着越来越多的公司采用Hadoop,它所处理的问题类型也变得愈发多元化.随着Hadoop适用场景数量的不断膨胀,控制好怎样执行以及何处执行map任务显得至关重要.实现这种控制的方法之一就是自定义Inp ...
- String对象不可改变的特性
1. 声明String对象 String s = "abcd"; 图1 2. 将一个字符串变量赋值给另一个String变量 String s2 = s; 图2 3. 字符串连接 s ...
- 普通pc电脑安装苹果系统mac_详细教程(精)附带所有工具下载
苹果操作系统只允许在苹果电脑上面安装和使用.和Windows不一样,要在PC上安装,需要一系列的模拟和破解.破解安装的过程很繁琐而具有挑战性,以下是安装10A432雪豹的PC安装指南,附带25张图片帮 ...
- SQL Server登录 18456错误
1.以windows验证模式进入数据库管理器. 第二步:右击sa,选择属性: 在常规选项卡中,重新填写密码和确认密码(改成个好记的).把强制实施密码策略去掉. 第三步:点击状态选项卡:勾选授予和启用. ...
- hdu 4294 数学分析+搜索
又要开始一段搜索的路程了. 最近看了这题,在网上看到一个结论,任何一个数倍数都能被不超过两个数字组成,假如一个数n个A%x=b,那么必然有m个A%=b那么此时n个A减去m个B就能够被x整除,那么此时就 ...