分类:
2012-10-13 14:03:56
原文地址:Linux下的精确定时与休眠 作者:g_hk
Linux中提供的休眠函数是sleep(),但是仅仅提供以秒为单位的休眠,这种休眠对有些进程显然太长了,那么怎样才能使进程以更小的时间分辨率休眠呢?
我知道的方法有2种,下面就做分别介绍。
第一种方法是使用定时器,Linux提供的定时器函数是:
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue);which指定那种定时器。Linux提供3种定时器:
struct itimerval { struct timeval it_interval; struct timeval it_value; }; struct timeval { long tv_sec; long tv_usec; };it_interval 指定间隔时间,it_value指定初始定时时间。如果只指定it_value,就是实现一次定时;如果同时指定it_interval,则超时后,系统 会重新初始化it_value为it_interval,实现重复定时;两者都清零,则会清除定时器。
int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);n指监视的文件描述符范围,通常设为所要select()的fd+1,readfds,writefds和exceptfds分别是读,写和异常文件描述符集,timeout为超时时间。
FD_CLR(int fd, fd_set *set); 清除fd FD_ISSET(int fd, fd_set *set); 测试fd是否设置 FD_SET(int fd, fd_set *set); 设置fd FD_ZERO(fd_set *set); 清空描述符集我们此时用不到这些宏,因为我们并不关心文件描述符的状态,我们关心的是select()超时。所以我们需要把readfds,writefds和 exceptfds都设为NULL,只指定timeout时间就行了。至于n我们可以不关心,所以你可以把它设为任何非负值。实现代码如下:
int usSleep(long us) { struct timeval tv; tv.tv_sec = 0; tv.tv_usec = us; return select(0, NULL, NULL, NULL, &tv); }呵呵,怎么样,是不是很简单?
int usleep(unsigned long usec);同sleep()一样,它在进程收到任何信号时都会返回。不过Linux手册上说,它并不会像sleep()一样会返回没休眠的秒数,而是返回-1,并把errno设为EINTR。 uClibc的实现如下:
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)); }可见usleep()内部是由nanosleep()函数实现的,接下来我们会讨论nanosleep()函数。我还见过如下用select()实现的,也支持了我前面所说的方法。
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); }接下来我们再来看看nanosleep()。nanosleep()可以实现纳秒(1/1000 000 000)级的休眠,所以它比usleep()更精确。但是你的CPU至少要1GHz以上才能支持这么高的分辨率。nanosleep()的原型如下:
int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);第一个参数指定要休眠的时间,第二个参数返回被信号中断时还没有休眠够的时间,timespec结构如下:
struct timespec { time_t tv_sec; /* seconds */ long tv_nsec; /* nanoseconds */ };tv_sev 用于指定秒数,tv_nsec用于指定纳秒数(0-999 999 999),注意不像前面所说的其它函数,nanosleep()对timespec结构的2个成员都要使用,所以你不能为tv_nsec指定大于999 999 999的数值,大于此数的应该送给tv_sec。所有的sleep函数遇信号都会返回,sleep()和alarm()遇信号返回剩余的时间,而 usleep(),nanosleep()和 select()遇信号都会返回-1。