kk Blog —— 通用基础


date [-d @int|str] [+%s|"+%F %T"]
netstat -ltunp
sar -n DEV 1

NMI 看门狗

http://blog.csdn.net/arethe/article/details/6153143

[X86和X86-64体系结构均支持NMI看门狗]

你的系统是不是会经常被锁住(Lock up)?直至解锁,系统不再响应键盘?你希望帮助我们解决类似的问题吗?如果你对所有的问题都回答“yes”,那么此文档正是为你而写。

在很多X86/X86-64结构的硬件上,我们都可以使用一种被称为“看门狗NMI中断”的机制。(NMI:Non Maskable Interrupt. 这种中断即使在系统被锁住时,也能被响应)。这种机制可以被用来调试内核锁住现象。通过周期性地执行NMI中断,内核能够监测到是否有CPU被锁住。当有处理器被锁住时,打印调试信息。

为了使用NMI看门狗,首先需要在内核中支持APIC。对于SMP内核,APIC的相关支持已自动地被编译进内核。对于UP内核,需要在内核配置中使能CONFIG_X86_UP_APIC (Processor type and features -> Local APIC support on uniprocessors) 或 CONFIG_X86_UP_IOAPIC (Processor type and features -> IO-APIC support on uniprocessors)。在没有IO-APIC的单处理器系统中,配置CONFIG_X86_UP_APIC。在有IO-APIC的单处理器系统中,则需配置CONFIG_X86_UP_IOAPIC。[注意:某些与内核调试相关选项可能会禁用NMI看门狗。如:Kernel Stack Meter或Kernel Tracer]。

对于X86-64系统,APIC已被编进内核。

使用本地APIC(nmi_watchdog=2)时,需要占用第一个性能寄存器,因而此寄存器不能再被另作它用(如高精度的性能分析)。Oprofile与perfctr的驱动已自动地禁用了本地APIC的NMI看门狗。

可以通过启动参数“nmi_watchdog=N”使能NMI看门狗。即在lilo.conf的相关项中添加如下语句:

1
  append=”nmi_watchdog=1”

对于具有IO-APIC的SMP与UP机器,设置nmi_watchdog=1。对于没有IO-APIC的UP机器,设置nmi_watchdog=2,但仅在某些处理器上可以起作用。如果有疑问,在用nmi_watchdog=1启动后,再查看/proc/interrupts文件中的NMI项,如果该项为0,那么便用nmi_watchdog=2重新启动,并再次检查NMI项。如果还是0,问题就比较严重了,你的处理器很可能不支持NMI。

“锁住(Lockup)”是指如下的情况:如果系统中的任何一个CPU不能处理周期性的本地时钟中断,并持续5秒钟以上,那么NMI的处理函数将产生一个oops并杀死当前进程。这是一种“可控崩溃”(Controlled Crash,所谓可控,是指发生崩溃时,能够输出内核信息),可以用此机制来调试“锁住”现象。那么,无论什么时候发生“锁住”,5秒钟之后都会自动地输出oops。如果内核没有输出信息,说明此时发生的崩溃过于严重(如:hardware-wise),以至于NMI中断都无法被响应,或者此次崩溃使得内核无法打印信息。

在使用本地APIC时要注意,NMI中断被触发的频率依赖于系统的当前负载。由于缺乏更好的时钟源,本地APIC中的NMI看门狗使用的是“有效周期(Cycle unhalted,这个词的翻译似乎不太确切,如果某位朋友有更佳的建议,请告知在下。)”事件。也许你已经猜到了,当CPU处于halted(空等)状态时,该时钟是不计数的。处理器处于空闲状态的时候,常出现这样的情况。如果你的系统在被锁住时,执行的不是hlt指令,看门狗中断很快就会被触发,因为每个时钟周期都会发生“有效周期”事件。如果不幸,处理器在被锁住时,执行的恰是“hlt”指令,那么“有效周期”事件永远都不会发生,看门狗自然也不会被触发。这是本地APIC看门狗的缺陷,在倒霉的时候,永远不会进行时钟计数。而I/O APIC中的看门狗由于采用外部时钟进行驱动,便不存在这个缺陷。但是,它的NMI频率非常高,会显著地影响系统的性能。

X86的nmi_watchdog在默认情况下是禁用的,因此你需要在系统启动的时候使能它。

在系统运行期间,可以禁用NMI看门狗,只要向文件“/proc/sys/kernel/nmi_watchdog”中写“0”即可。向该文件写“1”,将重新使能看门狗。即使如此,你仍然需要在启动时使用参数“nmi_watchdog=”。

注意:在2.4.2-ac18之前的内核中,X86 SMP平台会无条件地使能NMI-oopser。


www.2cto.com/kf/201311/260704.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
//  使能hard lockup探测
//  调用路径:watchdog_enable->watchdog_nmi_enable
//  函数任务:
//      1.初始化hard lockup检测事件
//          2.hard lockup阈值为10s
//      2.向performance monitoring子系统注册hard lockup检测事件
//      3.使能hard lockup检测事件
//  注:
//      performance monitoring,x86中的硬件设备,当cpu clock经过了指定个周期后发出一个NMI中断。
1.1 static int watchdog_nmi_enable(unsigned int cpu)
{
	//hard lockup事件
	struct perf_event_attr *wd_attr;
	struct perf_event *event = per_cpu(watchdog_ev, cpu);
	....
	wd_attr = &wd_hw_attr;
	//hard lockup检测周期,10s
	wd_attr->sample_period = hw_nmi_get_sample_period(watchdog_thresh);
	//向performance monitoring注册hard lockup检测事件
	event = perf_event_create_kernel_counter(wd_attr, cpu, NULL, watchdog_overflow_callback, NULL);
	....
	//使能hard lockup的检测
	per_cpu(watchdog_ev, cpu) = event;
	perf_event_enable(per_cpu(watchdog_ev, cpu));
	return 0;
}
 
//  换算hard lockup检测周期到cpu频率
1.2 u64 hw_nmi_get_sample_period(int watchdog_thresh)
{
	return (u64)(cpu_khz) * 1000 * watchdog_thresh;
}
 
//  hard lockup检测事件发生时的nmi回调函数
//  函数任务:
//      1.判断是否发生了hard lockup
//          1.1 dump hard lockup信息
1.3 static void watchdog_overflow_callback(struct perf_event *event,
	 struct perf_sample_data *data,
	 struct pt_regs *regs)
{
	//判断是否发生hard lockup
	if (is_hardlockup()) {
		int this_cpu = smp_processor_id();
 
		//打印hard lockup信息
		if (hardlockup_panic)
			panic("Watchdog detected hard LOCKUP on cpu %d", this_cpu);
		else
			WARN(1, "Watchdog detected hard LOCKUP on cpu %d", this_cpu);
 
		return;
	}
	return;
}
 
//  判断是否发生hard lockup
//  注:
//      如果时钟中断在指定阈值范围内为运行,核心认为可屏蔽中断被屏蔽时间过长
1.4 static int is_hardlockup(void)
{
	//获取watchdog timer的运行次数
	unsigned long hrint = __this_cpu_read(hrtimer_interrupts);
	//在一个hard lockup检测时间阈值内,如果watchdog timer未运行,说明cpu中断被屏蔽时间超过阈值
	if (__this_cpu_read(hrtimer_interrupts_saved) == hrint)
		return 1;
	//记录watchdog timer运行的次数
	__this_cpu_write(hrtimer_interrupts_saved, hrint);
	return 0;
}
 
//  关闭hard lockup检测机制
//  函数任务:
//      1.向performance monitoring子系统注销hard lockup检测控制块
//      2.清空per-cpu hard lockup检测控制块
//      3.释放hard lock检测控制块
2.1 static void watchdog_nmi_disable(unsigned int cpu)
{
	struct perf_event *event = per_cpu(watchdog_ev, cpu);
	if (event) {
		//向performance monitoring子系统注销hard lockup检测控制块
		perf_event_disable(event);
		//清空per-cpu hard lockup检测控制块
		per_cpu(watchdog_ev, cpu) = NULL;
		//释放hard lock检测控制块
		perf_event_release_kernel(event);
	}
	return;
}

中断,进程

blog.chinaunix.net/uid-20806345-id-3203602.html

中断不是进程,不受内核调度器的管辖。在系统处理进程的过程中,对于某个cpu来说,如果有内部中断或外部中断到来,则切换到中断处理程序,切换首先要将进程由用户态要切到进程的内核态,然后再将cpu切换到中断态,待处理完中断返回进程的内核态,再返回进程的用户态,如果中断时进程刚好处于内核态中不用由用户态切到内核态了。
中断处理时是不分优先级的,处理中断的过程中如果有任意中断到来,都会抢占当前的中断处理过程。所以对于要及时响应的中断,需要通过关中断来屏蔽其他中断。通常所说的中断优先级是指中断控制器端的优先级,当有多个中断触发时,首先选择优先级高的中断发出请求给处理器。中断优先级只是对中断控制器而言的,所有的中断对cpu来说都是一样的,没有优先级高低之分。
关中断是关闭所有的外部可屏蔽中断,和优先级没有关系,如果在某中断处理程序中关中断,则不会被任何可屏蔽中断抢占,但是会被任意的不可屏蔽中断抢占。关中断是中断处理程序可选的。

bbs.chinaunix.net/thread-2306027-1-8.html

软中断做的是一些可延迟的费时间的事,当然不能在中断里执行了。
__do_softirq代码,可以看到在执行可延迟函数第一件事就是开中断。但在开始之前,禁用了下半部中断(__local_bh_disable)。这样就算被中断了,返回内核时也不会被抢占,还是执行这里的代码。也不会被调度。
那么这样的后果就是软中断上下文里的会一直执行下去,直到到达了限定次数,然后唤醒守护进程。
因为软中断切换了栈,不再使用进程上下文,那么如果在软中断上下文直接或简洁调用了shedule,那么只有死翘翘了!!因为schedule调度回来的时候是依赖进程内核栈的thread_info。

内核抢占点之一就是中断返回的时候检查是否可以抢占,检查的内容之一就是preempt_count是否等于0,因为禁用了下半部中断,那么肯定就不会等于0的,所以不会被抢占。也就是说返回的时候不会发生调度。

个人理解 中断上下文 最大的特征 禁掉了某种中断(硬中断和软中断),所以导致 不能阻塞。
softirq 有可能在两种方式下被调用,一是中断处理程序退出时,开放硬件中断之后,会去调用do_softirq()。 do_softirq()会禁掉后半部抢占,并且现在执行流使用的是被中断的进程的栈,所以无法阻塞。
softirq的另一种调用方式是ksoftirq内核线程,同样do_softirq()被调用,后半部中断被禁掉,同样禁止阻塞。
工作队列,可以被任何中断或者软中断中断,运行在进程上下文,有自己的栈,可以阻塞。

看一下__do_softirq()的代码,新的硬中断确实可能触发更高优先级的软中断,但是这个软中断并不会在被中断的软中断之前得到执行,软中断始终是顺序执行的。从代码看来,新一批的软中断,无论优先级多高,也得等到前一批的软中断被处理完成之后才能得到处理。而优先级只能帮助软中断在对应的批次中优先得到处理。

硬中断和软中断

关闭硬中断: spin_lock_irq和spin_unlock_irq以及spin_lock_irqsave和spin_unlock_irqrestore
关闭软中断: spin_lock_bh和spin_unlock_bh


netfilter:
有些netfilter hooks可以从系统调用的context到达, 比如socket的send_msg()是可以到达LOCAL_OUT/POST_ROUTING的,
这样,也就是说,在这些情况下操作conntrack链表的时候,是进程上下文,而不是软中断上下文, 因此,是需要关闭bh的。

PRE_ROUTING上的按道理说,它只能从软中断到达,因此只需要spin_lock()就可以了。


http://blog.csdn.net/zhangskd/article/details/21992933

概述

从本质上来讲,中断是一种电信号,当设备有某种事件发生时,它就会产生中断,通过总线把电信号发送给中断控制器。 如果中断的线是激活的,中断控制器就把电信号发送给处理器的某个特定引脚。处理器于是立即停止自己正在做的事, 跳到中断处理程序的入口点,进行中断处理。

(1) 硬中断

由与系统相连的外设(比如网卡、硬盘)自动产生的。主要是用来通知操作系统系统外设状态的变化。比如当网卡收到数据包的时候,就会发出一个中断。我们通常所说的中断指的是硬中断(hardirq)。

(2) 软中断

为了满足实时系统的要求,中断处理应该是越快越好。linux为了实现这个特点,当中断发生的时候,硬中断处理那些短时间就可以完成的工作,而将那些处理事件比较长的工作,放到中断之后来完成,也就是软中断(softirq)来完成。

(3) 中断嵌套

Linux下硬中断是可以嵌套的,但是没有优先级的概念,也就是说任何一个新的中断都可以打断正在执行的中断,但同种中断除外。软中断不能嵌套,但相同类型的软中断可以在不同CPU上并行执行。

(4) 软中断指令

int是软中断指令。
中断向量表是中断号和中断处理函数地址的对应表。
int n - 触发软中断n。相应的中断处理函数的地址为:中断向量表地址 + 4 * n。

(5)硬中断和软中断的区别

软中断是执行中断指令产生的,而硬中断是由外设引发的。
硬中断的中断号是由中断控制器提供的,软中断的中断号由指令直接指出,无需使用中断控制器。
硬中断是可屏蔽的,软中断不可屏蔽。
硬中断处理程序要确保它能快速地完成任务,这样程序执行时才不会等待较长时间,称为上半部。
软中断处理硬中断未完成的工作,是一种推后执行的机制,属于下半部。

开关

(1) 硬中断的开关

简单禁止和激活当前处理器上的本地中断:

1
2
local_irq_disable();
local_irq_enable();

保存本地中断系统状态下的禁止和激活:

1
2
3
unsigned long flags;
local_irq_save(flags);
local_irq_restore(flags);
(2) 软中断的开关

禁止下半部,如softirq、tasklet和workqueue等:

1
2
local_bh_disable();
local_bh_enable();

需要注意的是,禁止下半部时仍然可以被硬中断抢占。

(3) 判断中断状态
1
2
3
#define in_interrupt() (irq_count()) // 是否处于中断状态(硬中断或软中断)
#define in_irq() (hardirq_count()) // 是否处于硬中断
#define in_softirq() (softirq_count()) // 是否处于软中断

硬中断

(1) 注册中断处理函数

注册中断处理函数:

1
2
3
4
5
6
7
8
9
10
11
/** 
 * irq: 要分配的中断号 
 * handler: 要注册的中断处理函数 
 * flags: 标志(一般为0) 
 * name: 设备名(dev->name) 
 * dev: 设备(struct net_device *dev),作为中断处理函数的参数 
 * 成功返回0 
 */  
  
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,   
	const char *name, void *dev);  

中断处理函数本身:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef irqreturn_t (*irq_handler_t) (int, void *);  
  
/** 
 * enum irqreturn 
 * @IRQ_NONE: interrupt was not from this device 
 * @IRQ_HANDLED: interrupt was handled by this device 
 * @IRQ_WAKE_THREAD: handler requests to wake the handler thread 
 */  
enum irqreturn {  
	IRQ_NONE,  
	IRQ_HANDLED,  
	IRQ_WAKE_THREAD,  
};  
typedef enum irqreturn irqreturn_t;  
#define IRQ_RETVAL(x) ((x) != IRQ_NONE)  
(2) 注销中断处理函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/** 
 * free_irq - free an interrupt allocated with request_irq 
 * @irq: Interrupt line to free 
 * @dev_id: Device identity to free 
 * 
 * Remove an interrupt handler. The handler is removed and if the 
 * interrupt line is no longer in use by any driver it is disabled. 
 * On a shared IRQ the caller must ensure the interrupt is disabled 
 * on the card it drives before calling this function. The function does 
 * not return until any executing interrupts for this IRQ have completed. 
 * This function must not be called from interrupt context. 
 */  
  
void free_irq(unsigned int irq, void *dev_id);  

软中断

(1) 定义

软中断是一组静态定义的下半部接口,可以在所有处理器上同时执行,即使两个类型相同也可以。
但一个软中断不会抢占另一个软中断,唯一可以抢占软中断的是硬中断。

软中断由softirq_action结构体表示:

1
2
3
struct softirq_action {  
	void (*action) (struct softirq_action *); /* 软中断的处理函数 */  
};  

目前已注册的软中断有10种,定义为一个全局数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static struct softirq_action softirq_vec[NR_SOFTIRQS];  
  
enum {  
	HI_SOFTIRQ = 0, /* 优先级高的tasklets */  
	TIMER_SOFTIRQ, /* 定时器的下半部 */  
	NET_TX_SOFTIRQ, /* 发送网络数据包 */  
	NET_RX_SOFTIRQ, /* 接收网络数据包 */  
	BLOCK_SOFTIRQ, /* BLOCK装置 */  
	BLOCK_IOPOLL_SOFTIRQ,  
	TASKLET_SOFTIRQ, /* 正常优先级的tasklets */  
	SCHED_SOFTIRQ, /* 调度程序 */  
	HRTIMER_SOFTIRQ, /* 高分辨率定时器 */  
	RCU_SOFTIRQ, /* RCU锁定 */  
	NR_SOFTIRQS /* 10 */  
};  
(2) 注册软中断处理函数
1
2
3
4
5
6
7
8
9
/** 
 * @nr: 软中断的索引号 
 * @action: 软中断的处理函数 
 */  
  
void open_softirq(int nr, void (*action) (struct softirq_action *))  
{  
	softirq_vec[nr].action = action;  
}  

例如:

1
2
open_softirq(NET_TX_SOFTIRQ, net_tx_action);
open_softirq(NET_RX_SOFTIRQ, net_rx_action);
(3) 触发软中断

调用raise_softirq()来触发软中断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void raise_softirq(unsigned int nr)  
{  
	unsigned long flags;  
	local_irq_save(flags);  
	raise_softirq_irqoff(nr);  
	local_irq_restore(flags);  
}  
  
/* This function must run with irqs disabled */  
inline void rasie_softirq_irqsoff(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); /* 唤醒ksoftirqd/n进程 */  
}  

Percpu变量irq_cpustat_t中的__softirq_pending是等待处理的软中断的位图,通过设置此变量

即可告诉内核该执行哪些软中断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static inline void __rasie_softirq_irqoff(unsigned int nr)  
{  
	trace_softirq_raise(nr);  
	or_softirq_pending(1UL << nr);  
}  
  
typedef struct {  
	unsigned int __softirq_pending;  
	unsigned int __nmi_count; /* arch dependent */  
} irq_cpustat_t;  
  
irq_cpustat_t irq_stat[];  
#define __IRQ_STAT(cpu, member) (irq_stat[cpu].member)  
#define or_softirq_pending(x) percpu_or(irq_stat.__softirq_pending, (x))  
#define local_softirq_pending() percpu_read(irq_stat.__softirq_pending)  

唤醒ksoftirqd内核线程处理软中断。

1
2
3
4
5
6
7
8
static void wakeup_softirqd(void)  
{  
	/* Interrupts are disabled: no need to stop preemption */  
	struct task_struct *tsk = __get_cpu_var(ksoftirqd);  
  
	if (tsk && tsk->state != TASK_RUNNING)  
		wake_up_process(tsk);  
}  

在下列地方,待处理的软中断会被检查和执行:
1. 从一个硬件中断代码处返回时
2. 在ksoftirqd内核线程中
3. 在那些显示检查和执行待处理的软中断的代码中,如网络子系统中

而不管是用什么方法唤起,软中断都要在do_softirq()中执行。如果有待处理的软中断,do_softirq()会循环遍历每一个,调用它们的相应的处理程序。在中断处理程序中触发软中断是最常见的形式。中断处理程序执行硬件设备的相关操作,然后触发相应的软中断,最后退出。内核在执行完中断处理程序以后,马上就会调用do_softirq(),于是软中断开始执行中断处理程序完成剩余的任务。

下面来看下do_softirq()的具体实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
asmlinkage void do_softirq(void)  
{  
	__u32 pending;  
	unsigned long flags;  
  
	/* 如果当前已处于硬中断或软中断中,直接返回 */  
	if (in_interrupt())   
		return;  
  
	local_irq_save(flags);  
	pending = local_softirq_pending();  
	if (pending) /* 如果有激活的软中断 */  
		__do_softirq(); /* 处理函数 */  
	local_irq_restore(flags);  
}  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/* We restart softirq processing MAX_SOFTIRQ_RESTART times, 
 * and we fall back to softirqd after that. 
 * This number has been established via experimentation. 
 * The two things to balance is latency against fairness - we want 
 * to handle softirqs as soon as possible, but they should not be 
 * able to lock up the box. 
 */  
asmlinkage void __do_softirq(void)  
{  
	struct softirq_action *h;  
	__u32 pending;  
	/* 本函数能重复触发执行的次数,防止占用过多的cpu时间 */  
	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), SOFTIRQ_OFFSET);  
	lockdep_softirq_enter(); /* current->softirq_context++ */  
	cpu = smp_processor_id(); /* 当前cpu编号 */  
  
restart:  
	/* Reset the pending bitmask before enabling irqs */  
	set_softirq_pending(0); /* 重置位图 */  
	local_irq_enable();  
	h = softirq_vec;  
	do {  
		if (pending & 1) {  
			unsigned int vec_nr = h - softirq_vec; /* 软中断索引 */  
			int prev_count = preempt_count();  
			kstat_incr_softirqs_this_cpu(vec_nr);  
  
			trace_softirq_entry(vec_nr);  
			h->action(h); /* 调用软中断的处理函数 */  
			trace_softirq_exit(vec_nr);  
  
			if (unlikely(prev_count != preempt_count())) {  
				printk(KERN_ERR "huh, entered softirq %u %s %p" "with preempt_count %08x,"  
					"exited with %08x?\n", vec_nr, softirq_to_name[vec_nr], h->action, prev_count,  
					preempt_count());  
			}  
			rcu_bh_qs(cpu);  
		}  
		h++;  
		pending >>= 1;  
	} while(pending);  
  
	local_irq_disable();  
	pending = local_softirq_pending();  
	if (pending & --max_restart) /* 重复触发 */  
		goto restart;  
  
	/* 如果重复触发了10次了,接下来唤醒ksoftirqd/n内核线程来处理 */  
	if (pending)  
		wakeup_softirqd();   
  
	lockdep_softirq_exit();  
	account_system_vtime(current);  
	__local_bh_enable(SOFTIRQ_OFFSET);  
}  
(4) ksoftirqd内核线程

内核不会立即处理重新触发的软中断。
当大量软中断出现的时候,内核会唤醒一组内核线程来处理。
这些线程的优先级最低(nice值为19),这能避免它们跟其它重要的任务抢夺资源。
但它们最终肯定会被执行,所以这个折中的方案能够保证在软中断很多时用户程序不会因为得不到处理时间而处于饥饿状态,同时也保证过量的软中断最终会得到处理。

每个处理器都有一个这样的线程,名字为ksoftirqd/n,n为处理器的编号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
static int run_ksoftirqd(void *__bind_cpu)  
{  
	set_current_state(TASK_INTERRUPTIBLE);  
	current->flags |= PF_KSOFTIRQD; /* I am ksoftirqd */  
  
	while(! kthread_should_stop()) {  
		preempt_disable();  
  
		if (! local_softirq_pending()) { /* 如果没有要处理的软中断 */  
			preempt_enable_no_resched();  
			schedule();  
			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))/* 被要求释放cpu */  
				 goto wait_to_die;  
  
			do_softirq(); /* 软中断的统一处理函数 */  
  
			preempt_enable_no_resched();  
			cond_resched();  
			preempt_disable();  
			rcu_note_context_switch((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;  
}