linux内核软中止
中止的效果:当一个中止信号抵达时,CPU有必要中止它当时正做的作业,转而去做中止要求其做的作业。
中止分为同步中止和异步中止两种。
1、同步中止又称反常,是由CPU履行指令时由CPU操控单元产生的。反常又分两种:
(1)、 一种是由程序履行犯错形成的,内核经过发送一个unix的信号来处理反常。
(2)、一种是由内核有必要处理的反常条件产生的,比方缺页反常,内核履行康复反常的一切过程。
2、异步中止,一般咱们就叫中止。由其他硬件设备依照CPU时钟信号随机产生。
中止处理程序的一般过程:
一个中止处理程序的几个中止服务例程之间是串行履行的,而且在一个中止处理程序完毕前,不应该再次出现这个中止,所以一般中止处理程序是先制止该中止,然后处理中止,处理完结后在使能该中止。
有一些中止是能够推迟处理的,这种可推迟中止能够在开中止的情况下履行,履行时答应其他中止抢占他。把可推迟中止从中止处理程序中抽出来有助于使内核坚持较短的呼应时刻。
Linux内核运用三种办法来处理这种可推迟的中止使命:可推迟函数(软中止和tasklets)以及作业行列。作业行列是作业在进程上下文中,能够睡觉,软中止和tasklets 是作业在中止上下文,不能够睡觉。本节只评论软中止和tasklets。
软中止:
Linux 2.6 版别运用如下几个软中止,不同版别之间略有差异。但一下几个不同版别都包括。
HI_SOFTIRQ=0, 处理高优先级的tasklet
TIMER_SOFTIRQ, 时钟中止相关的tasklet.
NET_TX_SOFTIRQ, 内核把数据报文传送给网卡。
NET_RX_SOFTIRQ, 内核从网卡接纳数据报文。
TASKLET_SOFTIRQ, 处理惯例tasklet。
低下标代表高优先级。
内核中界说了softirq_vec数组来寄存各种软中止。界说如下:
static struct softirq_action softirq_vec[32]__cacheline_aligned_in_smp;
数组元素为 softirq_action,一个元素代码一个软中止。不同的软中止号对应不同的数组的下标。
struct softirq_action
{
void (*action)(struct softirq_action *); //软中止产生时履行软中止的处理函数。
void *data; //软中止的处理函数的参数指针。
};
初始化软中止时调用函数 open_softirq()。如下代码。
void open_softirq(int nr, void (*action)(struct softirq_action*),void *data)
{
softirq_vec[nr].data = data;
softirq_vec[nr].action = action;
}
别的一个跟软中止相关的要害字段是 32 位的 preempt_counte字段,用它来盯梢内核抢占和内核操控途径的嵌套,该字段放在每个进程描绘符的 thread_info 字段中。用函数preempt_count()来回来该字段的值。
preempt_count字段
位描绘
0~7抢占计数器,记载显现禁用本地cpu内核抢占的次数,值为0时代表内核答应抢占。
8~15软中止计数器。记载软中止被禁用的次数,0表明软中止被激活。
16~27硬中止计数器。记载硬中止嵌套的层数。irq_entry()添加它的值,irq_exit()递减它的值。
28
当内核清晰不答应产生抢占或内核正在中止上下文中运行时,有必要制止内核的抢占功用。为了确认当时进程是否能够被抢占,内核快速查看preempt_counte字段是否等于零。
另一个跟软中止相关的字段是每个CPU都有一个32位掩码的字段
typedef struct {
unsigned int __softirq_pending;
} ____cacheline_aligned irq_cpustat_t;
他描绘挂起的软中止。每一位对应相应的软中止。比方0位代表HI_SOFTIRQ.
宏local_softirq_pending()来获取该字段的值。
运用函数raise_softirq()来激活软中止。即把呼应的软中止号对应的__softirq_pending中的方位1.表明该软中止被挂起。假如当时CPU不在中止上下文中,唤醒内核线程ksoftirqd来查看被挂起的软中止,然后履行相应软中止处理函数。
内核在如下几个点上查看被挂起的软中止:
1、当调用local_bh_enable()函数激活本地CPU的软中止时。条件满意就调用do_softirq() 来处理软中止。
2、当do_IRQ()完结硬中止处理时调用irq_exit()时调用do_softirq()来处理软中止。
3、当一个特别内核线程ksoftirq/n被唤醒时,处理软中止。
软中止处理函数详解:
点击(此处)折叠或翻开asmlinkage void do_softirq(void)
{
__u32 pending;
unsigned long flags;
/*假如当时处于硬中止中,在硬中止处理函数退出时会调用irq_exit()函数来处理软中止,
或当时软中止被禁用。所以in_interrupt()回来不为1 就没必要处理软中止,直接回来*/
if (in_interrupt())
return;
/*坚持中止寄存器的状况并禁用本地CPU的中止*/
local_irq_save(flags);
/*获得当时cpu上__softirq_pending字段,获取本地CPU上挂起的软中止*/
pending = local_softirq_pending();
/*假如当时CPU上有挂起的软中止,履行__do_softirq()来处理软中止*/
if (pending)
{
__do_softirq();
}
/*康复中止寄存器的状况*/
local_irq_restore(flags);
}
asmlinkage void __do_softirq(void)
{
struct softirq_action *h;
__u32 pending;
int max_restart = MAX_SOFTIRQ_RESTART; //10
int cpu;
/*获得当时cpu上__softirq_pending字段,获取本地CPU上挂起的软中止*/
pending = local_softirq_pending();
/*debug 用,不评论*/
account_system_vtime(current);
/*制止本地cpu的软中止,现在本地cpu上挂起的软中止现已存入pending暂时变量中了*/
__local_bh_disable((unsigned long)__builtin_return_address(0));
/*debug 用,不评论*/
trace_softirq_enter();
/*取本地cpu id 号*/
cpu = smp_processor_id();
restart:
/* Reset the pending bitmask before enabling irqs */
/*清空本地cpu的__softirq_pending字段*/
set_softirq_pending(0);
/*敞开本地cpu的硬中止*/
local_irq_enable();
/*循环履行被挂起的软中止处理函数。相应的软中止的处理函数存在数组softirq_ver[nr]中的元素 softirq_action-》action中*/
h = softirq_vec;
do {
if (pending & 1) {
h-》action(h);
rcu_bh_qsctr_inc(cpu);
}
h++;
pending 》》= 1;
} while (pending);
/*制止本地CPU的硬中止*/
local_irq_disable();
/*取本地CPU的__softirq_pending,查看是否还有新的被挂起的软中止而且查看被挂起软中止的次数小于10次,假如条件满意,
持续处理新的被挂起的软中止*/
pending = local_softirq_pending();
if (pending && –max_restart)
goto restart;
/*假如有新的挂起的软中止而且处理循环次数现已够了10次,
唤醒ksoftirq内核线程来处理软中止*/
if (pending)
wakeup_softirqd();
/*debug 用,不评论*/
trace_softirq_exit();
account_system_vtime(current);
/*使能本地CPU的软中止*/
_local_bh_enable();
}
Linux内核结构
Linux内核由七个部分构成,详细如下图:
a) 体系调用接口(SCI):open、read、write等体系调用
b) 进程办理(PM):创立进程、删去进程、调度进程等
c) 内存办理(MM):内存分配、办理等
d) 虚拟文件体系(VFS):为多种文件体系供给一致的操作接口
e) 网络协议栈:供给各种网络协议
f) CPU架构相关代码(Arch):为的是进步至移植性
g) 设备驱动程序(DD):各种设备驱动,占到内核的70%左右代码
linux内核源码详解
1. 源码获取
Linux内核获取有两种办法,一种是在www.kernel.org 直接获取,另一种是运用git获取(详细办法参阅网络)。
2. 源码目录简介
其源码主要有以下目录(介绍重要目录):
a) Arch目录:寄存处理器相关的代码。下设子目录,别离对应详细的CPU,每个子目录有boot,mm,以及kernel三个子目录,别离对应体系引导以及存储办理,和体系调用
b) Include目录:内核所需求的大部分头文件目录。与渠道无关的在include/linux子目录下,与渠道相关的则放在include相应的子目录中。
c) fs目录:寄存各种文件体系的完成代码。
d) init目录:init子目录包括中心的初始化代码(不是体系的引导代码)。其包括两个文件main.c和version.c,能够用来研讨中心怎么作业。
e) ipc目录:包括中心进程间的通讯代码。
f) kernel目录:包括内核办理的中心代码。与硬件相关代码放在arch/*/kernel目录下。
g) mm目录:包括了一切的内存办理代码。与硬件相关的内存办理代码坐落arch/*/mm目录下。
h) scripts目录:包括用于装备中心的脚本文件。
i) lib目录:包括了中心的库代码,与硬件相关的库代码被放在arch/*/lib/目录下