Pthread优先级和调度

时间:2015-09-29 08:13:15

标签: c pthreads scheduling thread-priority

我正在尝试使用Pthreads来设置优先级和调度策略。

我修改了一个简单的套接字(可在线下载),带有服务器和多个客户端。这些客户端只是将一个字符串发送到服务器。

服务器正在为每个连接的客户端创建一个新线程,并为每个新连接设置更高的优先级,这意味着最后连接的客户端是具有最高优先级的客户端。我期待的结果是具有最高优先级的线程是在该客户端连接之前打印出来的线程,而其他线程应该等待。

然而,这不是我得到的结果。无论如何,线程都被执行,即使是优先级较低的线程。我甚至尝试了几种配置,例如使用pthread_join,但在这种情况下,调度程序将等到该线程完成其执行,如果调用另一个具有更高优先级的线程(在我的示例中是另一个客户端连接),这是错误的,因为它应该将CPU释放到这个新创建的线程。

以下是代码:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <pthread.h>
#include <sched.h>
#include <sys/time.h>

#define PORTNUMBER 8888

int prioInit = 30; //global variable to initialize priority

struct serverParm 
{
       int connectionDesc;
};

//function used to increase priority to new threads
void addPrio(int *prio)
{
  *prio = *prio + 10;
}

void *serverThread(void *parmPtr) 
{
  #define PARMPTR ((struct serverParm *) parmPtr)
  int recievedMsgLen;
  char messageBuf[1025];

  struct sched_param Priority_Param; //struct to set priority
  int policy=SCHED_FIFO; //kind of policy desired, either SCHED_FIFO or SCHED_RR, otherwise Linux uses SCHED_OTHER

  // Server thread code to deal with message processing
  printf("DEBUG: connection made, connectionDesc=%d\n",
        PARMPTR->connectionDesc);

  pthread_t self_id= pthread_self(); // I ask for the tid..
  Priority_Param.sched_priority = prioInit; //.. set the priority (the higher the sooner it is executed, min at 1, max at 99)..
  addPrio(&prioInit); //..increase the base priority..
  if(pthread_setschedparam(self_id, policy, &Priority_Param) != 0) //.. and set the scheduling options.
  {
    printf("Error Set Sched\n");
    perror("");
    exit(1);
  }

  if (PARMPTR->connectionDesc < 0) 
  {
    printf("Accept failed\n");
    exit(1);   
  }

  // Receive messages from sender
  while ((recievedMsgLen=
        read(PARMPTR->connectionDesc,messageBuf,sizeof(messageBuf)-1)) > 0) 
  {
    recievedMsgLen[messageBuf] = '\0';
    printf("Message: %s\n",messageBuf);
  }
  close(PARMPTR->connectionDesc);  // Avoid descriptor leaks 
  free(PARMPTR);                   // And memory leaks                        
}

int main () 
{
  int listenDesc;
  struct sockaddr_in myAddr;
  struct serverParm *parmPtr;
  int connectionDesc;
  pthread_t threadID;

  // Create socket from which to read 
  if ((listenDesc = socket(AF_INET, SOCK_STREAM, 0)) < 0) 
  {
    perror("open error on socket");
    exit(1);
  }

  // Create "name" of socket
  myAddr.sin_family = AF_INET;
  myAddr.sin_addr.s_addr = INADDR_ANY;
  myAddr.sin_port = htons(PORTNUMBER);

  if (bind(listenDesc, (struct sockaddr *) &myAddr, sizeof(myAddr)) < 0) 
  {
    perror("bind error");
    exit(1);
  }

  // Start accepting connections
  listen(listenDesc,5);

  while (1)
  {
    // Wait for a client connection
    connectionDesc = accept(listenDesc, NULL, NULL);

    // Create a thread to actually handle this client
    parmPtr = (struct serverParm *)malloc(sizeof(struct serverParm));
    parmPtr->connectionDesc = connectionDesc;
    if (pthread_create(&threadID, NULL, serverThread, (void *)parmPtr) 
          != 0) 
    {
      perror("Thread create error");
      close(connectionDesc);
      close(listenDesc);
      exit(1);
    }

    printf("Parent ready for another connection\n");
  }

}

我正在使用-pthread选项进行编译并以root身份运行,以便设置和更改策略和优先级。我肯定在这里遗漏了一些东西,但我也想知道是否可以真正使用和更改调度选项,以便有类似于实时的行为。

2 个答案:

答案 0 :(得分:1)

调度程序策略和优先级用于确定在必须在两个或多个可运行线程之间进行选择时运行的线程。

如果您的高优先级线程在read()中阻塞,则它不符合运行条件,并且可运行的优先级较低的线程将有机会运行。优先级意味着即使所有可用的CPU核心在数据从网络到达优先级较高的线程时都运行较低优先级的线程,较低优先级的线程将立即被抢占,以便较高优先级的线程可以运行

如果要查看优先级的影响,请让优先级较低的线程执行大量工作,以便它几乎总是使用CPU:优先级较高的线程仍应立即响应网络输入。

(但是如果你这样做,要么对低优先级线程使用非实时优先级,要么确保你打开一个以提升的实时优先级运行的shell,这样优先级较低的线程就不会在需要时阻止shell运行。

旁注:您的addPrio()功能很有效 - 它应该围绕对*prio的更改锁定互斥锁。

答案 1 :(得分:1)

关于评估Linux实际行为的最终目标与premp-rt补丁相比,答案并不困难。

当然它的行为类似,只是延迟时间越来越大,但仍然有限。

如果你的实时响应要求不那么具有挑战性,你可以为股票Linux编写实时完美的软件。如果这些要求要在几毫秒内响应,那么您将需要premp-rt补丁。如果你的要求比那更严格,那么你需要一个不同的操作系统(VxWorks等)。

有一些非常好的建议here,特别是here。 Linux preempt-rt和实时并不完美,特别是在英特尔硬件上,但如果你的要求不那么紧,那么你可以做得很好。

我强烈建议您自行下载并应用preempt-rt。这样你就可以将FTRACE编译到内核中,然后就可以使用kernelshark了。您也可以将FTRACE放入库存内核。在开发多线程应用程序时,Kernelshark非常有用,尤其是当您打开task plots时。