一、timeval概述
timeval是Linux系统中的一个时间结构体,它由<time.h>
头文件定义。该结构体包含两个字段——秒数和微秒数。它广泛用于Linux系统中的许多接口,例如select、pselect、gettimeofday、settimeofday等。timeval的使用便捷性,以及对时间的高度精确度,使其成为Linux系统中常用的时间结构体之一。
二、timeval数据类型
timeval的定义如下:
struct timeval { time_t tv_sec; suseconds_t tv_usec; };
其中,tv_sec
表示从1970年1月1日0时0分0秒至今所经过的秒数,它是一个time_t
类型的整数;tv_usec
表示微秒数,它是一个suseconds_t
类型的整数。
我们可以通过以下方式对timeval变量进行初始化:
struct timeval tv = {0}; tv.tv_sec = 10; //设置秒数为10 tv.tv_usec = 500000; //设置微秒数为500000
三、timeval的精度
timeval结构体的精度可以达到微秒级别。gettimeofday()函数便是利用timeval来实现高精度的时间获取。
下面是一个使用gettimeofday()函数获取当前时间的示例代码:
struct timeval tv; gettimeofday(&tv, NULL); printf("Current time: %ld.%06ldn", tv.tv_sec, tv.tv_usec);
在以上代码中,我们使用gettimeofday()
函数获取当前时间,将结果存储在tv
结构体中,最后打印输出。
四、timeval在select函数中的应用
select()
函数是Linux系统中用于等待文件描述符状态变化的一个系统调用。该函数的第1个参数nfds
是待检测的最大文件描述符加1;第2个参数readfds
是待读取的文件描述符的集合;第3个参数writefds
是待写入的文件描述符的集合;第4个参数exceptfds
是待检测的异常文件描述符的集合;第5个参数timeout
是超时时间。
timeout参数可以用timeval结构体来表示。它指定select函数等待的最长时间。当timeout为NULL时,select()
函数将一直阻塞,直到有一个待读、待写或异常的文件描述符就绪。当timeout不为NULL且指定的时间到达时,select()
函数将立即返回。
下面是一个使用select函数实现高精度定时器的示例程序:
#include <sys/select.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> int main() { fd_set set; FD_ZERO(&set); FD_SET(STDIN_FILENO, &set); struct timeval tv = {0}; tv.tv_sec = 5; //设置定时时间为5秒 int ret = select(STDIN_FILENO + 1, &set, NULL, NULL, &tv); if (ret == -1) { perror("select"); exit(EXIT_FAILURE); } else if (ret == 0) { printf("Timeout occurred!n"); //超时 } else { if (FD_ISSET(STDIN_FILENO, &set)) { printf("Data is available now.n"); //有数据可读 } } return 0; }
在以上代码中,我们使用select()
函数实现一个高精度定时器。将标准输入的文件描述符添加到文件描述符集合中,设置超时时间为5秒。当超时时间到达时,select()
函数将返回0,表示超时。当有数据可读时,select()
函数将返回1。
五、timeval在网络编程中的应用
timeval结构体在网络编程中也有广泛的应用,它可以用来设置各类超时时间,例如网络I/O操作的超时时间。
下面是一个使用select函数实现socket I/O超时的示例程序:
#include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <arpa/inet.h> #include <netinet/in.h> int main() { int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { perror("socket"); exit(EXIT_FAILURE); } struct sockaddr_in sockaddr; sockaddr.sin_family = AF_INET; sockaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); sockaddr.sin_port = htons(8888); int ret = connect(sockfd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); if (ret < 0) { perror("connect"); exit(EXIT_FAILURE); } char buf[1024] = {0}; struct timeval timeout = {0}; timeout.tv_sec = 5; //设置超时时间为5秒 if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == -1) { perror("setsockopt"); exit(EXIT_FAILURE); } while (1) { ret = recv(sockfd, buf, sizeof(buf), 0); //recv函数默认是阻塞的 if (ret == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { //超时 printf("Timeout occurred!n"); continue; } perror("recv"); exit(EXIT_FAILURE); } else if (ret == 0) { printf("Connection closed.n"); //连接关闭 break; } else { printf("Received %d bytes of data.n", ret); //接收到数据 buf[ret] = ''; printf("%sn", buf); } } close(sockfd); return 0; }
在以上代码中,我们使用setsockopt()
函数将SO_RCVTIMEO选项设置为5秒超时。如果recv函数在5秒内没有读取到数据,recv()
函数将返回-1,此时我们需要检查errno是否为EAGAIN或EWOULDBLOCK,以判断是否超时。如果没有超时,我们可以继续尝试读取数据。
六、总结
timeval结构体在Linux系统编程中扮演着非常重要的角色,它广泛应用于文件描述符状态判断、高精度定时器、网络I/O超时等场景。掌握timeval结构体的使用,有助于我们写出更加高效、可靠的Linux系统程序。