Linux多线程编程中的调度策略编程

多线程编程

我们在进行多线程编程的时间,通常先会对问题领域进行任务的拆解,深入一点的多线程编程,会涉及到任务优先级的考虑;如果再深一点,一般可能就是多核编程:Cache热度、绑核、隔离CPU等。

但多核编程与具体硬件绑定较为紧密,通用性比较差,不到万不得已的时候,多核编程实为下策,应避免用之!

在此,要介绍的是在多线程编程中调度策略编程。相较于多核编程,可能更贴合普通的使用场景!

由来

对于某一些任务类型,例如,负载均衡器LB,这类任务有相当的密集CPU使用需求,但又不是100%的CPU消耗。在普通Linux CFS公平调度策略下,任务在时间片到时需要被换下马重新调度,在需要执行时,又因为历史上CPU使用时间的“被动”,经过动态计算优先级,又不一定能够马上被调度起来,可能会造成某些程度的丢包!

但在Linux系统分为实时系统和非实时系统,以前因为认知上的偏差,对于Linux CFS公平调度策略小小有点不满,为什么不可以实现线程调度策略定制的自由呢?

确实,Linux是自由的!近来,通过查看man 7 sched,让自己满足地惊艳了一把,原来Linux早就实现了这种定制需求!

多核编程不见得效果更好,建议使用通用性更好的实时调度策略编程

SCHED_RR实时调度策略

在Linux中调度策略分为实时调度策略和非实时策略。

深入分析了下SCHED_RR策略,此种调度策略已经可以比较好地满足实时性要求高和CPU使用密集的负载平衡器的多线程任务场景。

直白一点说就是:对于CPU较为密集使用的场景,想在自己退让CPU使用时,才脱离CPU执行;而当想使用CPU时,又可以获得较为实时的调度,上马执行

SCHED_RR实时调度策略,在调度等级上高于普通调度策略;同时实时任务优先级为静态优先级,不进行动态调整

通过控制内核最长使用CPU时间片时间/proc/sys/kernel/sched_rr_timeslice_ms,可以以控制任务在需要退让CPU使用时,才脱离CPU使用

通过查看/proc/{pid}/statusvoluntary_ctxt_switches、nonvoluntary_ctxt_switches两个统计项,可以观察任务主动让出CPU和被动让出CPU的比值,以量化策略控制的效果

结束语

在适合的多线程场景,建议多线程编程定制合适的调度策略

附录例子

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define __USE_GNU
#include 
#include 


static int g_exit;
static void term_handler(int s)
{
    g_exit = 1;
}

/*
 * cat /proc/sys/kernel/sched_rt_runtime_us
 * cat /proc/sys/kernel/sched_rr_timeslice_ms
 * cat /sys/fs/cgroup/cpu/user.slice/cpu.rt_runtime_us
 * echo $((10*1000)) > /sys/fs/cgroup/cpu/user.slice/cpu.rt_runtime_us
*/
int main(int argc , char* argv[])
{
  char szErrorDesc[256];
  
  struct sigaction act, oldact;
  act.sa_handler  = term_handler;
  sigemptyset(&act.sa_mask);
  act.sa_flags = 0;
  
  sigaction(SIGINT, &act, &oldact);
  sigaction(SIGTERM, &act, &oldact);
  //signal(SIGINT, SIG_IGN);
  
  pid_t pid = getpid();
  printf("pid: %d, policy: %d\n", pid, sched_getscheduler(pid));
  int policy = -1;
  
  int opt;
  
  while((opt = getopt(argc, argv, "rb")) != -1) {
     switch(opt) {
         case 'r':
         policy = SCHED_RR;
         break;
         case 'b':
         policy = SCHED_BATCH;
         break;
         default: /* '?' */
         fprintf(stderr, "Usage: %s [-r --SCHED_RR] [-b --SCHED_BATCH] \n", argv[0]);
         return 1;
     }
  }
  
  if(policy != -1) {
     struct sched_param tParam = {};
     
     if(policy == SCHED_RR)
        tParam.sched_priority = 1;
  
    int rc = sched_setscheduler(pid, policy, &tParam);
  
    if(rc < 0)
    {
      strerror_r(errno, szErrorDesc, sizeof(szErrorDesc));
      fprintf(stderr, "sched_setscheduler fail, errno: %d, %s", errno, szErrorDesc);
    }
    else
    {
      printf("after set thread policy: %d\n", sched_getscheduler(pid));
    }   
  }
  
  char szCmd[64];
  snprintf(szCmd, sizeof(szCmd), "cat /proc/%d/status", pid);
  printf("can see %s to see the context switch\n", szCmd);
  
  uint64_t count = 0;
  
  while(g_exit == 0) {
      ++count;
  }
  
  printf("cat %s to see the context switch result\n", szCmd);
  system(szCmd);
  
  return 0;
}

你可能感兴趣的:(c&c++技术,linux,实时调度策略,SCHED_RR,多线程编程)