文章目录
- 1.理论基础
- 1.1.sysctl指令与内核调度
- 2.代码分析
- 2.1 函数说明
- 2.2 源码编译
- 3.单进程中,两个线程,执行顺序如何?
- 3.1 两个线程SCHED_RR50与SCHED_RR50
- 3.1.1通过proc文件系统查看调度
- 3.2 两个线程SCHED_RR50与SCHED_RR60
- 3.3 两个线程SCHED_FIFO50与SCHED_FIFO50
- 3.4 两个线程SCHED_FIFO50与SCHED_FIFO60
- 3.5 两个线程SCHED_RR50与SCHED_FIFO50
- 3.5.1 延时大于100ms
- 3.5.2 延时小于100ms
- 4 总结分析
1.理论基础
本节旨在说明RT调度器下,即使用SCHED_RR和SCHED_FIFO两种调度策略情况下,单进程双线程的优先级与调度情况,即采用一个进程中一共两个线程来说明调度问题。
之前已经解释了调度器的知识,但是通过实例化后可以加深对此的理解,进一步理解RT调度器的机制。
环境配置:单核配置、关闭组调度、ubuntu虚拟机。
1.1.sysctl指令与内核调度
我们可以使用sysctl指令查看与配置内核调度相关的参数,我们先看下参数的含义:
//sysctl -a|grep sched
kernel.sched_autogroup_enabled = 0 //sysctl -w kernel.sched_autogroup_enabled=0/1关闭或者打开组调度
kernel.sched_cfs_bandwidth_slice_us = 5000
kernel.sched_child_runs_first = 0
kernel.sched_latency_ns = 6000000
kernel.sched_migration_cost_ns = 500000
kernel.sched_min_granularity_ns = 750000
kernel.sched_nr_migrate = 32
kernel.sched_rr_timeslice_ms = 100 //rr的调度策略时间片尾100ms
kernel.sched_rt_period_us = 1000000 //rt调度器的运行周期为1s
kernel.sched_rt_runtime_us = 950000 //此处是rt调度器是实际运行时间,设置为0.95s是因为需要给cfs调度器让出0.05s的执行时间
kernel.sched_schedstats = 0
kernel.sched_time_avg_ms = 1000
kernel.sched_tunable_scaling = 1
kernel.sched_wakeup_granularity_ns = 1000000
2.代码分析
2.1 函数说明
我们通过单进程双线程来说明线程调度与优先级的问题;先上源码:
源码在此就不做一步一步分析了,来看下关键的几个函数:
线程创建函数:pthread_create函数用来创建线程;
延时函数:
1.此处的delay函数是通过死循环完成的,而不是sleep或者nanosleep函数实现的,因为这两个函数会将目前的任务置为TASK_INTERRUPTIBLE状态,此会影响实际的线程调度的,因此在用户态使用此类函数一定需要注意对线程的影响;
2.还有一个层面的问题,就是延时函数的时长问题,我们采用时长是大于100ms的,为什么有这么个考虑,主要是基于内核对于SCHED_RR调度策略的时间片尾100ms,不至于时间太短而体现不出现RR调度器的调度状态;
时间获取函数:使用time和localtime函数来获取实时的时间参数,来说明线程执行的顺序,
优先级问题:此处设置的优先级与内核对应的0-99的优先级使用的有点差异,对于内核来说,数字越大,优先级越低,我们通过用户态的函数调度设置的优先级是相反的,数字越大,优先级越高,两者对应的关系为:内核优先级=99 - 用户态设置的优先级;可以通过proc文件系统的sched来进行分析查看;
#include <unistd.h>
#include <pthread.h>
#include <sched.h>
#include <stdio.h>
typedef struct tagTestClass
{
int main_sched_priority;
int main_sched_strategy;
int slave_sched_priority;
int slave_sched_strategy;
}T_TestClass;
int getdate(void);
int delay(void);
#define TEST_PRINTF \
do{\
for(int index = 0;index < 10;index++)\
{\
delay();\
printf("%s %s %d ",__FILE__,__FUNCTION__,index);\
getdate();\
}\
}while(0);
T_TestClass g_testClass = {50,SCHED_FIFO,50,SCHED_FIFO};
int delay(void)//通过此处的死循环来修改延时的时长,本机实测大概为3s左右;
{
for(int i = 0; i< 1000000000;i++)
{}
}
char* getStrategy(int prio)
{
char *a[5] = {"NORMAL","FIFO","RR","BATCH"};
return a[prio];
}
int getdate(void)
{
time_t t;
struct tm *timeinfo; //结构体
time(&t);
timeinfo = localtime(&t);
printf("time:%s", asctime(timeinfo)); //以字符串形式输出localtime本地时间
return 0;
}
void init(int index,int print_flag)
{
T_TestClass testClass[] = {
{50,SCHED_RR,50,SCHED_RR},
{50,SCHED_RR,60,SCHED_RR},
{50,SCHED_FIFO,50,SCHED_FIFO},
{50,SCHED_FIFO,60,SCHED_FIFO},
{40,SCHED_FIFO,70,SCHED_FIFO},
{70,SCHED_FIFO,40,SCHED_FIFO},
{50,SCHED_RR,50,SCHED_FIFO},
{50,SCHED_FIFO,50,SCHED_RR},
{50,SCHED_FIFO,0,SCHED_OTHER},
{50,SCHED_RR,0,SCHED_OTHER},
{0,SCHED_OTHER,50,SCHED_FIFO},
{0,SCHED_OTHER,50,SCHED_RR},
{110,SCHED_OTHER,120,SCHED_OTHER},
};
if(print_flag == 1)
for(int i = 0;i<(sizeof(testClass)/sizeof(testClass[0]));i++)
{
printf("------------------------------------------------------\n");
printf("use_index :%d\n",i);
printf("main policy is %s, priority is %d\n",
getStrategy(testClass[i].main_sched_strategy),testClass[i].main_sched_priority);
printf("slave policy is %s, priority is %d\n",
getStrategy(testClass[i].slave_sched_strategy),testClass[i].slave_sched_priority);
}
if(index >=(sizeof(testClass)/sizeof(testClass[0])))
{
printf("pleasw input correct index\n");
return ;
}
memcpy(&g_testClass,&testClass[index],sizeof(T_TestClass));
return;
}
void *thread_routine(void *arg)
{
int my_policy;
struct sched_param my_param;
pthread_getschedparam(pthread_self(), &my_policy, &my_param);
TEST_PRINTF
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t thread_id,thread_id1;
pthread_attr_t thread_attr;
int thread_policy;
struct sched_param thread_param;
int status, rr_min_priority, rr_max_priority;
int use_index = 0xFFF;
use_index = atoi(argv[1]);
if(use_index >100)
{
init(use_index,1);
return 0;
}
else
{
init(use_index,0);
}
printf("use_index %d\n",use_index);
printf("*******************start********************************\n");
printf("main policy is %s, priority is %d\n",getStrategy(g_testClass.main_sched_strategy),g_testClass.main_sched_priority);
printf("slave policy is %s, priority is %d\n",getStrategy(g_testClass.slave_sched_strategy),g_testClass.slave_sched_priority);
struct sched_param param;
sched_getparam(0,¶m);
param.__sched_priority = g_testClass.main_sched_priority;
sched_setscheduler(0,g_testClass.main_sched_strategy,¶m);
pthread_attr_init(&thread_attr);
status = pthread_attr_setschedpolicy(&thread_attr,g_testClass.slave_sched_strategy);//改变策略
if(status != 0)
{
printf("Unable to set policy.\n");
}
else
{
thread_param.sched_priority = g_testClass.slave_sched_priority;
pthread_attr_setschedparam(&thread_attr, &thread_param);
//无论何时,当你控制一个线程的调度策略或优先级时,必须将inheritsched属性设置为PTHREAD_EXPLICIT_SCHED。
pthread_attr_setinheritsched(&thread_attr, PTHREAD_EXPLICIT_SCHED);
}
pthread_create(&thread_id, &thread_attr, thread_routine, NULL);
TEST_PRINTF
pthread_join(thread_id, NULL);
printf("Main exiting");
return 0;
}
2.2 源码编译
gcc pthread.c -o pthread -lpthread
3.单进程中,两个线程,执行顺序如何?
一共实现的如下的两种线程与优先级的对应,来进行说明线程执行顺序。
例如:第一个,主线程为SCHED_RR,优先级为50,从线程为SCHED_RR,优先级为50。
{50,SCHED_RR,50,SCHED_RR},
{50,SCHED_RR,60,SCHED_RR},
{50,SCHED_FIFO,50,SCHED_FIFO},
{50,SCHED_FIFO,60,SCHED_FIFO},
{40,SCHED_FIFO,70,SCHED_FIFO},
{70,SCHED_FIFO,40,SCHED_FIFO},
{50,SCHED_RR,50,SCHED_FIFO},
{50,SCHED_FIFO,50,SCHED_RR},
{50,SCHED_FIFO,0,SCHED_OTHER},
{50,SCHED_RR,0,SCHED_OTHER},
{0,SCHED_OTHER,50,SCHED_FIFO},
{0,SCHED_OTHER,50,SCHED_RR},
{110,SCHED_OTHER,120,SCHED_OTHER},
3.1 两个线程SCHED_RR50与SCHED_RR50
来看下对应的执行结果,可以看见两者的执行的交替的,与SCHED_RR调度策略是可以对应上的,SCHED_RR采用时间片轮转进行实现;
root@ubuntu:/linuxshare/CLanguage# ./pthread 0
use_index 0
*******************start********************************
main policy is RR, priority is 50
slave policy is RR, priority is 50
pthread.c thread_routine 0 time:Thu Aug 18 08:09:23 2022
pthread.c main 0 time:Thu Aug 18 08:09:23 2022
pthread.c thread_routine 1 time:Thu Aug 18 08:09:28 2022
pthread.c main 1 time:Thu Aug 18 08:09:28 2022
pthread.c thread_routine 2 time:Thu Aug 18 08:09:33 2022
pthread.c main 2 time:Thu Aug 18 08:09:33 2022
pthread.c thread_routine 3 time:Thu Aug 18 08:09:39 2022
pthread.c main 3 time:Thu Aug 18 08:09:39 2022
pthread.c thread_routine 4 time:Thu Aug 18 08:09:43 2022
pthread.c main 4 time:Thu Aug 18 08:09:44 2022
pthread.c thread_routine 5 time:Thu Aug 18 08:09:48 2022
pthread.c main 5 time:Thu Aug 18 08:09:49 2022
pthread.c thread_routine 6 time:Thu Aug 18 08:09:54 2022
pthread.c main 6 time:Thu Aug 18 08:09:54 2022
pthread.c thread_routine 7 time:Thu Aug 18 08:09:59 2022
pthread.c main 7 time:Thu Aug 18 08:09:59 2022
pthread.c thread_routine 8 time:Thu Aug 18 08:10:04 2022
pthread.c main 8 time:Thu Aug 18 08:10:04 2022
pthread.c thread_routine 9 time:Thu Aug 18 08:10:09 2022
pthread.c main 9 time:Thu Aug 18 08:10:09 2022
3.1.1通过proc文件系统查看调度
ps -elf|grep pthread
cat /proc/
p
i
d
/
t
a
s
k
/
pid/task/
pid/task/pid/sched
cat /proc/
p
i
d
/
t
a
s
k
/
pid/task/
pid/task/pid/sched
通过proc文件系统,可以清晰的看到线程的优先级,以及调度策略等信息。
root@ubuntu:/linuxshare/CLanguage# ps -elf|grep pthread
4 R root 2460 1867 89 9 - - 3680 - 05:39 pts/8 00:00:04 ./pthread 0
0 S root 2463 2436 0 80 0 - 3556 pipe_w 05:39 pts/9 00:00:00 grep --color=auto pthread
root@ubuntu:/linuxshare/CLanguage# cat /proc/2460/task/2460/sched
pthread (2460, #threads: 2)
-------------------------------------------------------------------
se.exec_start : 2492829.935578
se.vruntime : 0.000000
se.sum_exec_runtime : 10744.167436
se.nr_migrations : 0
nr_switches : 123
nr_voluntary_switches : 0
nr_involuntary_switches : 123
se.load.weight : 1048576
se.runnable_weight : 1048576
se.avg.load_sum : 47671
se.avg.runnable_load_sum : 47671
se.avg.util_sum : 24689200
se.avg.load_avg : 1024
se.avg.runnable_load_avg : 1024
se.avg.util_avg : 525
se.avg.last_update_time : 2470198567936
policy : 2
prio : 49
clock-delta : 23
mm->numa_scan_seq : 0
numa_pages_migrated : 0
numa_preferred_nid : -1
total_numa_faults : 0
current_node=0, numa_group_id=0
numa_faults node=0 task_private=0 task_shared=0 group_private=0 group_shared=0
root@ubuntu:/home/wang# cat /proc/2460/task/2461/sched
pthread (2461, #threads: 2)
-------------------------------------------------------------------
se.exec_start : 2499829.311472
se.vruntime : 0.000000
se.sum_exec_runtime : 14177.711736
se.nr_migrations : 0
nr_switches : 151
nr_voluntary_switches : 0
nr_involuntary_switches : 151
se.load.weight : 1048576
se.runnable_weight : 1048576
se.avg.load_sum : 0
se.avg.runnable_load_sum : 0
se.avg.util_sum : 0
se.avg.load_avg : 1024
se.avg.runnable_load_avg : 1024
se.avg.util_avg : 504
se.avg.last_update_time : 2470198688581
policy : 2
prio : 49
clock-delta : 19
mm->numa_scan_seq : 0
numa_pages_migrated : 0
numa_preferred_nid : -1
total_numa_faults : 0
current_node=0, numa_group_id=0
numa_faults node=0 task_private=0 task_shared=0 group_private=0 group_shared=0
3.2 两个线程SCHED_RR50与SCHED_RR60
对应RT策略,先找到高优先的线程,然后执行同优先级的所有线程,目前只有这两个,因此先执行完从线程,再执行主线程
use_index 1
*******************start********************************
main policy is RR, priority is 50
slave policy is RR, priority is 60
pthread.c thread_routine 0 time:Thu Aug 18 08:13:07 2022
pthread.c thread_routine 1 time:Thu Aug 18 08:13:09 2022
pthread.c thread_routine 2 time:Thu Aug 18 08:13:11 2022
pthread.c thread_routine 3 time:Thu Aug 18 08:13:14 2022
pthread.c thread_routine 4 time:Thu Aug 18 08:13:16 2022
pthread.c thread_routine 5 time:Thu Aug 18 08:13:18 2022
pthread.c thread_routine 6 time:Thu Aug 18 08:13:20 2022
pthread.c thread_routine 7 time:Thu Aug 18 08:13:22 2022
pthread.c thread_routine 8 time:Thu Aug 18 08:13:24 2022
pthread.c thread_routine 9 time:Thu Aug 18 08:13:26 2022
pthread.c main 0 time:Thu Aug 18 08:13:29 2022
pthread.c main 1 time:Thu Aug 18 08:13:31 2022
pthread.c main 2 time:Thu Aug 18 08:13:33 2022
pthread.c main 3 time:Thu Aug 18 08:13:35 2022
pthread.c main 4 time:Thu Aug 18 08:13:37 2022
pthread.c main 5 time:Thu Aug 18 08:13:39 2022
pthread.c main 6 time:Thu Aug 18 08:13:41 2022
pthread.c main 7 time:Thu Aug 18 08:13:44 2022
pthread.c main 8 time:Thu Aug 18 08:13:46 2022
pthread.c main 9 time:Thu Aug 18 08:13:48 2022
3.3 两个线程SCHED_FIFO50与SCHED_FIFO50
可以看见在FIFO下,先执行链表前面的,然后依次执行,采取的是一直执行直到主动放弃的时候才会执行下一个线程。
root@ubuntu:/linuxshare/CLanguage# ./pthread 2
use_index 2
*******************start********************************
main policy is FIFO, priority is 50
slave policy is FIFO, priority is 50
pthread.c main 0 time:Fri Aug 19 05:08:15 2022
pthread.c main 1 time:Fri Aug 19 05:08:18 2022
pthread.c main 2 time:Fri Aug 19 05:08:20 2022
pthread.c main 3 time:Fri Aug 19 05:08:22 2022
pthread.c main 4 time:Fri Aug 19 05:08:24 2022
pthread.c main 5 time:Fri Aug 19 05:08:26 2022
pthread.c main 6 time:Fri Aug 19 05:08:28 2022
pthread.c main 7 time:Fri Aug 19 05:08:30 2022
pthread.c main 8 time:Fri Aug 19 05:08:32 2022
pthread.c main 9 time:Fri Aug 19 05:08:34 2022
pthread.c thread_routine 0 time:Fri Aug 19 05:08:36 2022
pthread.c thread_routine 1 time:Fri Aug 19 05:08:39 2022
pthread.c thread_routine 2 time:Fri Aug 19 05:08:41 2022
pthread.c thread_routine 3 time:Fri Aug 19 05:08:43 2022
pthread.c thread_routine 4 time:Fri Aug 19 05:08:45 2022
pthread.c thread_routine 5 time:Fri Aug 19 05:08:47 2022
pthread.c thread_routine 6 time:Fri Aug 19 05:08:49 2022
pthread.c thread_routine 7 time:Fri Aug 19 05:08:51 2022
pthread.c thread_routine 8 time:Fri Aug 19 05:08:53 2022
pthread.c thread_routine 9 time:Fri Aug 19 05:08:55 2022
3.4 两个线程SCHED_FIFO50与SCHED_FIFO60
在RT调度器下,无论是RR或者是FIFO,在不同的优先级的情况下,一定是先执行高优先级的,然后再执行低优先级的;
use_index 3
*******************start********************************
main policy is FIFO, priority is 50
slave policy is FIFO, priority is 60
pthread.c thread_routine 0 time:Fri Aug 19 05:12:36 2022
pthread.c thread_routine 1 time:Fri Aug 19 05:12:39 2022
pthread.c thread_routine 2 time:Fri Aug 19 05:12:41 2022
pthread.c thread_routine 3 time:Fri Aug 19 05:12:43 2022
pthread.c thread_routine 4 time:Fri Aug 19 05:12:45 2022
pthread.c thread_routine 5 time:Fri Aug 19 05:12:48 2022
pthread.c thread_routine 6 time:Fri Aug 19 05:12:50 2022
pthread.c thread_routine 7 time:Fri Aug 19 05:12:52 2022
pthread.c thread_routine 8 time:Fri Aug 19 05:12:54 2022
pthread.c thread_routine 9 time:Fri Aug 19 05:12:56 2022
pthread.c main 0 time:Fri Aug 19 05:12:58 2022
pthread.c main 1 time:Fri Aug 19 05:13:01 2022
pthread.c main 2 time:Fri Aug 19 05:13:03 2022
pthread.c main 3 time:Fri Aug 19 05:13:05 2022
pthread.c main 4 time:Fri Aug 19 05:13:07 2022
pthread.c main 5 time:Fri Aug 19 05:13:09 2022
pthread.c main 6 time:Fri Aug 19 05:13:12 2022
pthread.c main 7 time:Fri Aug 19 05:13:14 2022
pthread.c main 8 time:Fri Aug 19 05:13:16 2022
pthread.c main 9 time:Fri Aug 19 05:13:18 2022
3.5 两个线程SCHED_RR50与SCHED_FIFO50
这一种情况其实是最容易误解的,RR采取时间片进行执行,FIFO采取一直执行的策略,当两种策略同时处于同一优先级的情况下,还是先执行链表的第一个,即RR100ms,然后再执行FIFO直到执行完成,然后再回来执行RR剩余的部分。所以前面有设置延时的时长大于100ms。
3.5.1 延时大于100ms
可以看见此种情况从打印是看不到第一个阶段,即RR先执行100ms,其实也是执行了的,我们通过调整延时来达到此目的。
use_index 6
*******************start********************************
main policy is RR, priority is 50
slave policy is FIFO, priority is 50
pthread.c thread_routine 0 time:Fri Aug 19 05:16:45 2022
pthread.c thread_routine 1 time:Fri Aug 19 05:16:48 2022
pthread.c thread_routine 2 time:Fri Aug 19 05:16:51 2022
pthread.c thread_routine 3 time:Fri Aug 19 05:16:53 2022
pthread.c thread_routine 4 time:Fri Aug 19 05:16:55 2022
pthread.c thread_routine 5 time:Fri Aug 19 05:16:57 2022
pthread.c thread_routine 6 time:Fri Aug 19 05:16:59 2022
pthread.c thread_routine 7 time:Fri Aug 19 05:17:01 2022
pthread.c thread_routine 8 time:Fri Aug 19 05:17:03 2022
pthread.c thread_routine 9 time:Fri Aug 19 05:17:05 2022
pthread.c main 0 time:Fri Aug 19 05:17:08 2022
pthread.c main 1 time:Fri Aug 19 05:17:10 2022
pthread.c main 2 time:Fri Aug 19 05:17:12 2022
pthread.c main 3 time:Fri Aug 19 05:17:14 2022
pthread.c main 4 time:Fri Aug 19 05:17:16 2022
pthread.c main 5 time:Fri Aug 19 05:17:18 2022
pthread.c main 6 time:Fri Aug 19 05:17:20 2022
pthread.c main 7 time:Fri Aug 19 05:17:22 2022
pthread.c main 8 time:Fri Aug 19 05:17:24 2022
pthread.c main 9 time:Fri Aug 19 05:17:26 2022
3.5.2 延时小于100ms
可以看将,将把延时时间设置为小于100ms时,我们便可以从打印中明确到看到其调度的过程:
1.先执行SCHED_RR50,时间长度为100ms;
2.然后执行SCHED_FIFO50,一直到其执行完成;
3,然后再执行SCHED_RR50剩余的部分。
root@ubuntu:/linuxshare/CLanguage# ./pthread 6
use_index 6
*******************start********************************
main policy is RR, priority is 50
slave policy is FIFO, priority is 50
pthread.c main 0 time:Fri Aug 19 05:24:52 2022
pthread.c main 1 time:Fri Aug 19 05:24:52 2022
pthread.c thread_routine 0 time:Fri Aug 19 05:24:52 2022
pthread.c thread_routine 1 time:Fri Aug 19 05:24:53 2022
pthread.c thread_routine 2 time:Fri Aug 19 05:24:53 2022
pthread.c thread_routine 3 time:Fri Aug 19 05:24:53 2022
pthread.c thread_routine 4 time:Fri Aug 19 05:24:53 2022
pthread.c thread_routine 5 time:Fri Aug 19 05:24:53 2022
pthread.c thread_routine 6 time:Fri Aug 19 05:24:53 2022
pthread.c thread_routine 7 time:Fri Aug 19 05:24:53 2022
pthread.c thread_routine 8 time:Fri Aug 19 05:24:53 2022
pthread.c thread_routine 9 time:Fri Aug 19 05:24:53 2022
pthread.c main 2 time:Fri Aug 19 05:24:53 2022
pthread.c main 3 time:Fri Aug 19 05:24:53 2022
pthread.c main 4 time:Fri Aug 19 05:24:53 2022
pthread.c main 5 time:Fri Aug 19 05:24:53 2022
pthread.c main 6 time:Fri Aug 19 05:24:53 2022
pthread.c main 7 time:Fri Aug 19 05:24:53 2022
pthread.c main 8 time:Fri Aug 19 05:24:53 2022
pthread.c main 9 time:Fri Aug 19 05:24:53 2022
4 总结分析
通过以上的演示,可以进一步说明RT调度器的调度情况,其通过哈希表进行组织:
1.在优先级不同的情况下,永远先执行高优先级的,然后执行低优先级的;内核优先级=99 - 用户态设置的优先级。
2.遇到同优先级的,RR是通过时间片进行轮转执行,而FIFO策略则是一直占用调度器,直到主动退出执行。