早期的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;

__u32 status;

__u32 cpu;

int preempt_count;

mm_segment_t addr_limit;

struct restart_block restart_block;

#ifdef CONFIG_IA32_EMULATION

void __user *sysenter_return;

#endif

};

#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

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 {

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))) /

linux程序不可抢占,Linux内核抢占机制(preempt)相关推荐

  1. Linux用户抢占和内核抢占详解(概念, 实现和触发时机)--Linux进程的管理与调度(二十)

    日期 内核版本 架构 作者 GitHub CSDN 2016-07-01 Linux-4.6 X86 & arm gatieme LinuxDeviceDrivers Linux进程管理与调度 ...

  2. linux 程序占内存,linux概念之程序占用内存

    1.在linux下,查看一个运行中的程序,占用了多少内存,一般的命令有 (1). ps aux: 其中  VSZ(或VSS)列 表示,程序占用了多少虚拟内存. RSS列 表示, 程序占用了多少物理内存 ...

  3. linux 程序占内存,linux下,一个运行中的程序,究竟占用了多少内存

    1. 在linux下,查看一个运行中的程序, 占用了多少内存, 通常的命令有php (1). ps aux:html 其中  VSZ(或VSS)列 表示,程序占用了多少虚拟内存.linux RSS列 ...

  4. linux 程序退出 调试,linux驱动程序调试常用方法(printk,OOP,strace,hacking,ioctl,/proc,kgdb)...

    驱动程序开发的一个重大难点就是不易调试.本文目的就是介绍驱动开发中常用的几种直接和间接的调试手段,它们是: 利用printk 查看OOP消息 利用strace 利用内核内置的hacking选项 利用i ...

  5. 单片机上运行linux程序代码,在Linux下烧录51单片机

    原标题:在Linux下烧录51单片机 *本文作者:LEdge1,本文属 FreeBuf原创奖励计划,未经许可禁止转载. 背景 我一直在学习Linux 系统,但是最近还要学习51单片机,所以在Linux ...

  6. 后台运行linux程序,后台运行Linux程序的方法

    后台运行Linux程序,可以通过crontab设置,这种方法一般用来让一个程序定时运行,也可以通过./test &这样在程序末尾加上一个&使程序在后台运行. 编写代码,下面我将参考ht ...

  7. linux程序无法启动,Linux无法启动解决方法[阮胜昌]

    2007-10-22 22:27 linux中修复GRUB 呵呵,最近2003系统老出问题,主要是防火墙导致很多聊天软件都用不了@|@哎,有时候甚至把MBR文件也弄没了没办法重新安装GRUB么???? ...

  8. 安卓跑linux程序_Android 运行 Linux 可执行程序

    /**************************************************************************** * Android 运行 Linux 可执行 ...

  9. 整个linux程序的框架,Linux兼容Win32程序,一个框架、两个界面

    Linux兼容Win32程序,"一个框架.两个界面" Linux兼容Win32程序,"一个框架.两个界面" [日期:2007-07-19] 来源:Linux公社 ...

  10. Linux程序文件状态,linux 文件状态标识和IO操作方式

    转自:http://book.2cto.com/201212/11767.html 来自<UNIX/Linux程序设计教程> 文件状态标签指明文件的打开属性,它们由open()的flags ...

最新文章

  1. ASP.NET Core Web Razor Pages系列教程七: 添加新的字段
  2. 【下载】支持中文的 jspSmartUpload jar 包
  3. C++多线程简单入门(二)(Windows版)
  4. 认识mongodb文档的动态模式
  5. C#中委托和事件的区别
  6. memcached全面剖析–memcached的删除机制和发展方向
  7. ieee浮点数与常规浮点数_浮点数如何工作
  8. png图片怎么转换成jpg?
  9. python 条件循环赋值_python学习(五) 条件、循环和其他语句
  10. Codevs1378选课题解
  11. SQL/MYSQL在CMD命令操作符中创建数据库 、表单并插入数据查看
  12. 流量卡之家:三大运营商停售达量限速套餐?联通移动称未接到通知
  13. mysql数据库管理贡酒_【多选题】下列名酒产于四川的有(2.0分)A. 茅台酒 B. 五粮液 C. 古井贡酒 D. 剑南春...
  14. php大转盘程序,jQuery幸运大转盘_jQuery+PHP抽奖程序(下)
  15. java pdf添加透明水印,PDF怎么加透明水印?
  16. 代码规范检查工具+代码规范生成工具
  17. 信息论基础(信息量、熵、KL散度、交叉熵)
  18. java 邮件中文标题显示问号?
  19. 博通Broadcom SDK源码学习与开发10——Cable Modem IPv6地址
  20. 安卓笔记:修正Task ‘wrapper‘ not found in project

热门文章

  1. 迁移CentOS 8到版本 almalinux8
  2. 2017年,你还在用用户画像和协同过滤做推荐系统吗?
  3. MQTT 协议基本介绍
  4. 机器人炒菜感想_机器人炒菜 会炒600多道菜
  5. 修改家中的WiFi密码
  6. 职场上同事不会给你说的10个潜规则!
  7. 2020年校内ACM排位赛 NO.1场 题解反思
  8. python 海象运算符_python := 海象运算符
  9. 网站标题ico那些事
  10. 支付宝即时到帐 java_java 支付宝 第三方即时到账支付 接口