pthread调度策略,优先级和竞争范围

时间:2023-11-25 14:52:56

  实时调度:操作系统在有限的时间内提供特定水平的服务能力。受限制的响应时间不一定是块的反应,意味着可预知的响应速度。如果系统定义_POSIX_THRAED_PRIORITY_SCHEDULING,它为线程指派实时调度优先级提供支持。支持_POSIX_THRAED_PRIORITY_SCHEDULING的系统必须提供至少包括成员seched_priority的struct  sched_param结构体的定义,seched_priority的标准策略是SCHED_FIFO和SCHED_RR使用的唯一参数。

  影响实时调度:调度策略,参数,竞争范围和分配域

  实时调度在某种程度上不一定很快,实时调度可能会变得很慢,因为它包含了更多抢占检查的开销特别是在一台多处理机上

  固定优先级调度可能会导致优先级倒置,优先级倒置——低优先级线程阻塞高优先级线程运行,他是调度和同步之间一个不干净的相互作用的结果。调用要求一个线程运行,但是同步要求运用另行的线程,所以两个优先级好像颠倒。如:一个低优先级线程获互斥资源,并且被一个随后在同样资源阻塞的高优先级线程抢占时,优先级发生倒置,在只有两个线程被允许运行时,低优先级线程被先执行,然后释放互斥量,如果在他们之间有第三个线程准备好时,他能阻塞低优先级线程运行,因为低优先级线程拥有高优先级线程拥有的互斥量,中间优先级线程阻止了高优先级线程的执行

  优先级调度不完全可移植。如:使用系统竞争范围时,你的线程可以直接与操作系统内核线程相竞争,提高自定义线程的优先级可能会阻止内核I/O驱动在系统上的一些工作

  确实需要使用优先级调度时(一般避免使用,因为相比解决问题,他会引起更多的问题),应记住:

  1. 进程竞争范围比系统竞争范围“更好”,因为你不会阻止其他进程或内核中的某个线程运行
  2. SCHED_RR比SCHED_FIFO“更好”,并且具有可移植性,因为SCHED_RR线程将在具有相同优先级线程共享可用处理器时间间隔中被抢占
  3. 对SCHED_FIFO和SCHED_RR而言,低优先级比高优先级更好,因为能更少的妨碍另外重要的东西
//1.设置的最高和最低优先级,policy: 可以取三个值(SCHED_FIFO、SCHED_RR、SCHED_OTHER)
int sched_get_priority_max(int policy);
int sched_get_priority_min(int policy); //2.设置和获取优先级
//param是struct sched_param类型的指针,它仅仅包含一个成员变sched_priority,指明所要设置的静态线程优先级。
int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param);
int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param);
param.sched_priority = ; //设置优先级
struct sched_param
{
int __sched_priority; // 所要设定的线程优先级
}; //3.改变策略(静态改变策略和设置优先级)
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
int pthread_attr_getschedpolicy(pthread_attr_t *attr, int policy); //4.继承调度属性,该属性控制了你创建的线程从创建线程那继承调度信息,schedpolicy和schedparam显示设置调度信息
//我手动设置了调度策略或优先级时,必须显示的设置线程调度策略的inheritsched属性,因为pthread没有为inheritsched设置默认值
//所以在改变了调度策略或优先级时必须总是设置该属性。
int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched);
int pthread_attr_getinheritsched(pthread_attr_t *attr, int *inheritsched);
/*
第一个函数中inheritsched的取值为:PTHREAD_INHERIT_SCHED 或者 PTHREAD_EXPLICIT_SCHED。
前者为继承创建线程的调度策略和优先级,后者指定不继承调度策略和优先级,而是使用自己设置的调度策略和优先级。
无论何时,当你需要控制一个线程的调度策略或优先级时,必须将inheritsched属性设置为PTHREAD_EXPLICIT_SCHED。
*/ //5.置正在运行的线程的调度策略和优先级(动态设置线程的调度策略和优先级)
//前面的那些函数只能通过线程的属性对象 pthread_attr_t 来设置线程的调度策略和优先级,不能够直接设置正在运行的线程的调度策略和优先级
int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param);
int pthread_getschedparam(pthread_t thread, int *policy, struct sched_param *param);
//在成功完成之后返回零。其他任何返回值都表示出现了错误。如果出现以下任一情况,pthread_setschedparam() 函数将失败并返回
//相应的值--EINVAL所设置属性的值无效。ENOTSUP--尝试将该属性设置为不受支持的值。
//int pthread_setschedparam:thread参数所指向的线程不存在
//int pthread_getschedparam:1.参数policy或同参数policy关联的调度参数之一无效;
//2.数policy或调度参数之一的值不被支持;
//3.调用线程没有适当的权限来设置指定线程的调度参数或策略
//4.参数thread指向的线程不存在;5.实现不允许应用程序将参数改动为特定的值
  1. SCHED_OTHER(是Linux默认的分时调度策略):它是默认的线程分时调度策略,所有的线程的优先级别都是0(不使用sched_param结构体的sched_priority成员),如果系统使用这种调度策略,程序将无法设置线程的优先级。这种调度策略也是抢占式的,当高优先级的线程准备运行的时候,当前线程将被抢占并进入等待队列。这种调度策略仅仅决定线程在可运行线程队列中的具有相同优先级的线程的运行次序。(使用此方式的代码不可移植)
  2. SCHED_FIFO:它是一种实时的先进先出调用策略,且只能在超级用户下运行。这种调用策略仅仅被使用于优先级大于0的线程。使用SCHED_FIFO的线程运行到有更高级的线程准备好或者愿意自己阻塞为止;当有一个线程准备好时,除非有平等或更高级的线程准备好,否则他将很快运行自己。如果有若干相同优先级的线程等待执行,然而最早执行的线程无终止或者阻塞动作,那么其他线程是无法执行的,除非当前线程调用如pthread_yield之类的函数,所以在使用SCHED_FIFO的时候要小心处理相同级别线程的动作。
  3. SCHED_RR:若有一个此类的线程运行超过一定的时间没有阻塞,而另外的SCHED_RR或SCHED_FIFO策略相同优先级的线程准备好时,运行的线程将被枪占意识准备好的线程运行。
  4. 对于 SCHED_OTHER 策略,sched_priority 只能为 0。对于 SCHED_FIFO,SCHED_RR 策略,sched_priority 从 1 到 99。
  5. 调度策略和优先级是分开来描述的。前者使用预定义的SCHED_RR、SCHED_FIFO、SCHED_OTHER,后者是通过结果体struct sched_param给出的。
  6. 这些设置调度策略和优先级的函数操作的对象是线程的属性pthread_attr_t,而不是直接来操作线程的调度策略和优先级的。函数的第一个参数都是pthread_attr_t。
  7. 当pthread_setschedparam函数的参数 policy == SCHED_RR 或者 SCHED_FIFO 时,程序必须要在超级用户下运行
  8. pthread_setschedparam 函数改变在运行线程的调度策略和优先级肯定就不用调用函数来设置inheritsched属性了:pthread_attr_setinheritsched(&thread_attr, PTHREAD_EXPLICIT_SCHED); 因为该函数设置的对象是pthread_attr_t 
  9. 当在对象属性中设置调度策略或优先级时,必须同时设置inheritsched属性
  10. 改变线程属性中调度策略和参数时是两个操作,修改调度策略和修改参数
  11. 不能独立于线程的参数来修改一个可执行线程的调度策略,为了调度正确操作,参略和参数一定是一致的,每个调度策略有一个优先级的唯一范围,并且一个线程不能对一个对当前调度策略而言无效的优先级执行。

竞争范围和分配域

int pthread_attr_getscope(pthread_attr_t *attr, int *scope);
/*
返回值:若是成功返回0,否则返回错误的编号
形 参:
attr 指向一个线程属性的指针
scope 返回线程的作用域
*/
//指定了线程与谁竞争资源
int pthread_attr_setscope(pthread_attr_t *attr, int scope);
/*
返回值:若是成功返回0,否则返回错误的编号
attr 指向一个线程属性的指针
guardsize 线程的作用域,可以取如下值
PTHREAD_SCOPE_SYSTEM 与系统中所有进程中线程竞争
PTHREAD_SCOPE_PROCESS 与当前进程中的其他线程竞争
*/

  竞争范围:描述了线程为处理资源而竞争的方式,系统竞争范围意味着线程与进程之外的线程竞争处理器资源,一个进程内的高优先级系统竞争范围线程能阻止其他进程内的系统竞争范围运行。竞争范围指的是仅仅在同一进程内相互竞争。

  分配域:系统内线程可以为其他竞争的处理器的集合。一个系统可以有一个以上的分配域,每个包含一个以上的处理器,在一个单处理器的处理机上,各个分配域可以包含从一个处理器到系统中所有的处理器。(没有实现该接口)

  1. 竞争范围内的线程可以共享一个核实体
  2. 系统竞争范围线程之间的环境切换通常要求至少一次内核调用
  3. 竞争范围在优先级调度上没有给你真正的控制——高优先级的线程可以优先于进程内的其他线程运行
  4. 当一个线程被分配超过一个处理机的分配域时,应用程序不能完全依靠可预知的调度行为;如:高优先级和低优先级可以同时运行,调度程序不允许因为一个高优先级的线程正在运行而是处理及闲置,单处理机行为在一台多处理机上没什么意思。

核实体

  线程:调用pthread_create创建的一个线程,类行为pthread_t的一个标识符代表,使用pthreads接口可控制

  处理机:物理硬件

  核实体:线程和处理机间的一层附加抽象,可能是一个传统的UNIX进程。

  线程与核实体的交互方式:多对一(用户级),一对一(内核级),多对少