• Linux定时器


    Linux定时器

    学习《Linux高性能服务器编程》第十一章定时器,里面介绍了各种网络程序中的定时事件,为了印象深刻一些,多动手多实践,所以记下这个笔记。这一篇主要记录Linux中SIGALRM信号触发的定时器。

    SIGALRM信号

    由于alarmsetitimer函数设置的实时闹钟一旦超时,将触发SIGALRM信号。因此,我们可以利用该信号的信号处理函数来处理定时任务。但是,如果要处理多个定时任务,我们就需要不断地触发SIGALRM信号,并在其信号处理函数中执行到期的任务。

    一般而言,SIGALRM信号按照固定的频率生成,即由alarmsetitimer函数设置的定时周期T保持不变。如果某个定时任务的超时时间不是T的整数倍,那么它实际被执行的时间和预期的时间将略有偏差。因此定时周期T反映了定时的精度。

    alarm函数

    #include 
    
    unsigned int alarm(unsigned int seconds);
    
    • 1
    • 2
    • 3

    alarm定时发送 SIGALRM 给当前进程(需要注意的是alarm调用只会引起一次调用)。

    seconds参数表示经过seconds秒数后发送SIGALRM 给目前的进程

    alarm返回上次定时剩余时间。

    如果设置alarm(0)则表示取消闹钟

    我们举个小例子,结合前面的信号一起写下

    #include 
    #include 
    #include 
    #include 
    
    void sig_alarm(int a)
    {
        printf("hello world\n");
    }
    
    int main(int argc, char *argv[])
    {
        int i;
        alarm(3);                   // 3秒后发送信号
        signal(SIGALRM, sig_alarm); //设置信号对应的处理函数
        while (true)
        {
            printf("------------------\n");
            sleep(1);
        }
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    image-20220829204127701

    setitimer函数

    setitimer相比alarm,提供了更为精细的参数选择

    #include 
    
    int getitimer(int which, struct itimerval *curr_value);
    int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
    
    • 1
    • 2
    • 3
    • 4

    which指计时器采用那种类型的计时方法

    类型介绍
    ITIMER_REAL以系统真实的时间来计算,它送出SIGALRM信号。
    ITIMER_VIRTUAL以该进程用户空间下花费的时间来计算,它送出SIGVTALRM信号。
    ITIMER_PROF以该进程在用户空间下和内核下所费的时间来计算,它送出SIGPROF信号。

    new_valueold_value都是itimerval类型的结构体

    struct itimerval {
       struct timeval it_interval; /* Interval for periodic timer */
       struct timeval it_value;    /* Time until next expiration */
    };
    
    struct timeval {
       time_t      tv_sec;         /* seconds */
       suseconds_t tv_usec;        /* microseconds */
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    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

    #include 
    #include 
    #include 
    #include 
    
    void sig_alarm(int signo)
    {
        printf("hello world\n");
    }
    
    int main(int argc, char *argv[])
    {
        struct itimerval it, oldit;
    
        signal(SIGALRM, sig_alarm); //注册SIGALRM信号的捕捉处理函数。
    
        it.it_value.tv_sec = 3; //设置延时3s
        it.it_value.tv_usec = 0;
    
        it.it_interval.tv_sec = 5; //设置时间间隔5s
        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;
    }
    
    • 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

    image-20220829212215274

    socket选项SO_RCVTIMEO和SO_SNDTIMEO

    socket选项SO_RCVTIMEOSO_SNDTIMEO,它们分别用来设置socket 接收数据超时时间和发送数据超时时间。因此,这两个选项仅对与数据接收和发送相关的socket专用系统调用( socket专用的系统调用指的是5.2~5.11节介绍的那些socketAPI)有效,这些系统调用包括sendsendmsgrecvrecvmsgacceptconnect。将选项SO_RCVTIMEO和SO_SNDTIMEO对这些系统调用的影响总结于表中(来源Linux高性能服务器编程)。

    image-20220830110132529

    这里举书上的代码例子,比较简单 明了

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    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;
    }
    
    • 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
  • 相关阅读:
    现代卷积网络实战系列5:PyTorch从零构建GoogLeNet训练MNIST数据集
    Redis 主从架构数据同步
    数仓建模,什么是宽表?如何设计?好处与不足
    二叉搜索树
    零基础入行IC,选模拟版图还是数字后端?
    JDK版本对应其major.minor version,看这一篇就够啦(附java历史版本下载地址)
    bitmap技术解析:redis与roaringBitmap
    github知识小结(2022/08/07)
    SpringCloud微服务:Nacos的下载和配置
    01 【前言 基础使用 核心概念】
  • 原文地址:https://blog.csdn.net/qq_41474648/article/details/126600367