本篇文章给大家谈谈如何抢占linux内核,以及linux用户抢占和内核抢占对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔。
本文目录一览:
1、linux 用户进程 可以抢占内核进程吗
1.2.1 调度过程中关闭内核抢占
我们在上一篇linux内核主调度器schedule(文章链接, CSDN, Github)中在分析主调度器的时候, 我们会发现内核在进行调度之前都会通过preempt_disable关闭内核抢占, 而在完成调度工作后, 又会重新开启内核抢占
参见主调度器函数schedule
do {
preempt_disable(); /* 关闭内核抢占 */
__schedule(false); /* 完成调度 */
sched_preempt_enable_no_resched(); /* 开启内核抢占 */
} while (need_resched()); /* 如果该进程被其他进程设置了TIF_NEED_RESCHED标志,则函数重新执行进行调度 */123456123456
这个很容易理解, 我们在内核完成调度器过程中, 这时候如果发生了内核抢占, 我们的调度会被中断, 而调度却还没有完成, 这样会丢失我们调度的信息.
1.2.2 调度完成检查need_resched看是否需要重新调度
而同样我们可以看到, 在调度完成后, 内核会去判断need_resched条件, 如果这个时候为真, 内核会重新进程一次调度.
这个的原因, 我们在前一篇博客中, 也已经说的很明白了,
内核在thread_info的flag中设置了一个标识来标志进程是否需要重新调度, 即重新调度need_resched标识TIF_NEED_RESCHED, 内核在即将返回用户空间时会检查标识TIF_NEED_RESCHED标志进程是否需要重新调度,如果设置了,就会发生调度, 这被称为用户抢占
2 非抢占式和可抢占式内核
为了简化问题,我使用嵌入式实时系统uC/OS作为例子
首先要指出的是,uC/OS只有内核态,没有用户态,这和Linux不一样
多任务系统中, 内核负责管理各个任务, 或者说为每个任务分配CPU时间, 并且负责任务之间的通讯.
内核提供的基本服务是任务切换. 调度(Scheduler),英文还有一词叫dispatcher, 也是调度的意思.
这是内核的主要职责之一, 就是要决定该轮到哪个任务运行了. 多数实时内核是基于优先级调度法的, 每个任务根据其重要程度的不同被赋予一定的优先级. 基于优先级的调度法指,CPU总是让处在就绪态的优先级最高的任务先运行. 然而, 究竟何时让高优先级任务掌握CPU的使用权, 有两种不同的情况, 这要看用的是什么类型的内核, 是不可剥夺型的还是可剥夺型内核
2.1 非抢占式内核
非抢占式内核是由任务主动放弃CPU的使用权
非抢占式调度法也称作合作型多任务, 各个任务彼此合作共享一个CPU. 异步事件还是由中断服务来处理. 中断服务可以使一个高优先级的任务由挂起状态变为就绪状态.
但中断服务以后控制权还是回到原来被中断了的那个任务, 直到该任务主动放弃CPU的使用权时,那个高优先级的任务才能获得CPU的使用权。非抢占式内核如下图所示.
非抢占式内核的优点有
中断响应快(与抢占式内核比较);
允许使用不可重入函数;
几乎不需要使用信号量保护共享数据, 运行的任务占有CPU,不必担心被别的任务抢占。这不是绝对的,在打印机的使用上,仍需要满足互斥条件。
非抢占式内核的缺点有
任务响应时间慢。高优先级的任务已经进入就绪态,但还不能运行,要等到当前运行着的任务释放CPU
非抢占式内核的任务级响应时间是不确定的,不知道什么时候最高优先级的任务才能拿到CPU的控制权,完全取决于应用程序什么时候释放CPU
2.2 抢占式内核
使用抢占式内核可以保证系统响应时间. 最高优先级的任务一旦就绪, 总能得到CPU的使用权。当一个运行着的任务使一个比它优先级高的任务进入了就绪态, 当前任务的CPU使用权就会被剥夺,或者说被挂起了,那个高优先级的任务立刻得到了CPU的控制权。如果是中断服务子程序使一个高优先级的任务进入就绪态,中断完成时,中断了的任务被挂起,优先级高的那个任务开始运行。
抢占式内核如下图所示
抢占式内核的优点有
使用抢占式内核,最高优先级的任务什么时候可以执行,可以得到CPU的使用权是可知的。使用抢占式内核使得任务级响应时间得以最优化。
抢占式内核的缺点有:
不能直接使用不可重入型函数。调用不可重入函数时,要满足互斥条件,这点可以使用互斥型信号量来实现。如果调用不可重入型函数时,低优先级的任务CPU的使用权被高优先级任务剥夺,不可重入型函数中的数据有可能被破坏。
3 linux用户抢占
3.1 linux用户抢占
当内核即将返回用户空间时, 内核会检查need_resched是否设置, 如果设置, 则调用schedule(),此时,发生用户抢占.
3.2 need_resched标识
内核如何检查一个进程是否需要被调度呢?
内核在即将返回用户空间时检查进程是否需要重新调度,如果设置了,就会发生调度, 这被称为用户抢占, 因此内核在thread_info的flag中设置了一个标识来标志进程是否需要重新调度, 即重新调度need_resched标识TIF_NEED_RESCHED
并提供了一些设置可检测的函数
函数
描述
定义
set_tsk_need_resched 设置指定进程中的need_resched标志 include/linux/sched.h, L2920
clear_tsk_need_resched 清除指定进程中的need_resched标志 include/linux/sched.h, L2926
test_tsk_need_resched 检查指定进程need_resched标志 include/linux/sched.h, L2931
而我们内核中调度时常用的need_resched()函数检查进程是否需要被重新调度其实就是通过test_tsk_need_resched实现的, 其定义如下所示
//
static __always_inline bool need_resched(void)
{
return unlikely(tif_need_resched());
}
//
#define tif_need_resched() test_thread_flag(TIF_NEED_RESCHED)1234567812345678
3.3 用户抢占的发生时机(什么时候需要重新调度need_resched)
一般来说,用户抢占发生几下情况:
从系统调用返回用户空间;
从中断(异常)处理程序返回用户空间
从这里我们可以看到, 用户抢占是发生在用户空间的抢占现象.
更详细的触发条件如下所示, 其实不外乎就是前面所说的两种情况: 从系统调用或者中断返回用户空间
时钟中断处理例程检查当前任务的时间片,当任务的时间片消耗完时,scheduler_tick()函数就会设置need_resched标志;
信号量、等到队列、completion等机制唤醒时都是基于waitqueue的,而waitqueue的唤醒函数为default_wake_function,其调用try_to_wake_up将被唤醒的任务更改为就绪状态并设置need_resched标志。
设置用户进程的nice值时,可能会使高优先级的任务进入就绪状态;
改变任务的优先级时,可能会使高优先级的任务进入就绪状态;
新建一个任务时,可能会使高优先级的任务进入就绪状态;
对CPU(SMP)进行负载均衡时,当前任务可能需要放到另外一个CPU上运行
4 linux内核抢占
4.1 内核抢占的概念
对比用户抢占, 顾名思义, 内核抢占就是指一个在内核态运行的进程, 可能在执行内核函数期间被另一个进程取代.
4.2 为什么linux需要内核抢占
linux系统中, 进程在系统调用后返回用户态之前, 或者是内核中某些特定的点上, 都会调用调度器. 这确保除了一些明确指定的情况之外, 内核是无法中断的, 这不同于用户进程.
如果内核处于相对耗时的操作中, 比如文件系统或者内存管理相关的任务, 这种行为可能会带来问题. 这种情况下, 内核代替特定的进程执行相当长的时间, 而其他进程无法执行, 无法调度, 这就造成了系统的延迟增加, 用户体验到”缓慢”的响应. 比如如果多媒体应用长时间无法得到CPU, 则可能发生视频和音频漏失现象.
在编译内核时如果启用了对内核抢占的支持, 则可以解决这些问题. 如果高优先级进程有事情需要完成, 那么在启用了内核抢占的情况下, 不仅用户空间应用程序可以被中断, 内核也可以被中断,
linux内核抢占是在Linux2.5.4版本发布时加入的, 尽管使内核可抢占需要的改动特别少, 但是该机制不像抢占用户空间进程那样容易实现. 如果内核无法一次性完成某些操作(例如, 对数据结构的操作), 那么可能出现静态条件而使得系统不一致.
内核抢占和用户层进程被其他进程抢占是两个不同的概念, 内核抢占主要是从实时系统中引入的, 在非实时系统中的确也能提高系统的响应速度, 但也不是在所有情况下都是最优的,因为抢占也需要调度和同步开销,在某些情况下甚至要关闭内核抢占, 比如前面我们将主调度器的时候, linux内核在完成调度的过程中是关闭了内核抢占的.
内核不能再任意点被中断, 幸运的是, 大多数不能中断的点已经被SMP实现标识出来了. 并且在实现内核抢占时可以重用这些信息. 如果内核可以被抢占, 那么单处理器系统也会像是一个SMP系统
4.3 内核抢占的发生时机
要满足什么条件,kernel才可以抢占一个任务的内核态呢?
没持有锁。锁是用于保护临界区的,不能被抢占。
Kernel code可重入(reentrant)。因为kernel是SMP-safe的,所以满足可重入性。
内核抢占发生的时机,一般发生在:
当从中断处理程序正在执行,且返回内核空间之前。当一个中断处理例程退出,在返回到内核态时(kernel-space)。这是隐式的调用schedule()函数,当前任务没有主动放弃CPU使用权,而是被剥夺了CPU使用权。
当内核代码再一次具有可抢占性的时候,如解锁(spin_unlock_bh)及使能软中断(local_bh_enable)等, 此时当kernel code从不可抢占状态变为可抢占状态时(preemptible again)。也就是preempt_count从正整数变为0时。这也是隐式的调用schedule()函数
如果内核中的任务显式的调用schedule(), 任务主动放弃CPU使用权
如果内核中的任务阻塞(这同样也会导致调用schedule()), 导致需要调用schedule()函数。任务主动放弃CPU使用权
内核抢占,并不是在任何一个地方都可以发生,以下情况不能发生
内核正进行中断处理。在Linux内核中进程不能抢占中断(中断只能被其他中断中止、抢占,进程不能中止、抢占中断),在中断例程中不允许进行进程调度。进程调度函数schedule()会对此作出判断,如果是在中断中调用,会打印出错信息。
内核正在进行中断上下文的Bottom Half(中断下半部,即软中断)处理。硬件中断返回前会执行软中断,此时仍然处于中断上下文中。如果此时正在执行其它软中断,则不再执行该软中断。
内核的代码段正持有spinlock自旋锁、writelock/readlock读写锁等锁,处干这些锁的保护状态中。内核中的这些锁是为了在SMP系统中短时间内保证不同CPU上运行的进程并发执行的正确性。当持有这些锁时,内核不应该被抢占。
内核正在执行调度程序Scheduler。抢占的原因就是为了进行新的调度,没有理由将调度程序抢占掉再运行调度程序。
内核正在对每个CPU“私有”的数据结构操作(Per-CPU date structures)。在SMP中,对于per-CPU数据结构未用spinlocks保护,因为这些数据结构隐含地被保护了(不同的CPU有不一样的per-CPU数据,其他CPU上运行的进程不会用到另一个CPU的per-CPU数据)。但是如果允许抢占,但一个进程被抢占后重新调度,有可能调度到其他的CPU上去,这时定义的Per-CPU变量就会有问题,这时应禁抢占。
2、linux内核怎么调度系统
1.调度器的概述
多任务操作系统分为非抢占式多任务和抢占式多任务。与大多数现代操作系统一样,Linux采用的是抢占式多任务模式。这表示对CPU的占用时间由操作系统决定的,具体为操作系统中的调度器。调度器决定了什么时候停止一个进程以便让其他进程有机会运行,同时挑选出一个其他的进程开始运行。
2.调度策略
在Linux上调度策略决定了调度器是如何选择一个新进程的时间。调度策略与进程的类型有关,内核现有的调度策略如下:
#define SCHED_NORMAL 0#define SCHED_FIFO 1#define SCHED_RR 2#define SCHED_BATCH 3/* SCHED_ISO: reserved but not implemented yet */#define SCHED_IDLE 5
0: 默认的调度策略,针对的是普通进程。
1:针对实时进程的先进先出调度。适合对时间性要求比较高但每次运行时间比较短的进程。
2:针对的是实时进程的时间片轮转调度。适合每次运行时间比较长得进程。
3:针对批处理进程的调度,适合那些非交互性且对cpu使用密集的进程。
SCHED_ISO:是内核的一个预留字段,目前还没有使用
5:适用于优先级较低的后台进程。
注:每个进程的调度策略保存在进程描述符task_struct中的policy字段
3.调度器中的机制
内核引入调度类(struct sched_class)说明了调度器应该具有哪些功能。内核中每种调度策略都有该调度类的一个实例。(比如:基于公平调度类为:fair_sched_class,基于实时进程的调度类实例为:rt_sched_class),该实例也是针对每种调度策略的具体实现。调度类封装了不同调度策略的具体实现,屏蔽了各种调度策略的细节实现。
调度器核心函数schedule()只需要调用调度类中的接口,完成进程的调度,完全不需要考虑调度策略的具体实现。调度类连接了调度函数和具体的调度策略。
武特师兄关于sche_class和sche_entity的解释,一语中的。
调度类就是代表的各种调度策略,调度实体就是调度单位,这个实体通常是一个进程,但是自从引入了cgroup后,这个调度实体可能就不是一个进程了,而是一个组
4.schedule()函数
linux 支持两种类型的进程调度,实时进程和普通进程。实时进程采用SCHED_FIFO 和SCHED_RR调度策略,普通进程采用SCHED_NORMAL策略。
preempt_disable():禁止内核抢占
cpu_rq():获取当前cpu对应的就绪队列。
prev = rq-curr;获取当前进程的描述符prev
switch_count = prev-nivcsw;获取当前进程的切换次数。
update_rq_clock() :更新就绪队列上的时钟
clear_tsk_need_resched()清楚当前进程prev的重新调度标志。
deactive_task():将当前进程从就绪队列中删除。
put_prev_task() :将当前进程重新放入就绪队列
pick_next_task():在就绪队列中挑选下一个将被执行的进程。
context_switch():进行prev和next两个进程的切换。具体的切换代码与体系架构有关,在switch_to()中通过一段汇编代码实现。
post_schedule():进行进程切换后的后期处理工作。
5.pick_next_task函数
选择下一个将要被执行的进程无疑是一个很重要的过程,我们来看一下内核中代码的实现
对以下这段代码说明:
1.当rq中的运行队列的个数(nr_running)和cfs中的nr_runing相等的时候,表示现在所有的都是普通进程,这时候就会调用cfs算法中的pick_next_task(其实是pick_next_task_fair函数),当不相等的时候,则调用sched_class_highest(这是一个宏,指向的是实时进程),这下面的这个for(;;)循环中,首先是会在实时进程中选取要调度的程序(p = class-pick_next_task(rq);)。如果没有选取到,会执行class=class-next;在class这个链表中有三种类型(fair,idle,rt).也就是说会调用到下一个调度类。
static inline struct task_struct *pick_next_task(struct rq *rq){ const struct sched_class *class; struct task_struct *p; /*
* Optimization: we know that if all tasks are in
* the fair class we can call that function directly:
*///基于公平调度的普通进程
if (likely(rq-nr_running == rq-cfs.nr_running)) {
p = fair_sched_class.pick_next_task(rq); if (likely(p)) return p;
}//基于实时调度的实时进程
class = sched_class_highest; for ( ; ; ) {
p = class-pick_next_task(rq); //实时进程的类
if (p) return p; /*
* Will never be NULL as the idle class always
* returns a non-NULL p:
*/
class = class-next; //rt-next = fair; fair-next = idle
}
}
在这段代码中体现了Linux所支持的两种类型的进程,实时进程和普通进程。回顾下:实时进程可以采用SCHED_FIFO 和SCHED_RR调度策略,普通进程采用SCHED_NORMAL调度策略。
在这里首先说明一个结构体struct rq,这个结构体是调度器管理可运行状态进程的最主要的数据结构。每个cpu上都有一个可运行的就绪队列。刚才在pick_next_task函数中看到了在选择下一个将要被执行的进程时实际上用的是struct rq上的普通进程的调度或者实时进程的调度,那么具体是如何调度的呢?在实时调度中,为了实现O(1)的调度算法,内核为每个优先级维护一个运行队列和一个DECLARE_BITMAP,内核根据DECLARE_BITMAP的bit数值找出非空的最高级优先队列的编号,从而可以从非空的最高级优先队列中取出进程进行运行。
我们来看下内核的实现
struct rt_prio_array {
DECLARE_BITMAP(bitmap, MAX_RT_PRIO+1); /* include 1 bit for delimiter */
struct list_head queue[MAX_RT_PRIO];
};
数组queue[i]里面存放的是优先级为i的进程队列的链表头。在结构体rt_prio_array 中有一个重要的数据构DECLARE_BITMAP,它在内核中的第一如下:
define DECLARE_BITMAP(name,bits) \
unsigned long name[BITS_TO_LONGS(bits)]
5.1对于实时进程的O(1)算法
这个数据是用来作为进程队列queue[MAX_PRIO]的索引位图。bitmap中的每一位与queue[i]对应,当queue[i]的进程队列不为空时,Bitmap的相应位就为1,否则为0,这样就只需要通过汇编指令从进程优先级由高到低的方向找到第一个为1的位置,则这个位置就是就绪队列中最高的优先级(函数sched_find_first_bit()就是用来实现该目的的)。那么queue[index]-next就是要找的候选进程。
如果还是不懂,那就来看两个图
注:在每个队列上的任务一般基于先进先出的原则进行调度(并且为每个进程分配时间片)
在内核中的实现为:
static struct sched_rt_entity *pick_next_rt_entity(struct rq *rq, struct rt_rq *rt_rq){ struct rt_prio_array *array = rt_rq-active; struct sched_rt_entity *next = NULL; struct list_head *queue; int idx;
idx = sched_find_first_bit(array-bitmap); //找到优先级最高的位
BUG_ON(idx = MAX_RT_PRIO); queue = array-queue + idx; //然后找到对应的queue的起始地址
next = list_entry(queue-next, struct sched_rt_entity, run_list); //按先进先出拿任务
return next;
}
那么当同一优先级的任务比较多的时候,内核会根据
位图:
将对应的位置为1,每次取出最大的被置为1的位,表示优先级最高:
5.2 关于普通进程的CFS算法:
我们知道,普通进程在选取下一个需要被调度的进程时,是调用的pick_next_task_fair函数。在这个函数中是以调度实体为单位进行调度的。其最主要的函数是:pick_next_entity,在这个函数中会调用wakeup_preempt_entity函数,这个函数的主要作用是根据进程的虚拟时间以及权重的结算进程的粒度,以判断其是否需要抢占。看一下内核是怎么实现的:
wakeup_preempt_entity(struct sched_entity *curr, struct sched_entity *se)
{
s64 gran, vdiff = curr-vruntime - se-vruntime;//计算两个虚拟时间差//如果se的虚拟时间比curr还大,说明本该curr执行,无需抢占
if (vdiff = 0) return -1;
gran = wakeup_gran(curr, se); if (vdiff gran) return 1; return 0;
}
gran为需要抢占的时间差,只有两个时间差大于需要抢占的时间差,才需要抢占,这里避免太频繁的抢占
wakeup_gran(struct sched_entity *curr, struct sched_entity *se)
{
unsigned long gran = sysctl_sched_wakeup_granularity; if (cfs_rq_of(curr)-curr sched_feat(ADAPTIVE_GRAN))
gran = adaptive_gran(curr, se);
/*
* Since its curr running now, convert the gran from real-time
* to virtual-time in his units.
*/ if (sched_feat(ASYM_GRAN)) {
/*
* By using 'se' instead of 'curr' we penalize light tasks, so
* they get preempted easier. That is, if 'se' 'curr' then
* the resulting gran will be larger, therefore penalizing the
* lighter, if otoh 'se' 'curr' then the resulting gran will
* be smaller, again penalizing the lighter task.
*
* This is especially important for buddies when the leftmost
* task is higher priority than the buddy.
*/ if (unlikely(se-load.weight != NICE_0_LOAD))
gran = calc_delta_fair(gran, se);
} else { if (unlikely(curr-load.weight != NICE_0_LOAD))
gran = calc_delta_fair(gran, curr);
} return gran;
}
6.调度中的nice值
首先需要明确的是:nice的值不是进程的优先级,他们不是一个概念,但是进程的Nice值会影响到进程的优先级的变化。
通过命令ps -el可以看到进程的nice值为NI列。PRI表示的是进程的优先级,其实进程的优先级只是一个整数,它是调度器选择进程运行的基础。
普通进程有:静态优先级和动态优先级。
静态优先级:之所有称为静态优先级是因为它不会随着时间而改变,内核不会修改它,只能通过系统调用nice去修改,静态优先级用进程描述符中的static_prio来表示。在内核中/kernel/sched.c中,nice和静态优先级的关系为:
#define NICE_TO_PRIO(nice) (MAX_RT_PRIO + (nice) + 20)
#define PRIO_TO_NICE(prio) ((prio) - MAX_RT_PRIO - 20)
动态优先级:调度程序通过增加或者减小进程静态优先级的值来奖励IO小的进程或者惩罚cpu消耗型的进程。调整后的优先级称为动态优先级。在进程描述中用prio来表示,通常所说的优先级指的是动态优先级。
由上面分析可知,我们可以通过系统调用nice函数来改变进程的优先级。
#include stdlib.h#include stdio.h#include math.h#include unistd.h#include sys/time.h#define JMAX (400*100000)#define GET_ELAPSED_TIME(tv1,tv2) ( \
(double)( (tv2.tv_sec - tv1.tv_sec) \
+ .000001 * (tv2.tv_usec - tv1.tv_usec)))//做一个延迟的计算double do_something (void){ int j; double x = 0.0; struct timeval tv1, tv2;
gettimeofday (tv1, NULL);//获取时区
for (j = 0; j JMAX; j++)
x += 1.0 / (exp ((1 + x * x) / (2 + x * x)));
gettimeofday (tv2, NULL); return GET_ELAPSED_TIME (tv1, tv2);//求差值}int main (int argc, char *argv[]){ int niceval = 0, nsched; /* for kernels less than 2.6.21, this is HZ
for tickless kernels this must be the MHZ rate
e.g, for 2.6 GZ scale = 2600000000 */
long scale = 1000; long ticks_cpu, ticks_sleep; pid_t pid;
FILE *fp; char fname[256]; double elapsed_time, timeslice, t_cpu, t_sleep; if (argc 1)
niceval = atoi (argv[1]);
pid = getpid (); if (argc 2)
scale = atoi (argv[2]); /* give a chance for other tasks to queue up */
sleep (3); sprintf (fname, "/proc/%d/schedstat", pid);//读取进程的调度状态
/*
在schedstat中的数字是什么意思呢?:
*/
/* printf ("Fname = %s\n", fname); */
if (!(fp = fopen (fname, "r"))) { printf ("Failed to open stat file\n"); exit (-1);
} //nice系统调用
if (nice (niceval) == -1 niceval != -1) { printf ("Failed to set nice to %d\n", niceval); exit (-1);
}
elapsed_time = do_something ();//for 循环执行了多长时间
fscanf (fp, "%ld %ld %d", ticks_cpu, ticks_sleep, nsched);//nsched表示调度的次数
t_cpu = (float)ticks_cpu / scale;//震动的次数除以1000,就是时间
t_sleep = (float)ticks_sleep / scale;
timeslice = t_cpu / (double)nsched;//除以调度的次数,就是每次调度的时间(时间片)
printf ("\nnice=%3d time=%8g secs pid=%5d"
" t_cpu=%8g t_sleep=%8g nsched=%5d"
" avg timeslice = %8g\n",
niceval, elapsed_time, pid, t_cpu, t_sleep, nsched, timeslice);
fclose (fp); exit (0);
}
说明: 首先说明的是/proc/[pid]/schedstat:在这个文件下放着3个变量,他们分别代表什么意思呢?
第一个:该进程拥有的cpu的时间
第二个:在对列上的等待时间,即睡眠时间
第三个:被调度的次数
由结果可以看出当nice的值越小的时候,其睡眠时间越短,则表示其优先级升高了。
7.关于获取和设置优先级的系统调用:sched_getscheduler()和sched_setscheduler
#include sched.h#include stdlib.h#include stdio.h#include errno.h#define DEATH(mess) { perror(mess); exit(errno); }void printpolicy (int policy){ /* SCHED_NORMAL = SCHED_OTHER in user-space */
if (policy == SCHED_OTHER) printf ("policy = SCHED_OTHER = %d\n", policy); if (policy == SCHED_FIFO) printf ("policy = SCHED_FIFO = %d\n", policy); if (policy == SCHED_RR) printf ("policy = SCHED_RR = %d\n", policy);
}int main (int argc, char **argv){ int policy; struct sched_param p; /* obtain current scheduling policy for this process */
//获取进程调度的策略
policy = sched_getscheduler (0);
printpolicy (policy); /* reset scheduling policy */
printf ("\nTrying sched_setscheduler...\n");
policy = SCHED_FIFO;
printpolicy (policy);
p.sched_priority = 50; //设置优先级为50
if (sched_setscheduler (0, policy, p))
DEATH ("sched_setscheduler:"); printf ("p.sched_priority = %d\n", p.sched_priority); exit (0);
}
输出结果:
[root@wang schedule]# ./get_schedule_policy policy = SCHED_OTHER = 0
Trying sched_setscheduler...
policy = SCHED_FIFO = 1
p.sched_priority = 50
可以看出进程的优先级已经被改变。
3、Linux内核抢占是否抢占正在执行的系统调用?
任务调度只对进程(线程)有效。
任务调度只能调度“调用的write系统调用”的进程。
一旦已经进入系统调用,就必须等到系统调用执行完毕。
4、linux进程处于内核态会被更高优先级抢占吗
内核态抢占(Kernel
Preemption)
在2.6
kernel以前,kernelcode(中断和系统调用属于kernel
code)会一直运行,直到code被完成或者被阻塞(系统调用可以被阻塞)。在
2.6kernel里,Linuxkernel变成可抢占式。当从中断处理例程返回到内核态(kernel-space)时,kernel会检查是否可以抢占和是否需要重新调度。kernel可以在任何时间点上抢占一个任务(因为中断可以发生在任何时间点上),只要在这个时间点上kernel的状态是安全的、可重新调度的。
如何抢占linux内核的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于linux用户抢占和内核抢占、如何抢占linux内核的信息别忘了在本站进行查找喔。