`
bulote
  • 浏览: 1308225 次
文章分类
社区版块
存档分类
最新评论

Linux内核抢占-2

 
阅读更多

转自:http://www.shangshuwu.cn/index.php/Linux%E5%86%85%E6%A0%B8%E6%8A%A2%E5%8D%A0%E6%9C%BA%E5%88%B6(preempt)


Linux内核抢占机制(preempt)

早期的Linux核心是不可抢占的。它的调度方法是:一个进程可以通过schedule()函数自愿地启动一次调度。非自愿的强制性调度只能发生在每次从系统调用返回的前夕以及每次从中断或异常处理返回到用户空间的前夕。但是,如果在系统空间发生中断或异常是不会引起调度的。这种方式使内核实现得以简化。但常存在下面两个问题:

  • 如果这样的中断发生在内核中,本次中断返回是不会引起调度的,而要到最初使CPU从用户空间进入内核空间的那次系统调用或中断(异常)返回时才会发生调度。
  • 另外一个问题是优先级反转。在Linux中,在核心态运行的任何操作都要优先于用户态进程,这就有可能导致优先级反转问题的出现。例如,一个低优先级的用户进程由于执行软/硬中断等原因而导致一个高优先级的任务得不到及时响应。

当前的Linux内核加入了内核抢占(preempt)机制。内核抢占指用户程序在执行系统调用期间可以被抢占,该进程暂时挂起,使新唤醒的高优先级进程能够运行。这种抢占并非可以在内核中任意位置都能安全进行,比如在临界区中的代码就不能发生抢占。临界区是指同一时间内不可以有超过一个进程在其中执行的指令序列。在Linux内核中这些部分需要用自旋锁保护。

内核抢占要求内核中所有可能为一个以上进程共享的变量和数据结构就都要通过互斥机制加以保护,或者说都要放在临界区中。在抢占式内核中,认为如果内核不是在一个中断处理程序中,并且不在被 spinlock等互斥机制保护的临界代码中,就认为可以"安全"地进行进程切换。

Linux内核将临界代码都加了互斥机制进行保护,同时,还在运行时间过长的代码路径上插入调度检查点,打断过长的执行路径,这样,任务可快速切换进程状态,也为内核抢占做好了准备。

Linux内核抢占只有在内核正在执行例外处理程序(通常指系统调用)并且允许内核抢占时,才能进行抢占内核。禁止内核抢占的情况列出如下:

(1)内核执行中断处理例程时不允许内核抢占,中断返回时再执行内核抢占。

(2)当内核执行软中断或tasklet时,禁止内核抢占,软中断返回时再执行内核抢占。

(3)在临界区禁止内核抢占,临界区保护函数通过抢占计数宏控制抢占,计数大于0,表示禁止内核抢占。

抢占式内核实现的原理是在释放自旋锁时或从中断返回时,如果当前执行进程的 need_resched 被标记,则进行抢占式调度。

Linux内核在线程信息结构上增加了成员preempt_count作为内核抢占锁,为0表示可以进行内核高度,它随spinlock和rwlock等一起加锁和解锁。线程信息结构thread_info列出如下(在include/asm-x86/thread_info.h中):

struct thread_info {
	struct task_struct	*task;		/*主任务结构 */
	struct exec_domain	*exec_domain;	/* 执行的域*/
	__u32			flags;		/* low level flags */
	__u32			status;		/* 线程同步标识*/
	__u32			cpu;		/* 当前的CPU */
	int 			preempt_count;	/* 0 => 可以抢占(preemptable),
						   <0 => BUG */
	mm_segment_t		addr_limit;
	struct restart_block    restart_block;
#ifdef CONFIG_IA32_EMULATION
	void __user		*sysenter_return;
#endif
};

内核调度器的入口为preempt_schedule(),他将当前进程标记为TASK_PREEMPTED状态再调用schedule(),在TASK_PREEMPTED状态,schedule()不会将进程从运行队列中删除。

内核抢占API函数

在中断或临界区代码中,线程需要关闭内核抢占,因此,互斥机制(如:自旋锁(spinlock)、RCU等)、中断代码、链表数据遍历等需要关闭内核抢占,临界代码运行完时,需要开启内核抢占。关闭/开启内核抢占需要使用内核抢占API函数preempt_disable和preempt_enable。

内核抢占API函数说明如下(在include/linux/preempt.h中):

preempt_enable() //内核抢占计数preempt_count减1

preempt_disable() //内核抢占计数preempt_count加1

preempt_enable_no_resched()  //内核抢占计数preempt_count减1,但不立即抢占式调度

preempt_check_resched () //如果必要进行调度

preempt_count() //返回抢占计数

preempt_schedule() //核抢占时的调度程序的入口点

  内核抢占API函数的实现宏定义列出如下(在include/linux/preempt.h中):

#define preempt_disable() \

do { \
	inc_preempt_count(); \
	barrier(); \       //加内存屏障,阻止gcc编译器对内存进行优化
} while (0)
#define inc_preempt_count() \

do { \
	preempt_count()++; \
} while (0)

#define preempt_count()	(current_thread_info()->preempt_count)

内核抢占调度

Linux内核在硬中断或软中断返回时会检查执行抢占调度。分别说明如下:

(1)硬中断返回执行抢占调度

Linux内核在硬中断或出错退出时执行函数retint_kernel,运行抢占函数,函数retint_kernel列出如下(在arch/x86/entry_64.S中):

#ifdef CONFIG_PREEMPT
	/* 返回到内核空间,检查是否需要执行抢占*/
	/* 寄存器rcx存放threadinfo地址,此时,中断关闭*/
ENTRY(retint_kernel)
	cmpl $0,threadinfo_preempt_count(%rcx)
	jnz  retint_restore_args
	bt  $TIF_NEED_RESCHED,threadinfo_flags(%rcx)
	jnc  retint_restore_args
	bt   $9,EFLAGS-ARGOFFSET(%rsp)	/* 中断是否关闭? */
	jnc  retint_restore_args
	call preempt_schedule_irq
	jmp exit_intr
#endif

函数preempt_schedule_irq是出中断上下文时内核抢占调度的入口点,该函数被调用和返回时中断应关闭,保护此函数从中断递归调用。该函数列出如下(在kernel/sched.c中):

asmlinkage void __sched preempt_schedule_irq(void)
{
	struct thread_info *ti = current_thread_info();

	/* 用于捕捉需要修补的调用者 */
	BUG_ON(ti->preempt_count || !irqs_disabled());

	do {
                /*内核抢占计数加一个较大的值PREEMPT_ACTIVE,表示正在处理抢占,由于计数值较大,基本上不会再进行抢占调度*/
		add_preempt_count(PREEMPT_ACTIVE); 
		local_irq_enable();   /*开启中断*/
		schedule();           /*内核调度,用于内核抢占,即运行优先级较高的任务*/
		local_irq_disable();  /*关闭中断*/
		sub_preempt_count(PREEMPT_ACTIVE);

		/*再次检查,避免在调度与现在时刻之间失去抢占机会*/
		barrier();   /*加内存屏障*/
	} while (unlikely(test_thread_flag(TIF_NEED_RESCHED)));
}

调度函数schedule会检测进程的 preempt_counter 是否很大,避免普通调度时又执行内核抢占调度。

(2)软中断返回执行抢占调度

在打开页出错函数pagefault_enable和软中断底半部开启函数local_bh_enable中,会调用函数preempt_check_resched检查是否需要执行内核抢占。如果不是并能调度,进程才可执行内核抢占调度。函数preempt_check_resched列出如下:

#define preempt_check_resched() \
do { \     /*如果不是普通调度,才可执行抢占调度*/
	if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) \ 
		preempt_schedule(); \
} while (0)

函数preempt_schedule源代码与函数preempt_schedule_irq基本上一样,对进程进行调度,这里不再分析。


分享到:
评论

相关推荐

    linux内核抢占

    详细的讲解linux内核抢占机制,启动内核抢占的条件。

    linux内核抢占及驱动

    linux内核抢占及驱动附加,概述一览. 内涵Linux抢占原理介绍

    Linux可抢占内核的分析.PDF

    Linux可抢占内核的分析.PDF

    Linux内核抢占补丁的基本原理.pdf

    Linux内核抢占补丁的基本原理.pdf

    Linux内核抢占实现机制分析.doc

    详解了Linux内核抢占实现机制。首先介绍了内核抢占和用户抢占的概念和区别,接着分析了不可抢占内核的特点及实时系统中实现内核抢占的必要性。然后分析了禁止内核抢占的情况和内核抢占的时机,最后介绍了实现抢占...

    Linux内核的可抢占性分析和研究

    Linux内核的可抢占性分析和研究Linux内核的可抢占性分析和研究Linux内核的可抢占性分析和研究

    Linux内核抢占的实现机制分析.pdf

    Linux内核抢占的实现机制分析.pdf

    Linux系统内核抢占补丁的原理

    具 体的方法就是在进程的任务结构上增加一个preempt_count变量作为内核抢占锁,它随着spinlock和rwlock一起加锁和解锁。当 preempt_count为0时表示可以进行内核调度。内核调度器的入口为preempt_schedule(),它将...

    linux操作系统内核技术-uestc课件

     3介绍支持SMP的O(1)调度,用户和内核抢占和进程上下文切换,了解优先级复算,睡眠和唤醒机制,SMP的负载均衡。(4小时)  4掌握在x86体系结构上系统调用的具体实现原理,接口参数传递,用户地址空间和核心地址...

    linux-rt-rpi, 面向 树莓派的实时抢占内核.zip

    linux-rt-rpi, 面向 树莓派的实时抢占内核 用于的实时内核( )存储库包含了一个补丁了rt补丁并被配置为完全内核的树莓派 Linux内核。 下面提供了编译。设置和测试内核的说明。在Linux上实现内核的交叉编译下载 树莓派...

    Linux内核设计与实现(第2版 中文版)

    此书是当今首屈一指的linux内核入门最佳图书。作者是为2.6内核加入了抢占的人,对调度部分非常精通,而调度是整个系统的核心,因此本书是很权威的。这本书讲解浅显易懂,全书没有列举一条汇编语句,但是给出了整个...

    Linux内核设计与实现 part2(第二版 中文PDF) Linux Kernel Development (2nd Edition)

    本书填补了Linux内核理论和实践细节之间的鸿沟。本书针对Linux 2.6内核,包括O(1)调度程序、抢占式内核、块I/O层以及I/O调度程序等。本书还包含了Linux内核开发者在开发时需要用到的很多信息,包括调试技术、编程...

    关于Linux系统内核抢占补丁的原理说明

    Linux 2.4内核正好细化了多CPU下的内核线程同步...具体的方法就是在进程的任务结构上增加一个preempt_count变量作为内核抢占锁,它随着spinlock和rwlock一起加锁和解锁。当 preempt_count为0时表示可以进行内核调度。

    Linux内核设计与实现 part1(第二版 中文PDF) Linux Kernel Development (2nd Edition)

    本书填补了Linux内核理论和实践细节之间的鸿沟。Robed Love著,陈莉君翻译。 本书针对Linux 2.6内核,包括O(1)调度程序、抢占式内核、块I/O层以及I/O调度程序等。本书还包含了Linux内核开发者在开发时需要用到的很多...

    Linux2.6内核标准教程(共计8-- 第1个)

    1.1 为什么研究Linux内核 2 1.1.1 Linux的历史来源 2 1.1.2 Linux的发展现状 3 1.1.3 Linux的前景展望 3 1.2 选择什么版本进行研究 3 1.3 内核基本结构 4 1.3.1 内核在操作系统中的地位 4 1.3.2 ...

    Linux操作系统内核抢占补丁的基本原理

    CPU在内核中运行时并不是处处不可抢占的,内核中存在一些空隙,在这时进行抢占是安全的,内核抢占补丁的基本原理就是将SMP可并行的代码段看成是可以进行内核抢占的区域。2.4内核正好细化了多CPU下的内核线程同步机构...

    【论文】linux内核实时化技术

    个真正的实时操作系统,然而,Linux2.6 通过在内核中增添抢占点(preemption point), 将内核变为可抢占内核,并对进程调度程序以及内核同步设施(synchronization)做了 大幅的改进,使其在实时方面有了明显的改善...

    Linux2.6内核标准教程(共计8--第6个)

    1.1 为什么研究Linux内核 2 1.1.1 Linux的历史来源 2 1.1.2 Linux的发展现状 3 1.1.3 Linux的前景展望 3 1.2 选择什么版本进行研究 3 1.3 内核基本结构 4 1.3.1 内核在操作系统中的地位 4 1.3.2 ...

    Linux2.6内核标准教程(共计8--第8个)

    1.1 为什么研究Linux内核 2 1.1.1 Linux的历史来源 2 1.1.2 Linux的发展现状 3 1.1.3 Linux的前景展望 3 1.2 选择什么版本进行研究 3 1.3 内核基本结构 4 1.3.1 内核在操作系统中的地位 4 1.3.2 ...

Global site tag (gtag.js) - Google Analytics