当前位置: 首页 > news >正文

Linux定时器

Linux定时器

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

SIGALRM信号

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

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

alarm函数

#include <unistd.h>

unsigned int alarm(unsigned int seconds);

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

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

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

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

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

#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);                   // 3秒后发送信号
    signal(SIGALRM, sig_alarm); //设置信号对应的处理函数
    while (true)
    {
        printf("------------------\n");
        sleep(1);
    }

    return 0;
}

image-20220829204127701

setitimer函数

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

#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_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 */
};

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 <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); //注册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;
}

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 <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;
}

相关文章:

  • iNFTnews | 一词解答区块链技术普及的制胜关键
  • 京准,PTP时间同步服务器在运营商通信网应用
  • 判断数组类型的方法(Array.isArray)以及Math数字对象
  • zemax---Tangential plane, meridian plane and sagittal plane(切线面,子午面与弧矢面)(完结)
  • Java配置42-配置redis高可用(sentinel监控)
  • 共话龙蜥:中国操作系统到底有没有角力世界舞台的实力?
  • vue独立提供模板下载功能
  • 怎么判断MES系统好不好?MES又是如何帮企业省钱的?
  • 数据治理:为什么不见BI作关联分析
  • 聚氨基酯偶联牛血清白蛋白/人血清白蛋白/卵清白蛋白纳米粒PAE-BSA/HSA/OVA(合成路线)
  • 应用开发类API推荐
  • sonar是一款静态代码质量分析工具
  • EN 14782建筑金属结构产品—CE认证
  • Leetcode 376. 摆动序列
  • Linux12 crontab 定时任务 at 一次性任务
  • 【每日笔记】【Go学习笔记】2019-01-10 codis proxy处理流程
  • CoolViewPager:即刻刷新,自定义边缘效果颜色,双向自动循环,内置垂直切换效果,想要的都在这里...
  • java架构面试锦集:开源框架+并发+数据结构+大企必备面试题
  • java小心机(3)| 浅析finalize()
  • JS进阶 - JS 、JS-Web-API与DOM、BOM
  • React as a UI Runtime(五、列表)
  • react-core-image-upload 一款轻量级图片上传裁剪插件
  • SAP云平台运行环境Cloud Foundry和Neo的区别
  • Terraform入门 - 1. 安装Terraform
  • Twitter赢在开放,三年创造奇迹
  • UEditor初始化失败(实例已存在,但视图未渲染出来,单页化)
  • 程序员最讨厌的9句话,你可有补充?
  • 大主子表关联的性能优化方法
  • 基于Android乐音识别(2)
  • 解决jsp引用其他项目时出现的 cannot be resolved to a type错误
  • 聊聊flink的TableFactory
  • 我是如何设计 Upload 上传组件的
  • 在Docker Swarm上部署Apache Storm:第1部分
  • python最赚钱的4个方向,你最心动的是哪个?
  • scrapy中间件源码分析及常用中间件大全
  • Spring Batch JSON 支持
  • 进程与线程(三)——进程/线程间通信
  • ​LeetCode解法汇总2808. 使循环数组所有元素相等的最少秒数
  • (ISPRS,2023)深度语义-视觉对齐用于zero-shot遥感图像场景分类
  • (LeetCode 49)Anagrams
  • (pojstep1.3.1)1017(构造法模拟)
  • (附源码)ssm学生管理系统 毕业设计 141543
  • (六)c52学习之旅-独立按键
  • (十)DDRC架构组成、效率Efficiency及功能实现
  • (转)Sublime Text3配置Lua运行环境
  • .bat批处理(二):%0 %1——给批处理脚本传递参数
  • .gitattributes 文件
  • .NET 除了用 Task 之外,如何自己写一个可以 await 的对象?
  • .NET开发不可不知、不可不用的辅助类(三)(报表导出---终结版)
  • /使用匿名内部类来复写Handler当中的handlerMessage()方法
  • ??在JSP中,java和JavaScript如何交互?
  • @Autowired注解的实现原理
  • @RequestBody详解:用于获取请求体中的Json格式参数
  • @serverendpoint注解_SpringBoot 使用WebSocket打造在线聊天室(基于注解)
  • [202209]mysql8.0 双主集群搭建 亲测可用