SCHED_FIFO与SCHED_OTHER调度机制

Wesley13
• 阅读 777

疑问

两个线程分别有不同的调度策略,一个SCHED_FIFO,一个SCHED_OTHER,按照之前的理解,SCHED_FIFO实时线程一定会占用CPU一直运行,导致SCHED_OTHER的普通线程得不到CPU,事实是这样么?

验证

写了一小段代码,一个是验证SCHED_FIFO的高优先级线程会不会抢占低优先级的线程,在不主动放弃的情况下一直运行,一个是测试普通优先级的线程会不会得到CPU时间;

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #define __USE_GNU
 5 #include <pthread.h>
 6 #include <sched.h>
 7 
 8 long long a = 0;
 9 long long b = 0;
10 
11 int attach_cpu(int cpu_index)
12 {
13     int cpu_num = sysconf(_SC_NPROCESSORS_CONF);
14     if (cpu_index < 0 || cpu_index >= cpu_num) 
15     {
16         printf("cpu index ERROR!\n");
17         return -1;
18     }
19  
20     cpu_set_t mask;
21     CPU_ZERO(&mask);
22     CPU_SET(cpu_index, &mask);
23  
24     if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0)
25     {
26         printf("set affinity np ERROR!\n");
27         return -1;
28     }
29  
30     return 0;
31 }
32  
33 
34 void *thread1(void *param)
35 {
36     attach_cpu(0);
37 
38     long long i;
39     for (i = 0; i < 10000000000; i++)
40     {
41         a++;
42     }
43 }
44 
45 void *thread2(void *param)
46 {
47     attach_cpu(0);
48 
49     long long i;
50     for (i = 0; i < 10000000000; i++)
51     {
52         b++;
53     }
54 }
55 
56 
57 int main()
58 {
59     pthread_t t1;
60     pthread_t t2;
61     
62     int policy;
63     struct sched_param param;
64     
65     if (pthread_create(&t1, NULL, thread1, NULL) < 0)
66     {
67         printf("create t1 failed!\n");
68         return -1;
69     }
70 
71     param.sched_priority = 10;
72     policy = SCHED_FIFO;
73     pthread_setschedparam(t1, policy, &param);
74     
75     if (pthread_create(&t2, NULL, thread2, NULL) < 0)
76     {
77         printf("create t2 failed!\n");
78         return -1;
79     }
80     
81     param.sched_priority = 11;
82     policy = SCHED_FIFO;
83     pthread_setschedparam(t2, policy, &param);
84     
85     while (1)
86     {
87         printf("a=%lld, b=%lld\n", a, b);
88         sleep(1);
89     }
90     
91     pthread_join(t1, NULL);
92     pthread_join(t2, NULL);
93     
94     return 0;
95 }

通过运行结果来看:

1. SCHED_FIFO的高优先级线程会抢占低优先级线程;

2.SCHED_FIFO的高优先级线程一旦占用CPU并不主动放弃CPU的情况下将一直占用,此时低优先级线程得不到CPU时间;

3.主线程为SCHED_OTHER普通线程,得到了CPU时间,能够打印出信息;

原理

对于上述现象在manpage中已经有了很好的描述,以下摘录一些;前面的疑问在下面的限制实时线程的CPU使用时间部分;

调度策略

系统中的每个线程都关联了一个调度策略和优先级,调度器正是根据调度策略和优先级进行线程调度的,从而决定哪个线程将在下一个调度中得到CPU时间;

对于普通调度策略(SCHED_OTHER, SCHED_IDLE, SCHED_BATCH),优先级是没有作用的,实际上必须是0,这样实时测量线程可以马上抢占普通线程;

对于实时调度策略(SCHED_FIFO, SCHED_RR),优先级需要设置为1(最小)-99(最大)中的某个值;

调度器为每个优先级维护了一个待调度线程的列表,当需要进行调度时,调度器访问最高优先级的非空的列表,然后从列表头选择一个线程调度运行;

线程的调度策略决定了一个可调度线程应该放在哪个列表的哪个位置;

所有的调度都是支持抢占的,如果有高优先级的线程准备好运行了,那么它将抢占当前运行的线程,这使得当前线程被重新加入到等待调度的链表中;调度策略决定了在同一个优先级列表中的可调度线程的顺序;

SCHED_FIFO:先进先出调度

SCHED_FIFO线程的优先级必须大于0,当它运行时,一定会抢占正在运行的普通策略的线程(SCHED_OTHER, SCHED_IDLE, SCHED_BATCH);SCHED_FIFO策略是没有时间片的算法,需要遵循以下规则:

1)如果一个SCHED_FIFO线程被高优先级线程抢占了,那么它将会被添加到该优先级等待列表的首部,以便当所有高优先级的线程阻塞的时候得到继续运行;

2)当一个阻塞的SCHED_FIFO线程变为可运行时,它将被加入到同优先级列表的尾部;

3)如果通过系统调用改变线程的优先级,则根据不同情况有不同的处理方式:

a)如果优先级提高了,那么线程会被添加到所对应新优先级的尾部,因此,这个线程有可能会抢占当前运行的同优先级的线程;

b)如果优先级没变,那么线程在列表中的位置不变;

c)如果优先级降低了,那么它将被加入到新优先级列表的首部;

根据POSIX.1-2008规定,除了使用pthread_setschedprio(3)以外,通过使用其他方式改变策略或者优先级会使得线程加入到对应优先级列表的尾部;

4)如果线程调用了sched_yield(2),那么它将被加入到列表的尾部;

SCHED_FIFO会一直运行,直到它被IO请求阻塞,或者被更高优先级的线程抢占,亦或者调用了sched_yield();

SCHED_RR:轮转调度

SCHED_RR是SCHED_FIFO的简单增强,除了对于线程占用的时间总量之外,对于SCHED_FIFO适用的规则对于SCHED_RR同样适用;如果SCHED_RR线程的运行时间大于等于时间总量,那么它将被加入到对应优先级列表的尾部;如果SCHED_RR线程被抢占了,当它继续运行时它只运行剩余的时间量;时间总量可以通过sched_rr_get_interval()函数获取;

SCHED_OTHER:默认Linux时间共享调度

SCHED_OTHER只能用于优先级为0的线程,SCHED_OTHER策略是所有不需要实时调度线程的统一标准策略;调度器通过动态优先级来决定调用哪个SCHED_OTHER线程,动态优先级是基于nice值的,nice值随着等待运行但是未被调度执行的时间总量的增长而增加;这样的机制保证了所有SCHED_OTHER线程调度的公平性;

限制实时线程的CPU使用时间

SCHED_FIFO, SCHED_RR的线程如果内部是一个非阻塞的死循环,那么它将一直占用CPU,使得其它线程没有机会运行;

在2.6.25以后出现了限制实时线程运行时间的新方式,可以使用RLIMIT_RTTIME来限制实时线程的CPU占用时间;Linux也提供了两个proc文件,用于控制为非实时线程运行预留CPU时间;

/proc/sys/kernel/sched_rt_period_us

这个文件中的数值指定了总CPU(100%)时间的宽度值,默认值是1,000,000;

/proc/sys/kernel/sched_rt_runtime_us

这个文件中的数值指定了实时线程可以运行的CPU时间宽度,如果设置为-1,则认为不给非实时线程预留任何运行时间,默认值是950,000,因为第一个文件的总量是1,000,000,也就是说默认配置为非实时线程预留了5%的CPU时间;

manpage连接:http://man7.org/linux/man-pages/man7/sched.7.html

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Easter79 Easter79
3年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这