Linux定时器
学习《Linux高性能服务器编程》第十一章定时器,里面介绍了各种网络程序中的定时事件,为了印象深刻一些,多动手多实践,所以记下这个笔记。这一篇主要记录Linux中SIGALRM信号触发的定时器。
SIGALRM信号
由于alarm
和setitimer
函数设置的实时闹钟一旦超时,将触发SIGALRM
信号。因此,我们可以利用该信号的信号处理函数来处理定时任务。但是,如果要处理多个定时任务,我们就需要不断地触发SIGALRM
信号,并在其信号处理函数中执行到期的任务。
一般而言,SIGALRM
信号按照固定的频率生成,即由alarm
或setitimer
函数设置的定时周期T
保持不变。如果某个定时任务的超时时间不是T
的整数倍,那么它实际被执行的时间和预期的时间将略有偏差。因此定时周期T
反映了定时的精度。
alarm函数
1 2 3
| #include <unistd.h>
unsigned int alarm(unsigned int seconds);
|
alarm
定时发送 SIGALRM
给当前进程(需要注意的是alarm
调用只会引起一次调用)。
seconds
参数表示经过seconds
秒数后发送SIGALRM
给目前的进程
alarm
返回上次定时剩余时间。
如果设置alarm(0)
则表示取消闹钟
我们举个小例子,结合前面的信号一起写下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <signal.h>
void sig_alarm(int a) { printf("hello world\n"); }
int main(int argc, char *argv[]) { int i; alarm(3); signal(SIGALRM, sig_alarm); while (true) { printf("------------------\n"); sleep(1); }
return 0; }
|
setitimer函数
setitimer
相比alarm
,提供了更为精细的参数选择
1 2 3 4
| #include <sys/time.h>
int getitimer(int which, struct itimerval *curr_value); int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
|
which
指计时器采用那种类型的计时方法
类型 |
介绍 |
ITIMER_REAL |
以系统真实的时间来计算,它送出SIGALRM 信号。 |
ITIMER_VIRTUAL |
以该进程用户空间下花费的时间来计算,它送出SIGVTALRM 信号。 |
ITIMER_PROF |
以该进程在用户空间下和内核下所费的时间来计算,它送出SIGPROF 信号。 |
new_value
和old_value
都是itimerval
类型的结构体
1 2 3 4 5 6 7 8 9
| struct itimerval { struct timeval it_interval; struct timeval it_value; };
struct timeval { time_t tv_sec; suseconds_t tv_usec; };
|
timeval
结构体中成员很简单,tv_sec
设置秒,tv_usec
设置微妙。
itimerval
结构体中成员it_interval
为计时间隔,it_value
为延时时长。比如:我想3s后,以每次5s的时间间隔打印hello world,那么就需要设置it_value
为3s,设置it_interval
为5s(3s后第一次打印,此后每次以5s为间隔打印)。
其中的new_value
参数用来对计时器进行设置。
old_value
参数,通常用不上,设置为NULL,它是用来存储上一次setitimer
调用时设置的new_value值。
函数调用成功返回0,失败返回-1,并且设置errno
。
假如it_value为0是不会触发信号的,所以要能触发信号,it_value得大于0;如果it_interval为0,只会延时,不会定时(也就是说只会触发一次信号)。
下面就写一个延时3s后,以5s为间隔打印hello world
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include <stdio.h> #include <sys/time.h> #include <signal.h> #include <unistd.h>
void sig_alarm(int signo) { printf("hello world\n"); }
int main(int argc, char *argv[]) { struct itimerval it, oldit;
signal(SIGALRM, sig_alarm);
it.it_value.tv_sec = 3; it.it_value.tv_usec = 0;
it.it_interval.tv_sec = 5; it.it_interval.tv_usec = 0;
if (setitimer(ITIMER_REAL, &it, &oldit) == -1) { perror("setitimer error"); return -1; }
while (1) { printf("------------------\n"); sleep(1); };
return 0; }
|
socket选项SO_RCVTIMEO和SO_SNDTIMEO
socket
选项SO_RCVTIMEO
和SO_SNDTIMEO
,它们分别用来设置socket
接收数据超时时间和发送数据超时时间。因此,这两个选项仅对与数据接收和发送相关的socket
专用系统调用( socket专用的系统调用指的是5.2~5.11节介绍的那些socketAPI)有效,这些系统调用包括send
、sendmsg
、recv
、recvmsg
、accept
和 connect
。将选项SO_RCVTIMEO和SO_SNDTIMEO对这些系统调用的影响总结于表中(来源Linux高性能服务器编程)。
这里举书上的代码例子,比较简单 明了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <assert.h> #include <stdio.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> #include <string.h>
int timeout_connect(const char *ip, int port, int time) { int ret = 0; struct sockaddr_in address; bzero(&address, sizeof(address)); address.sin_family = AF_INET; inet_pton(AF_INET, ip, &address.sin_addr); address.sin_port = htons(port);
int sockfd = socket(PF_INET, SOCK_STREAM, 0); assert(sockfd >= 0);
struct timeval timeout; timeout.tv_sec = time; timeout.tv_usec = 0; socklen_t len = sizeof(timeout); ret = setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, len); assert(ret != -1);
ret = connect(sockfd, (struct sockaddr *)&address, sizeof(address)); if (ret == -1) { if (errno == EINPROGRESS) { printf("connecting timeout\n"); return -1; } printf("error occur when connecting to server\n"); return -1; }
return sockfd; }
int main(int argc, char *argv[]) { if (argc <= 2) { printf("usage: %s ip_address port_number\n", basename(argv[0])); return 1; } const char *ip = argv[1]; int port = atoi(argv[2]);
int sockfd = timeout_connect(ip, port, 10); if (sockfd < 0) { return 1; } return 0; }
|