Precise timing and hibernation under Linux

  

The sleep function provided in Linux is sleep(), but only provides hibernation in seconds. This hibernation is obviously too long for some processes, so how can we make the process more Small time resolution sleep?

There are two ways I know, and I will introduce them separately below.

The first method is to use a timer. The timer function provided by Linux is:

int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue);which specifies that Kind of timer. Linux provides three kinds of timers:

TIMER_REAL: Accurate timer, timeout will issue SIGALRM signal; TIMER_VIRTUAL: virtual timer, only process time, so it will change according to process execution time, can not achieve accurate timing, Timeout sends SIGVTALRM signal; TIMER_PROF: Synopsis timer, it will change according to process time and system time, can not achieve accurate timing, timeout will issue SIGPROF signal;

In the process should capture the set timer will be issued The signal, because the process receives a signal from the timer timeout, the default action is terminated.

value is to set the timer time, the relevant structure is as follows:

struct itimerval { struct timeval it_interval; struct timeval it_value;};struct timeval { long tv_sec; long tv_usec;};it_interval specified interval Time, it_value specifies the initial timing time. If only it_value is specified, it is implemented once; if it_interval is specified at the same time, after the timeout, the system will re-initialize it_value to it_interval to implement repeat timing; if both are cleared, the timer will be cleared.

tv_sec provides second-level precision, tv_usec provides microsecond precision, with a value of first, pay attention to 1s = 1000000us.

ovalue is used to save the previous value, standing as NULL.

If you sleep with a timer provided by setitimer(), simply pause the wait timer signal with pause(). The second method is to use select() to provide precise timing and hibernation:

int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);n refers to monitoring The file descriptor range, usually set to fd+1, readfds, writefds, and exceptfds for select() are read, write, and exception file descriptor sets, and timeout is the timeout.

The macros that may be used for file descriptor set operations are:

FD_CLR(int fd, fd_set *set); Clear fdFD_ISSET(int fd, fd_set *set); Test fd Set FD_SET(int fd, fd_set *set); Set fdFD_ZERO(fd_set *set); Clear the descriptor set We don't need these macros at this time, because we don't care about the state of the file descriptor, we care about select() time out. So we need to set readfds, writefds and exceptfds to NULL, just specify the timeout time. As for n we can not care, so you can set it to any non-negative value. The implementation code is as follows:

int usSleep(long us) { struct timeval tv; tv.tv_sec = 0; tv.tv_usec = us; return select(0, NULL, NULL, NULL, &tv);} Oh, how, is it very simple?

Conclusion: setitimer() and select() can achieve precise sleep of the process. This article gives a brief introduction to them and gives a simple implementation based on select(). I don't recommend using setitimer because one of the Linux systems provides a limited number of timers (each process can have at most 3 different types of timers), and setitimer() is not simple to select().

Edited on September 12, 2008 ------------------------------------ -------------------------------

Thank you for the feedback of 33 and ffff, ms and us errors Already corrected. Because the younger brother is not good at learning, I will forgive the misunderstanding caused by everyone. I also welcome your comments and we will make progress together. I have changed the title, the original title is not suitable for the current content, because I want to add other sleep functions. First, as ffff puts it, usleep() can be used to achieve sleep below the second of the process.

int usleep(unsigned long usec); Like sleep(), it returns when the process receives any signal. However, the Linux manual says that it does not return the number of seconds without sleep like sleep(), but instead returns -1 and sets errno to EINTR. The implementation of uClibc is as follows:

int usleep (__useconds_t usec){ const struct timespec ts = { .tv_sec = (long int) (usec /1000000), .tv_nsec = (long int) (usec % 1000000) * 1000ul }; return(nanosleep(&ts, NULL));} Visible usleep() is internally implemented by the nanosleep() function. Next we will discuss the nanosleep() function. I have also seen the following implementation with select(), which also supports the method I mentioned earlier.

int usleep( unsigned long usec ){ static struct { /* `timeval' */long tv_sec; /* seconds */long tv_usec; ​​/* microsecs */} delay; /* _select() timeout * /delay.tv_sec = usec /1000000L; delay.tv_usec = usec % 1000000L; return select(0, (long *)0, (long *)0, (long *)0, &delay);} Next we come again Take a look at nanosleep(). Nanosleep() can achieve nanosecond (1/1000 000 000) level of sleep, so it is more accurate than usleep(). But your CPU needs at least 1GHz to support such high resolution. The prototype of nanosleep() is as follows:

int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); the first parameter specifies the time to sleep, and the second parameter returns no sleep when the signal is interrupted. Enough time, the timespec structure is as follows:

struct timespec { time_t tv_sec; /* seconds */long tv_nsec; /* nanoseconds */}; tv_sev is used to specify the number of seconds, tv_nsec is used to specify the number of nanoseconds ( 0-999 999 999), note that unlike the other functions mentioned above, nanosleep() is used for both members of the timespec structure, so you cannot specify a value greater than 999 999 999 for tv_nsec. Give tv_sec. All sleep functions will return when the signal is encountered. Sleep() and alarm() will return the remaining time, while usleep(), nanosleep() and select() will return -1.

Copyright © Windows knowledge All Rights Reserved