程序中用sleep和select阻塞休眠的区别

在看公司项目中发现超时控制中使用select替代sleep就行阻塞,循环检查任务是否超时,在网上找了很多资料说了select的各种好处:

1:sleep不准确,只能精确到秒(这个我感觉可以使用usleep代替,不是个很好理由)。

2:sleep容易受到系统信号,例如SIGALRM影响,各个系统版本实现不一,sleep是个glic库函数,不是系统调用。

3:更高级的说话是,sleep浪费CPU,使程序阻塞。

我的分析思路:

程序调用sleep就是放弃cpu执行,进入阻塞状态,但是sleep的秒数过后,有可能有很多进程或线程在竞争cpu,而且优先级都比当前程序高,导致当先进程或线程无法立即执行,这是导致sleep不准确的一个原因。


gnu里介绍sleep:

The function sleep gives a simple way to make the program wait for a short interval. If your program doesn’t use signals (except to terminate), then you can expect sleep to wait reliably throughout the specified interval. Otherwise, sleep can return sooner if a signal arrives; if you want to wait for a given interval regardless of signals, use select (see Waiting for I/O) and don’t specify any descriptors to wait for.

Resist the temptation to implement a sleep for a fixed amount of time by using the return value of sleep, when nonzero, to call sleep again. This will work with a certain amount of accuracy as long as signals arrive infrequently. But each signal can cause the eventual wakeup time to be off by an additional second or so. Suppose a few signals happen to arrive in rapid succession by bad luck—there is no limit on how much this could shorten or lengthen the wait.

Instead, compute the calendar time at which the program should stop waiting, and keep trying to wait until that calendar time. This won’t be off by more than a second. With just a little more work, you can use select and make the waiting period quite accurate. (Of course, heavy system load can cause additional unavoidable delays—unless the machine is dedicated to one application, there is no way you can avoid this.)

On some systems, sleep can do strange things if your program uses SIGALRM explicitly. Even if SIGALRM signals are being ignored or blocked when sleep is called, sleep might return prematurely on delivery of a SIGALRM signal. If you have established a handler for SIGALRM signals and a SIGALRM signal is delivered while the process is sleeping, the action taken might be just to cause sleep to return instead of invoking your handler. And, if sleep is interrupted by delivery of a signal whose handler requests an alarm or alters the handling of SIGALRM, this handler and sleep will interfere.

On GNU systems, it is safe to use sleep and SIGALRM in the same program, because sleep does not work by means of SIGALRM.

有的系统使用alarm,pause或sigsuspend来实现sleep,这样使sleep受到SIGALRM信号影响。各个系统的sleep版本不一。

ldd --version能看到libc版本,gnu的sleep已经不依靠SIGALRM实现,所以SIGALRM对sleep没有影响。

unsigned int 
__sleep (unsigned int seconds)
{
  /* This is not necessary but some buggy programs depend on it.  */
  if (__builtin_expect (seconds == 0, 0)) 
    {   
#ifdef CANCELLATION_P
      CANCELLATION_P (THREAD_SELF);
#endif
      return 0;
    }   

  int save_errno = errno;

  const unsigned int max 
    = (unsigned int) (((unsigned long int) (~((time_t) 0))) >> 1); 
  struct timespec ts = { 0, 0 };
  do  
    {   
      if (sizeof (ts.tv_sec) <= sizeof (seconds))
        {   
          /* Since SECONDS is unsigned assigning the value to .tv_sec can
             overflow it.  In this case we have to wait in steps.  */
          ts.tv_sec += MIN (seconds, max);
          seconds -= (unsigned int) ts.tv_sec;
        }   
      else
        {   
          ts.tv_sec = (time_t) seconds;
          seconds = 0;
        }   

      if (__nanosleep (&ts, &ts) < 0)
        /* We were interrupted.
           Return the number of (whole) seconds we have not yet slept.  */
        return seconds + ts.tv_sec;
    }   
  while (seconds > 0); 

  __set_errno (save_errno);

  return 0;
}
weak_alias (__sleep, sleep)

gnu里sleep的实现,是依赖__nanosleep来完成的。nanosleep是linux的系统调用,它是使用定时器来实现的,该调用使调用进程睡眠,并往定时器队上加入一个timer_list型定时器,timer_list结构里包括唤醒时间以及唤醒后执行的函数,通过nanosleep()加入的定时器的执行函数仅仅完成唤醒当前进程的功能。系统通过一定机制定时检查这些队列,如果定时时间已超过,则执行定时器指定的函数唤醒调用进程。当然,由于系统时间片可能丢失,所以nanosleep精度也不是很高。

#include  
#include  
#include  
#include  

void ouch(int sig) 
{ 
    printf("OUCH! - I got signal %d \n", sig); 
} 

int main() 
{ 
        struct sigaction act; 
        struct timespec ts; 
        int ret; 
        act.sa_handler = ouch; 
        sigemptyset(&act.sa_mask); 
        act.sa_flags = 0;  
        act.sa_flags |= SA_RESTART; 
        sigaction(SIGALRM, &act, 0); 
        //signal(SIGALRM, SIG_IGN); 
        while (1) 
        {   

                alarm(5);
                printf("hello world\n"); 
                ts.tv_sec = 1;
                ts.tv_nsec = 1;  
                ret = nanosleep(&ts,NULL); 
                if (ret < 0)  
                {   
                        if (errno == EINTR) 
                                printf("nanosleep interrupted \n"); 
                        else 
                                printf("nanosleep errno %s \n", strerror(errno)) ; 
                }else 
                        printf("sleep over \n"); 
        }   
            
        printf("main end "); 
        return 0;  
}

如果没有指定信号处理方法,默认下程序会停掉

[@sjs_54_217 sleep_test]# ./a.out 
hello world
sleep over 
hello world
sleep over 
hello world
sleep over 
hello world
sleep over 
hello world
Alarm clock

  //sigaction(SIGALRM, &act, 0); 
  //signal(SIGALRM, SIG_IGN); 
  alarm(5);
  while (1) 
  {   

          //alarm(5); 放到里面每次都会重新定时,不会起到定时作用
          printf("hello world\n"); 
          ts.tv_sec = 1;
          ts.tv_nsec = 1;  
          ret = nanosleep(&ts,NULL); 
          if (ret < 0)  
          {   
                if (errno == EINTR) 
                          printf("nanosleep interrupted \n"); 
                else 
                          printf("nanosleep errno %s \n", strerror(errno)) ; 
          }else 
                          printf("sleep over \n"); 
  }   


你可能感兴趣的:(C++,linux内核,Linux内核)