Linux的进程管理之单进程多线程实例说明—7

  • Post author:
  • Post category:linux




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,&param);
	param.__sched_priority = g_testClass.main_sched_priority;
	sched_setscheduler(0,g_testClass.main_sched_strategy,&param);
	
    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/






p


i


d


/


t


a


s


k


/





pid/sched

cat /proc/



p

i

d

/

t

a

s

k

/

pid/task/






p


i


d


/


t


a


s


k


/





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策略,先找到高优先的线程,然后执行同优先级的所有线程,目前只有这两个,因此先执行完从线程,再执行主线程i

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策略则是一直占用调度器,直到主动退出执行。

在这里插入图片描述



版权声明:本文为weixin_43708235原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。