《深入理解Linux内核》第四章中断和异常(三)

上两篇分析了I/O中断处理过程,接下来我们来分析softirq和tasklet

一、softirq

中断处理一般分为三部分:第一是critical,第二是Noncritical,第三是Noncritical deferrable,softirq就经常用于Noncritical deferrable阶段

1、优点:

1、softirq执行期间,中断处于使能

2、将其从中断处理函数中去除,可以减小kernel对中断反应时间,这对中断反应时间要求高的中断请求很重要

2、特点:

1、静态分配

2、必须具有可重入性,在多核系统中,几个CPU可以同时运行同种softirq

3、操作种类

1、初始化softirq

2、激活softirq

3、Mask softirq

4、处理softirq

4、Linux2.6 softirq种类

softirqindexDescription
HI_SOFTIRQ0Handles high priority tasklets
TIMER_SOFTIRQ1Tasklets related to timer interrupts
NET_TX_SOFTIRQ2Transmits packets to network cards
NET_RX_SOFTIRQ3Receives packets from network cards
SCSI_SOFTIRQ4Post-interrupt processing of SCSI commands
TASKLET_SOFTIRQ5Handles regular tasklets

 

5、重要数据类型

1、struct softirq_action

struct softirq_action
{
	void	(*action)(struct softirq_action *);
	void	*data;
};

static struct softirq_action softirq_vec[32] __cacheline_aligned_in_smp

注意:这里虽然定义了32个,但是其实只用了0-5前面六个,index越低,优先级越高 

2、preempt_count

struct thread_info {
	struct task_struct	*task;		/* main task structure */
	struct exec_domain	*exec_domain;	/* execution domain */
	unsigned long		flags;		/* low level flags */
	unsigned long		status;		/* thread-synchronous flags */
	__u32			cpu;		/* current CPU */
	__s32			preempt_count; /* 0 => preemptable, <0 => BUG */


	mm_segment_t		addr_limit;	/* thread address space:
					 	   0-0xBFFFFFFF for user-thead
						   0-0xFFFFFFFF for kernel-thread
						*/
	struct restart_block    restart_block;

	unsigned long           previous_esp;   /* ESP of the previous stack in case
						   of nested (IRQ) stacks
						*/
	__u8			supervisor_stack[0];
};

 preempt_count位域说明:

bit[7 : 0]            preemption counter       0代表可抢占,大于0代表禁止抢占

bit[15 : 8]          softirq counter                0代表可延迟函数使能,大于0代表可延迟函数禁止

bit[27 : 16]        hardirq counter               代表硬件中断嵌套层次,在irq_enter()和irq_exit()分别增加和减少

bit28                 preempt_active flag         

3、irq_cpustat_t

typedef struct {
	unsigned int __softirq_pending;
	unsigned long idle_timestamp;
	unsigned int __nmi_count;	/* arch dependent */
	unsigned int apic_timer_irqs;	/* arch dependent */
} ____cacheline_aligned irq_cpustat_t;

irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned

注意:这是一个per CPU类型 

6、代码分析

1、open_softirq

void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
{
	softirq_vec[nr].data = data;
	softirq_vec[nr].action = action;
}

EXPORT_SYMBOL(open_softirq);

根据softirq种类,来填充softirq_vec数组 

2、 raise_softirq

irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned

#ifndef __ARCH_IRQ_STAT
extern irq_cpustat_t irq_stat[];		/* defined in asm/hardirq.h */
#define __IRQ_STAT(cpu, member)	(irq_stat[cpu].member)
#endif

  /* arch independent irq_stat fields */
#define local_softirq_pending() \
	__IRQ_STAT(smp_processor_id(), __softirq_pending)


#define __raise_softirq_irqoff(nr) do { local_softirq_pending() |= 1UL << (nr); } while (0)

/*
 * This function must run with irqs disabled!
 */
inline fastcall 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();
}

EXPORT_SYMBOL(raise_softirq_irqoff);

void fastcall raise_softirq(unsigned int nr)
{
	unsigned long flags;

	local_irq_save(flags);
	raise_softirq_irqoff(nr);
	local_irq_restore(flags);
}

首先关闭local中断,然后调用raise_softirq_irqoff(),设置irq_stat数组中当前CPU对应的irq_cpustat_t中__softirq_pending字段对应的bit位, 判断是否在中断上下文(包括软中断和硬中断),如果不在,则唤醒ksoftirqd守护线程

3、do_softirq

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);
}

EXPORT_SYMBOL(do_softirq);

 1)首先判断是否在中断上下文,因为如果在中断上下文,则说明可能正在处理softirq,这种情况是需要禁止的

 2)获取irq_stat数组中当前CPU对应的irq_cpustat_t中__softirq_pending字段

 3)判断是否有pending的softirq,如果有,则调用__do_softirq

4、__do_softirq

*
 * 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.
 */
#define MAX_SOFTIRQ_RESTART 10

asmlinkage void __do_softirq(void)
{
	struct softirq_action *h;
	__u32 pending;
	int max_restart = MAX_SOFTIRQ_RESTART;
	int cpu;

	pending = local_softirq_pending();

	local_bh_disable();
	cpu = smp_processor_id();
restart:
	/* Reset the pending bitmask before enabling irqs */
	local_softirq_pending() = 0;

	local_irq_enable();

	h = softirq_vec;

	do {
		if (pending & 1) {
			h->action(h);
			rcu_bh_qsctr_inc(cpu);
		}
		h++;
		pending >>= 1;
	} while (pending);

	local_irq_disable();

	pending = local_softirq_pending();
	if (pending && --max_restart)
		goto restart;

	if (pending)
		wakeup_softirqd();

	__local_bh_enable();
}

1)  获取irq_stat数组中当前CPU对应的irq_cpustat_t中__softirq_pending字段到pending临时变量后,并它清零

2)找到pending中被置1的bit,逐个调用注册的softirq_action->action(),处理各个softirq

3)  判断是否有新的softirq产生,如果有,则继续处理

4)为了防止一直有softirq产生,kernel一直处理softirq,而导致用户程序被长时间挂起,所以这里限定最大次数为10次,如果10次后,依然有softirq产生,则唤醒ksoftirqd守护线程,留到下次处理

5、ksoftirqd

static DEFINE_PER_CPU(struct task_struct *, ksoftirqd)

p = kthread_create(ksoftirqd, hcpu, "ksoftirqd/%d", hotcpu)

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;
}

/*
 * we cannot loop indefinitely here to avoid userspace starvation,
 * but we also don't want to introduce a worst case 1/HZ latency
 * to the pending events, so lets the scheduler to balance
 * the softirq load for us.
 */
static inline 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、将ksoftirqd定义per CPU类型

2、通过kthread_create()创建ksoftirq线程,并注册回调函数ksoftirqd

3、ksoftirq将nice设置为19,可见这个优先级是比较低的,然后判断是否有softirq产生,如果没有,则睡眠

4、等醒来时,判断是否有softirq,如果没有,则继续睡眠,如果有,则调用do_softirq()

6、softirq检测场合

1、调用local_bh_enable( )

2、当处理完I/O中断时,调用irq_exit()

3、唤醒ksoftirqd守护线程

4、在多核系统中,各CPU间中断退出时,调用irq_exit()

5、如果系统中使用I/O APIC,smp_apic_timer_interrupt()结束时,调用irq_exit()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 《深入理解Linux内核》是一本经典的操作系统内核相关的书籍,它详细介绍了Linux内核的结构、逻辑和功能,为读者深入理解Linux操作系统提供了重要的参考。 第四版中文版epub的出版,不仅极大地方便了读者在电子设备上阅读学习,还将经典的内核学习资源带给更多、更广泛的读者,让更多的人通过它深入理解Linux内核。 该书主要包括内核基础、进程管理、进程间通信、系统调用、中断异常、设备驱动程序、文件系统等多个部分,通过深入讲解每个部分的特点和实现原理,为读者打开了Linux内核的大门,让其能够全面深入地了解Linux操作系统。 在学习的过程中,读者需要具备一定的操作系统和编程基础,但是,即使是初学者,也可以通过本书的详细讲解和案例研究逐步掌握操作系统内核的相关知识。 总之,对于学习操作系统内核知识的读者,这本书是一个不可或缺的重要资源,而第四版中文版epub的发布,更是进一步扩展了该书的影响范围,为更多读者带来了便利和学习机会。 ### 回答2: 《深入理解Linux内核》第四版是一本图书,主要介绍了Linux内核的运作原理和实现机制。它详细介绍了内核底层的数据结构、内存管理、进程调度、文件系统、网络协议栈、设备驱动等方面的知识,并提供了丰富的代码示例以帮助读者更好地理解。本书是日本著名的内核开发者Masami Hiramatsu和Takatoshi Kondo所著的,由于其权威性、详细性和实用性而备受欢迎。 本书适合具有一定Linux系统编程和操作经验的读者。对于想要从事Linux内核开发、系统调优、性能优化、驱动编写等方面的人员来说,本书是一本绝佳的参考书。此外,本书还能帮助读者更好地理解其他Linux系统相关的书籍和文档。 本书以深入浅出的方式,循序渐进地介绍了Linux内核的各个方面,同时也提供了丰富的例子和实用的技巧。读者可以从中掌握Linux内核的基本原理和实现方法,以及如何进行Linux系统的调优和优化。 总之,《深入理解Linux内核》第四版是一本权威、实用、详细的图书,对于想要更深入了解Linux内核的读者,是一本不可缺少的参考书。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值