精华内容
下载资源
问答
  • Linux定时器接口

    2018-09-22 09:54:00
    Linux定时器接口主要分为三类: 一. sleep(), unsleep, alarm(),引用了SIGALARM信号,在多线程中使用信号又是相当麻烦的。 二. nanosleep(), clock_nanosleep(),让线程挂起,程序失去响应,多线程网络编程中应该...

    Linux定时器接口主要分为三类:

    一. sleep(), unsleep, alarm(),引用了SIGALARM信号,在多线程中使用信号又是相当麻烦的。

    二. nanosleep(), clock_nanosleep(),让线程挂起,程序失去响应,多线程网络编程中应该避免。

    三. timerfd_create(),也是用信号来deliver超时,将时间转变成一个文件描述符,可以像其他I/O事件一样操作定时器,所以程序中在写I/O框架用到定时器首选timerfd_create()。

     

    1. timerfd的创建

    int timerfd_create(int clockid, int flags);//成功返回0

    第一个参数为CLOCK_REALTIME:表示相对时间,表示从1970.1.1到现在的时间。或者CLOCK_MONOTONIC:表示绝对时间,表示系统重启到现在的时间。

    第二个参数为TFD_NONBLOCK(非阻塞)或TFD_CLOEXEC(同O_CLOEXEC)。

    2. 定时器的设置(启动和停止定时器)

    int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspce *old_value);//成功返回0

    第一个参数fd是timerfd_create创建的定时器文件描述符。

    第二个参数如果是0表示相对定时器,为TFD_TIMER_ABSTIME表示绝对定时器。

    第三个参数new_value设置超时时间,为0表示停止定时器。

    第四个参数为原来的超时时间,一般设为NULL。

    3. 代码实例:

    //代码逻辑:创建timer等待时间,创建timer定时器文件描述符,创建epoll_event事件绑定timer,另起一线程等待事件。时间到,事件触发,操作map之前用mutex锁住,找到GroupID

    #include <syso/timerfd.h>

    #include <syso/epoll.h>

    itimerspec timerValue;
    memset(&timerValue, 0, sizeof(timerValue));
    timerValue.it_value.tv_sec = tagCollection->GetSampleRate() / 1000;
    timerValue.it_value.tv_nsec = (tagCollection->GetSampleRate() % 1000) * 1000 * 1000; // GetPublishInterval is in milliseconds
    timerValue.it_interval.tv_sec = timerValue.it_value.tv_sec;
    timerValue.it_interval.tv_nsec = timerValue.it_value.tv_nsec;

    timerFd = timerfd_create(CLOCK_MONOTONIC, 0);
    if (timerFd < 0)
    {
      SYSO_CRIT("timer_create failed with error: %s", strerror(errno));
      return;
    }
    // set events
    epoll_event epollEvent;
    memset(&epollEvent, 0, sizeof(epoll_event));
    epollEvent.events = EPOLLIN;
    epollEvent.data.fd = timerFd;
    epoll_ctl(epollFd, EPOLL_CTL_ADD, timerFd, &epollEvent); // this is thread safe
    // start timer
    if (timerfd_settime(timerFd, 0, &timerValue, nullptr) < 0)
    {
      SYSO_NOTICE("timerfd_settime failed with error", strerror(errno));
    }
    else
    {
      SYSO_INFO("PlcAdapterProxy Create Get Sample timer: ", timerFd, ", interval: ", timerValue.it_value.tv_sec, " seconds");
    }

    另起一线程等待epollfd

    for(;;) // wait for events
    {
      int eventCount = epoll_wait(epollFd, newEvents, MAX_TIMER_EVENTS, -1);
      if (eventCount > 0)
      {
        for (int i = 0; i < eventCount; ++i)
        {
          uint64_t clearEventDummy = 0;
          static_cast<void>(read(newEvents[i].data.fd, &clearEventDummy, sizeof(uint64_t))); // clears the event

          std::unique_lock<std::mutex> lock(epollToTimerIdMapMutex);
          auto iter = epollToTimerIdMap.find(newEvents[i].data.fd);
          if(iter != epollToTimerIdMap.end())
          {
            CollectionTimerElapsed(iter->second);
          }
        }
      }
      else
      {
        SYSO_CRIT("epoll_wait for timers failed, errno:%s", strerror(errno));
      }
    }

     

    转载于:https://www.cnblogs.com/embeddedking/p/9689494.html

    展开全文
  • Linux常用的时间相关函数 获取当前时间: time(0) ------ time_t ------ 秒 gettimeofday ------ timeval ------ 微妙 可以认为这两个函数都是线程安全的,获取到的时间戳是不包括闰秒的 获取当地时间: localtime_r...
    Linux常用的时间相关函数

    获取当前时间:
    time(0) ------ time_t ------ 秒
    gettimeofday ------ timeval ------ 微妙
    可以认为这两个函数都是线程安全的,获取到的时间戳是不包括闰秒

    获取当地时间:
    localtime_r函数,_r后缀是线程安全的

    定时器相关函数
    timerfd_create timerfd_gettime timerfd_settime (改用文件描述符方式取代了信号)
    关于定时器接口的演进参考文章:Linux 下定时器的实现方式分析

    由于基于文件描述符,使得该接口可以支持 select(2),poll(2) 等异步接口,使得定时器的实现和使用更加的方便,更重要的是,支持 fork(2),exec(2) 这样多进程的语义,因此,可以用在多线程环境之中,它们的使用比 POSIX timer [ 2 ]更加的灵活,其根本原因在于定时器的管理统一到了 unix/linux 基本哲学之一 ---- “一切皆文件”之下。

    Linux定时器管理

    原先信号通知的方式只支持注册一个定时器,因此必须在用户空间进行管理。后面新的POSIX timer支持了多个定时器,但也是由内核空间代为组织管理。最后演变出的基于文件描述符的定时器,文件描述符的数量是有限的,因此也必须进行管理。

    Linux内核定时器的管理是使用了基于时间轮的方式

    定时器的管理方式对比(Linux下定时器的设计与实现
    在这里插入图片描述

    各种网络库的定时器实现

    muduo网络库
    直接是使用了STL中的set容器,多线程竞争的问题它是采用了各个线程处理各自的定时器,因此几乎没有竞争,具体实现过程如下:

    1. 每个线程都有一个EventLoop对象,EventLoop对象初始化的时候会new一个TimerQueue,因此每个IO线程会有一个TimerQueue,TimerQueue对象初始化的时候就会分配好Timerfd和channel等,当Timerfd有可读事件发生时,就会从队列中拿出到时事件处理
    2. 在增加和删除定时器时,通过runInLoop方法保证了能够在非IO线程给指定EventLoop的IO线程增加和删除定时器。因此,保存定时器的队列也不需要加锁
    3. TimerQueue有一个cancelingTimers_ 成员,其作用是:当在定时队列中找不到删除的定时器,而此时正在执行定时器的回调函数,那么就将此定时器记录下来。在此次所有的定时器执行完成之后会调用reset 函数,如果是个周期性的定时器且不在cancelingTimers_ 中,则可以将其重新加入我们的TimerQueue中。
    4. 在reset函数中,如果不是周期性定时器就会被delete掉,在cancel函数和TimerQueue析构函数中都会delete掉Timer对象。(可用unique_ptr解决这个问题,使用std:move 应该能够实现转移)
    5. runInLoop的实现中调用了queueinloop方法,该方法会往目标IO线程的EventLoop对象的pendingFunctors_增加元素(typedef std::function<void()> Functor 参数为空,所有返回值类型都兼容),同时唤醒该线程(eventfd可读),而在EventLoop的循环中就会定期执行这些函数。

    libco协程库
    使用的是时间轮的方式

    brpc框架
    timer_keeping.md

    RPC框架的特性:

    1. 大部分定时器都是RPC调用超时定时器,绝大部分场景下是不需要用到的
    2. 多线程框架,并且任何线程都可能被阻塞,因此需要独立的线程做定时任务才能保证定时器的准确性
    3. 相互竞争的线程设定的时间往往聚集在同一个区域,因为程序的超时大都是一个值,加上当前时间后都差不多。
    展开全文
  • linux 定时器

    千次阅读 2018-11-29 22:35:59
    windows下的接口支持单进程中拥有多个定时器,而linux则只允许单进程拥有一个定时器,因此在linux下的单进程中要使用多个定时器,则需要自己维护管理, 1.alarm函数,对定时要求不太精确的话,使用alarm()和signal...

    参考:

    https://www.cnblogs.com/wenqiang/p/5525261.html

    windows下的接口支持单进程中拥有多个定时器,而linux则只允许单进程拥有一个定时器,因此在linux下的单进程中要使用多个定时器,则需要自己维护管理,

    1.alarm函数,对定时要求不太精确的话,使用alarm()和signal()就行了

    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <signal.h>

    void func()
    {
        printf("this is func\n");
    }

    int main()
    {

        signal(SIGALRM, func); //执行的函数
        alarm(1);//设置定时1s

        while (1);

        return 0;
    }

    上面函数只执行一次,如果想连续执行,需要在func函数中添加 alarm(1);

    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <signal.h>

    void func()
    {
        printf("this is func\n");
        signal(SIGALRM, func); //1s后要执行的函数
        alarm(1);//设置定时1s
    }

    int main()
    {

        signal(SIGALRM, func); //1s后要执行的函数
        alarm(1);//设置定时1s

        while (1);

     return 0;
    }

    2.实现精度较高的定时功能的话,就要使用setitimer函数。

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <signal.h>
    #include <sys/time.h>

    void test_func()
    {
        static count = 0;

        printf("count is %d\n", count++);
    }

    void init_sigaction()
    {
        struct sigaction act;
              
        act.sa_handler = test_func; //设置处理信号的函数
        act.sa_flags  = 0;

        sigemptyset(&act.sa_mask);
        sigaction(SIGPROF, &act, NULL);//时间到发送SIGROF信号
    }

    void init_time()
    {
        struct itimerval val;
             
        val.it_value.tv_sec = 3; //3秒后启用定时器  秒
        val.it_value.tv_usec = 0; // 微妙

        val.it_interval.tv_sec=3;//定时器间隔为3s
        val.it_interval.tv_usec = 0; //

    /*如果it_interval设置为0则定时器只执行一次

    * val.it_interval.tv_sec=0

    *;val.it_interval.tv_sec=0

    */

     

        setitimer(ITIMER_PROF, &val, NULL);
    }

    int main(int argc, char **argv)
    {

        init_sigaction();
        init_time();

        while(1);

        return 0;
    }

     

    3.基于时间轮的定时器简单实现,在实际项目中,一个常用的做法是新起一个线程,专门管理定时器,定时来源使用rtc、select等比较精确的来源,定时器超时后向主要的work线程发消息即可,或者使用timefd接口

    #include <stdio.h>
    #include <signal.h>
    #include <stdlib.h>
    #include <unistd.h>

    #define TIME_WHEEL_SIZE 8

    typedef void (*func)(int data);

    struct timer_node {
        struct timer_node *next;
        int rotation;
        func proc;
        int data;
    };

    struct timer_wheel {
        struct timer_node *slot[TIME_WHEEL_SIZE];
        int current;
    };

    struct timer_wheel timer = {{0}, 0};

    void tick(int signo)
    {
        // 使用二级指针删进行单链表的删除
        struct timer_node **cur = &timer.slot[timer.current];
        while (*cur) {
            struct timer_node *curr = *cur;
            if (curr->rotation > 0) {
                curr->rotation--;
                cur = &curr->next;
            } else {
                curr->proc(curr->data);
                *cur = curr->next;
                free(curr);
            }
        }
        timer.current = (timer.current + 1) % TIME_WHEEL_SIZE;
        alarm(1);
    }

    void add_timer(int len, func action)
    {
        int pos = (len + timer.current) % TIME_WHEEL_SIZE;
        struct timer_node *node = malloc(sizeof(struct timer_node));

        // 插入到对应格子的链表头部即可, O(1)复杂度
        node->next = timer.slot[pos];
        timer.slot[pos] = node;
        node->rotation = len / TIME_WHEEL_SIZE;
        node->data = 0;
        node->proc = action;
    }

     // test case1: 1s循环定时器
    int g_sec = 0;
    void do_time1(int data)
    {
        printf("timer %s, %d\n", __FUNCTION__, g_sec++);
        add_timer(1, do_time1);
    }

    // test case2: 2s单次定时器
    void do_time2(int data)
    {
        printf("timer %s\n", __FUNCTION__);
    }

    // test case3: 9s循环定时器
    void do_time9(int data)
    {
        printf("timer %s\n", __FUNCTION__);
        add_timer(9, do_time9);
    }

    int main()
    {
        signal(SIGALRM, tick);
        alarm(1); // 1s的周期心跳

        // test
        add_timer(1, do_time1);
        add_timer(2, do_time2);
        add_timer(9, do_time9);

        while(1) pause();
        return 0;
    }
     

    展开全文
  • 一个非常简单易用的linux下的C语言的定时器封装接口,使用查询方式,可同时使用不限个数的定时器,还附带有详细的使用demo。
  • timerfd是Linux为用户程序提供的一个定时器接口。这个接口基于文件描述符,通过文件描述符的可读事件进行超时通知,所以能够被用于select/poll的应用场景。timerfd是linux内核2.6.25版本中加入的借口。 timerfd、...

    1、概述

          timerfd是Linux为用户程序提供的一个定时器接口。这个接口基于文件描述符,通过文件描述符的可读事件进行超时通知,所以能够被用于select/poll的应用场景。timerfd是linux内核2.6.25版本中加入的借口。
          timerfdeventfdsignalfd配合epoll使用,可以构造出一个零轮询的程序,但程序没有处理的事件时,程序是被阻塞的。这样的话在某些移动设备上程序更省电。

    2、函数接口

    (1) 第一个系统调用, 该系统调用创建一个新的定时器对象 :

    #include <sys/timerfd.h>
    int timerfd_create(int clockid, int flags);
    //成功返回一个指代该对象的文件描述符, 失败返回-1及errno
    • 参数 clockid 可以设置为 CLOCK_REALTIME CLOCK_MONOTONIC

         CLOCK_REALTIME:可设定的系统级实时时钟,相对时间,从1970.1.1到目前的时间。更改系统时间会更改获取的值。也就是说,它以系统时间为坐标。

        CLOCK_MONOTONIC:不可设定的恒定态时钟,与CLOCK_REALTIME相反,它是以绝对时间为准,获取的时间为系统重启到现在的时间,更改系统时间对齐没有影响。

    • 参数flags, 支持 TFD_CLOEXECTFD_NONBLOCK 和 0

      TFD_CLOEXEC 为新的文件描述符设置运行时关闭标志 (FD_CLOEXEC)
      TFD_NONBLOCK 为底层的打开文件描述符设置 O_NONBLOCK 标志, 随后的读操作将是非阻塞的, 这与调用 fcntl 效果相同

    (2) 第二个系统调用, 可以启动或停止由文件描述符 fd 所指代的定时器

    int timerfd_settime(int fd, int flags, const struct 
    		itimerspec* new_value, struct itimerspec* old_value);
    //成功返回0, 失败返回-1和 errno
    • 参数flags 的值可以是 0 (相对时间), 可以是 TFD_TIMER_ABSTIME (绝对时间)
    • 参数new_value、old_value都是struct itimerspec* 类型
    struct itimerspec 
    {
        struct timespec it_interval;   //间隔时间
        struct timespec it_value;      //设置启动时间
    };
    
    struct timespec 
    {
        time_t tv_sec;    //秒
        long tv_nsec;    //纳秒
    }; 
    

    (3) 第三个系统调用, 返回文件描述符 fd 所标识定时器的间隔及剩余时间

    int timerfd_gettime(int fd, struct itimerspec *curr_value);
    //成功返回0, 失败返回-1和errno

    (4)获取时钟的值

    #include <time.h>
    int clock_gettime(clockid_t clk_id, struct timespect *tp);

    3、使用epoll监听定时器事件

           使用方法和socketfd类似,利用timerfd_create创建定时器事件,通过timerfd_settime设置启动时间和时间间隔,然后使用epoll_ctl加入监听队列。

    #include <sys/timerfd.h>
    #include <iostream>
    #include <time.h>
    #include <sys/epoll.h>
    #include <unistd.h>
    #include <assert.h>
    using namespace std;
    
    const int MAXNUM = 20;
    
    int main(int argc, char *argv[])
    {
        struct itimerspec new_value;
        struct timespec now;
        uint64_t exp;
        ssize_t s;
    
        int ret = clock_gettime(CLOCK_REALTIME, &now);//获取基准时钟时间(相对时间可忽略此函数)
        assert(ret != -1);
    
        new_value.it_value.tv_sec = now.tv_sec+3; //启动后3秒
        new_value.it_value.tv_nsec = now.tv_nsec; 
    
        new_value.it_interval.tv_sec = 1;      //之后每次到期的时间间隔
        new_value.it_interval.tv_nsec = 0;
    
        int timefd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK); // 构建了一个定时器
        assert(timefd != -1);
        
        //如果创建相对时间事件,且 timerfd_settime  第二参数设置0,则不需要clock_gettime获取基准时间
        ret = timerfd_settime(timefd, TFD_TIMER_ABSTIME, &new_value, NULL);//启动定时器
        assert(ret != -1);
    
        cout << "timer started" << endl; // 定时器开启啦!
    
    
        int epollfd = epoll_create(1);  //创建epoll实例对象
    
        struct epoll_event ev;
        struct epoll_event events[MAXNUM];
        ev.data.fd = timefd;
        ev.events = EPOLLIN | EPOLLET;
        epoll_ctl(epollfd, EPOLL_CTL_ADD, timefd, &ev); //添加到epoll事件集合
    
        for (; ;) 
        {
            int num = epoll_wait(epollfd, events, MAXNUM, -1);
            assert(num >= 0);
    
            for (int i = 0; i < num; i++) 
            {
                if (events[i].events & EPOLLIN) 
                {
                    //....处理其他事件
                    if (events[i].data.fd == timefd) 
                    {
                        s = read(events[i].data.fd, &exp, sizeof(uint64_t)); //需要读出uint64_t大小, 不然会发生错误
                        assert(s == sizeof(uint64_t));
                        cout << "here is timer" << endl;
                    }
                }
            }
        }
    
        close(timefd);
        close(epollfd);
    
        return 0;
    }
    

     

    展开全文
  • linux定时器的使用

    2018-08-09 11:48:13
     定时器(Timer)本来是硬件资源,但是由linux接管了所有的CPU资源,并对这些资源进行了抽象,那么我们对Timer的使用就应该按照linux的要求,使用linux提供的接口来使用Timer。 2.使用 初始化 static struct timer_...
  • 我们没法在接口中来指定调用,这时我们就可以利用linux定时器crontab来完成 现在我在项目中遇见这样一个问题,我需要每5分钟调用一个接口接口会大量的操作数据库的数据更新, 这样的一个接口调用 如果
  • 创建、初始化以及删除一个定时器的行动被分为三个不同的函数:timer_create()(创建定时器)、timer_settime()(初始化定时器)以及timer_delete(销毁它)。   (1)创建一个定时器: int timer_create(clockid_t ...
  • 内核提供了一组与定时器相关的接口用来简化管理定时器的操作。所有这些接口都声明在&lt;linux/Timer.h&gt;中,大多数接口在&lt;kernel/timer.c&gt;中的到实现。  创建定时器首先要先定义它,然后...
  • Linux内核定时器

    2020-08-13 17:44:09
     一、Linux定时器主要调用接口 1、定时器数据结构 1 struct timer_list { 2 /* 3 * All fields that change during normal runtime grouped to 4 * the same cacheline 5 */ 6 struct hlist_node entry; ...
  • 2.6.13 - 2.6.14 添加 #define DEFINE_TIMER(_name, _function, _expires, _data) 2.6.14 - 2.6.15 static inline void setup_timer(struct timer_list * timer, void (*function)(uns...
  • linux内核定时器

    2021-01-13 22:01:29
    一、timer 定时器 工作流程: 1)创建timer,编写定时器处理函数; 2)为timer的expires、data、function赋值; 3)调用add_timer将timer加入... //linux中对所有定时器实行链表管理,此为链表头结点 unsigned long
  • linux 超时定时器

    千次阅读 2013-11-20 16:03:43
    linux新定时器:timefd及相关操作函数 ...timerfd是Linux为用户程序提供的一个定时器接口。这个接口基于文件描述符,通过文件描述符的可读事件进行超时通知,所以能够被用于select/poll的应用场景。 一,相关操作函数
  • Linux定时器的实现之一

    千次阅读 2018-02-03 12:48:07
    Linux多定时器的实现之一 时间管理是计算机系统的主要任务。在时间管理中,经常利用...windows系统提供了SetTimer和timeSetEvent等定时器接口Linux中则提供了setitimer等接口。这些函数的接口很类似,大体上都是
  • linux内核定时器编程

    2014-04-17 11:16:27
    1.linux内核定时器基本结构和函数 1)struct timer_list 一个struct timer_list对应了一个定时器。 #include &lt;linux/timer.h&gt; 以下列出常用的接口: struct timer_list { /*....*/ unsigned ...
  • 定时器: linux操作系统提供了一个内核定时器 ...假设HZ的值为200,Linux定时器最短定时时间为5ms,小于该时间的定时可直接通过硬件定时器完成 定时器使用过程: 定时器接口函数介绍:
  • Linux提供的一个定时器接口。这个接口基于文件描述符,通过文件描述符的可读事件进行超时通知,所以能够被用于select/poll/epoll的应用场景。timerfd是linux内核2.6.25版本中加入的接口。 可以实现定时器的...
  • Linux定时器的讲解

    2017-02-09 20:23:54
    最强大的定时器接口来自POSIX时钟系列,其创建、初始化以及删除一个定时器的行动被分为三个不同的函数:timer_create()(创建定时器)、timer_settime()(初始化定时器)以及timer_delete(销毁它)。   创建一个...

空空如也

空空如也

1 2 3 4 5 ... 19
收藏数 361
精华内容 144
关键字:

linux定时器接口

linux 订阅