timeval详解

一、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.%06ld\n", 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] = '\0';
            printf("%s\n", buf);
        }
    }
    close(sockfd);
    return 0;
}

在以上代码中,我们使用setsockopt()函数将SO_RCVTIMEO选项设置为5秒超时。如果recv函数在5秒内没有读取到数据,recv()函数将返回-1,此时我们需要检查errno是否为EAGAIN或EWOULDBLOCK,以判断是否超时。如果没有超时,我们可以继续尝试读取数据。

六、总结

timeval结构体在Linux系统编程中扮演着非常重要的角色,它广泛应用于文件描述符状态判断、高精度定时器、网络I/O超时等场景。掌握timeval结构体的使用,有助于我们写出更加高效、可靠的Linux系统程序。

原创文章,作者:NGFWJ,如若转载,请注明出处:https://www.506064.com/n/329312.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
NGFWJ的头像NGFWJ
上一篇 2025-01-14 18:55
下一篇 2025-01-14 18:55

相关推荐

  • Linux sync详解

    一、sync概述 sync是Linux中一个非常重要的命令,它可以将文件系统缓存中的内容,强制写入磁盘中。在执行sync之前,所有的文件系统更新将不会立即写入磁盘,而是先缓存在内存…

    编程 2025-04-25
  • 神经网络代码详解

    神经网络作为一种人工智能技术,被广泛应用于语音识别、图像识别、自然语言处理等领域。而神经网络的模型编写,离不开代码。本文将从多个方面详细阐述神经网络模型编写的代码技术。 一、神经网…

    编程 2025-04-25
  • Linux修改文件名命令详解

    在Linux系统中,修改文件名是一个很常见的操作。Linux提供了多种方式来修改文件名,这篇文章将介绍Linux修改文件名的详细操作。 一、mv命令 mv命令是Linux下的常用命…

    编程 2025-04-25
  • nginx与apache应用开发详解

    一、概述 nginx和apache都是常见的web服务器。nginx是一个高性能的反向代理web服务器,将负载均衡和缓存集成在了一起,可以动静分离。apache是一个可扩展的web…

    编程 2025-04-25
  • 详解eclipse设置

    一、安装与基础设置 1、下载eclipse并进行安装。 2、打开eclipse,选择对应的工作空间路径。 File -> Switch Workspace -> [选择…

    编程 2025-04-25
  • Python输入输出详解

    一、文件读写 Python中文件的读写操作是必不可少的基本技能之一。读写文件分别使用open()函数中的’r’和’w’参数,读取文件…

    编程 2025-04-25
  • C语言贪吃蛇详解

    一、数据结构和算法 C语言贪吃蛇主要运用了以下数据结构和算法: 1. 链表 typedef struct body { int x; int y; struct body *nex…

    编程 2025-04-25
  • Java BigDecimal 精度详解

    一、基础概念 Java BigDecimal 是一个用于高精度计算的类。普通的 double 或 float 类型只能精确表示有限的数字,而对于需要高精度计算的场景,BigDeci…

    编程 2025-04-25
  • MPU6050工作原理详解

    一、什么是MPU6050 MPU6050是一种六轴惯性传感器,能够同时测量加速度和角速度。它由三个传感器组成:一个三轴加速度计和一个三轴陀螺仪。这个组合提供了非常精细的姿态解算,其…

    编程 2025-04-25
  • git config user.name的详解

    一、为什么要使用git config user.name? git是一个非常流行的分布式版本控制系统,很多程序员都会用到它。在使用git commit提交代码时,需要记录commi…

    编程 2025-04-25

发表回复

登录后才能评论