body, table{font-family: 微软雅黑; font-size: 13.5pt}
table{border-collapse: collapse; border: solid gray; border-width: 2px 0 2px 0;}
th{border: 1px solid gray; padding: 4px; background-color: #DDD;}
td{border: 1px solid gray; padding: 4px;}
tr:nth-child(2n){background-color: #f8f8f8;}

timerfd是Linux提供的一个定时器接口。这个接口基于文件描述符,通过文件描述符的可读事件进行超时通知,所以能够被用于select/poll/epoll的应用场景。timerfd是linux内核2.6.25版本中加入的接口。
可以实现定时器的功能,将定时器抽象为文件描述符,当定时器到期时可以对其read,这样也可以放到监听队列的主循环中。timerfd有数据可读要把它读走,不然定时器失效
#include <sys/timerfd.h>
    int timerfd_create(int clockid, int flags);
    int timerfd_settime(int fd, int flags,
                        const struct itimerspec *new_value,
                        struct itimerspec *old_value);
timerfd_create

●功能:该函数生成一个定时器对象,返回与之关联的文件描述符。
●参数详解:
-clockid: 可设置为
       CLOCK_REALTIME:相对时间,从1970.1.1到目前的时间。更改系统时间会更改获取的值,它以系统时间为坐标。
       CLOCK_MONOTONIC:绝对时间,获取的时间为系统重启到现在的时间,更改系统时间对其没有影响。
-flags: 可设置为
       TFD_NONBLOCK(非阻塞),
       TFD_CLOEXEC(同O_CLOEXEC)
       linux内核2.6.26版本以上都指定为0
timerfd_settime

●功能:该函数能够启动和停止定时器
●参数详解:
-fd: timerfd对应的文件描述符
-flags:
      0表示是相对定时器
      TFD_TIMER_ABSTIME表示是绝对定时器
-new_value:设置超时时间,如果为0则表示停止定时器。
-old_value:
      一般设为NULL, 不为NULL,则返回定时器这次设置之前的超时时间
操作

●read:读取缓冲区中的数据,其占据的存储空间为sizeof(uint_64),表示超时次数。
    
●select/poll/epoll:当定时器超时时,会触发定时器相对应的文件描述符上的读操作,IO复用操作会返回,然后再去对该读事件进行处理。
struct itimerspec {
              struct timespec it_interval; /* Interval for periodic timer */间隔时间
              struct timespec it_value;    /* Initial expiration */初始到期时间
          };

struct timespec {
              time_t tv_sec;        /* Seconds */
              long  tv_nsec;        /* Nanoseconds */
          };

eventfd的主要是用于进程或者线程间通信(如通知/等待机制的实现)。
实现了线程之间事件通知的方式,eventfd的缓冲区大小是sizeof(uint64_t);向其write可以递增这个计数器,read操作可以读取,并进行清零;eventfd也可以放到监听队列中,当计数器不是0时,有可读事件发生,可以进行读取。
#include <sys/eventfd.h>
int eventfd(unsigned int initval, int flags);
参数解释:

●如果是2.6.26或之前版本的内核,flags 必须设置为0。
●initval:初始化计数器值,该值保存在内核.
●flags支持以下标志位:
  - EFD_NONBLOCK 类似于使用O_NONBLOCK标志设置文件描述符。
  - EFD_CLOEXEC  类似open以O_CLOEXEC标志打开,O_CLOEXEC 应该表示执行exec()时,之前通过open()打开的文件描述符会自动关闭.
●返回值:函数返回一个文件描述符,与打开的其他文件一样,可以进行读写操作。
操作:

●read:如果计数器A的值不为0时,读取成功,获得该值。如果A的值为0,非阻塞模式时,会直接返回失败,并把error置为EINVAL;如果为阻塞模式,一直会阻塞到A为非0为止。
●write:将缓冲区写入的8字节整形值加到内核计数器上,即会增加8字节的整数在计数器A上,如果其值达到0xfffffffffffffffe时,就会阻塞(在阻塞模式下),直到A的值被read。
write操作,写入的数据会加到计数器上,read操作读走计数器上的值后会把计数器置为0.
#include <poll.h>
    int poll(struct pollfd *fds, nfds_t nfds, int timeout);
fds      可以传递多个结构体,也就是说可以监测多个驱动设备所产生的事件,只要有一个产生了请求事件,就能立即返回
nfds     监测驱动文件的个数
timeout  超时时间,单位为ms -1永久等待,0立即返回

events:
POLLIN       有数据可读
POLLRDNORM   有普通数据可读,等效与POLLIN
POLLPRI      有紧迫数据可读
POLLOUT      写数据不会导致阻塞
POLLER       指定的文件描述符发生错误
POLLHUP      指定的文件描述符挂起事件
POLLNVAL     无效的请求,打不开指定的文件描述符
返回值:

有事件发生   返回revents域不为0的文件描述符个数(也就是说事件发生,或者错误报告)

超时        返回0;
失败      返回-1,并设置errno为错误类型
失败返回-1的错误码->EINTR 请求的事件之前产生一个信号,调用可以重新发起。
// #define EINTR 4   /* Interrupted system call */

struct pollfd {
               int   fd;      
/* file descriptor *//* 文件描述符 */
               short events;  
/* requested events *//* 请求的事件类型,监视驱动文件的事件掩码 */
               short revents; 
/* returned events *//* 驱动文件实际返回的事件 */
           };

poll机制就是给定一段时间,在这一段时间内程序处于睡眠状态一直等待某一个资源,它会在两种情况下返回
①时间到了.
②等到了资源.

等待期间将进程休眠,利用事件驱动来唤醒进程,将更能提高CPU的效率。
#include <stdlib.h>
int atoi(const char *nptr);
long atol(const char *nptr);
long long atoll(const char *nptr);
example:
eventfd.cpp
#include<iostream>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/eventfd.h>
#include<sys/poll.h>
#define handle_error(msg) \
           do{\
                   perror(msg);\
                   exit(EXIT_FAILURE);\
                }while(0)
using namespace std;
int main(int argc,char** argv)
{
        uint64_t u;
        int efd = eventfd(10,0);  // 计数器的初值为10,第二个参数写0
        //最新的进程/线程通信
        if(-1==efd)
        {
                handle_error("eventfd");
        }
        int ret = fork();
        if(0==ret)
        {
                for(int j=1;j<argc;++j)
                {
                        u = atoll(argv[j]);
                        cout<<"child writing "<<u<<" to efd"<<endl;
                        ssize_t s = write(efd,&u,sizeof(uint64_t));  // 会把写入的数据累加到计数器上
                        if(s!=sizeof(uint64_t))
                        {
                                handle_error("write");
                        }
                }
                cout<<"child completed write loop\n";
                exit(EXIT_SUCCESS);
        }
        else
        {
                //sleep(2);  // 等子进程创建好,并循环完毕
                for(int i=0;i<argc-1;++i)
                {
                        ssize_t s = read(efd,&u,sizeof(uint64_t));  // 读完计数器会自动清0
                        if(s!=sizeof(uint64_t))
                        {
                                handle_error("read");
                        }
                        cout<<"parent read "<<u<<" from efd\n";
                }
                exit(EXIT_SUCCESS);
        }
}
//父进程在睡眠2s的过程中,子进程已经完成了for循环,向内核的计数器上写了1,2,3;
//加上初值10,一共就是16
//child writing 1 to efd
//child writing 2 to efd
//child writing 3 to efd
//child completed write loop
//parent read 16 from efd
//父进程不睡眠,for循环读走所有输入
//父进程先于子进程运行,读走了计数器的值10,计数器此时值变为0,后面父进程读操作处于阻塞状态
//等到子进程写入1的时候唤醒父进程,父进程接着读。
//$>./a.out 1 2 3
//parent read 10 from efd
//child writing 1 to efd
//parent read 1 from efd
//child writing 2 to efd
//parent read 2 from efd
//$>child writing 3 to efd
// 结果这样是因为父进程循环次数,第一次来就读了一次,所有最后子进程写3的时候父进程已经退出了

eventfd+poll.cpp
#include<iostream>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/eventfd.h>
#include<sys/poll.h>
#include<sys/wait.h>
#define handle_error(msg) \
           do{\
                   perror(msg);\
                   exit(EXIT_FAILURE);\
                }while(0)
using namespace std;
int main(int argc,char** argv)
{
        uint64_t u;
        int efd = eventfd(0,0);  // 计数器的初值为0,第二个参数写0
        //最新的进程/线程通信
        if(-1==efd)
        {
                handle_error("eventfd");
        }
        int ret = fork();
        if(0==ret)
        {
                for(int j=1;j<argc;++j)
                {
                        u = atoll(argv[j]);
                        cout<<"child writing "<<u<<" to efd"<<endl;
                        ssize_t s = write(efd,&u,sizeof(uint64_t));
                        if(s!=sizeof(uint64_t))
                        {
                                handle_error("write");
                        }
                }
                cout<<"child completed write loop\n";
                //exit(EXIT_SUCCESS);
        }
        else
        {
                struct pollfd ppfd;
                ppfd.fd = efd;
                ppfd.events = POLLIN;
                for(int i=1;i<argc;++i)
                {
                        poll(&ppfd,1,-1);  // 事件在等待ppfd可读,等待过程中父进程睡眠(休眠) 
        // 事件发生就唤醒,接着向下执行
        // 没有这个读一次就会卡住
                        ssize_t s = read(efd,&u,sizeof(uint64_t));
                        if(s!=sizeof(uint64_t))
                        {
                                handle_error("read");
                        }
                        cout<<"parent read "<<u<<" from efd\n";
                }
                wait(NULL);
        }
}

//child writing 1 to efd
//parent read 1 from efd
//child writing 2 to efd
//parent read 2 from efd
//child writing 3 to efd
//parent read 3 from efd
//child completed write loop

timerfd.cpp
#include<iostream>
#include<sys/timerfd.h>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<time.h>
using namespace std;
int main()
{
        int timerfd = timerfd_create(CLOCK_REALTIME,0);
        if(-1==timerfd)
        {
                perror("timerfd_create");
                return -1;
        }
        struct itimerspec new_value;
        memset(&new_value,0,sizeof(itimerspec));
        new_value.it_value.tv_sec = 5; //初始到期时间
        new_value.it_interval.tv_sec = 3;  //间隔时间
        int ret = timerfd_settime(timerfd,0,&new_value,NULL);
        if(-1==ret)
        {
                perror("timerfd_settimer");
                return -1;
        }
        long int timeNum;
        time_t t;
        time(&t);  // 获取当前秒数
        // 读到的是个8位的整数
        cout<<ctime(&t)<<endl;
        while(ret = read(timerfd,&timeNum,8),time(&t),cout<<ctime(&t))  // 时间到,timerfd可读,要把数据读走,不然阻塞,定时器没法正常工作了
        {
                cout<<"timerfd read cnt "<<timeNum<<endl<<endl;
        }
}
//Fri Apr 20 11:12:22 2018
//
//Fri Apr 20 11:12:27 2018
//timerfd read cnt 1
//
//Fri Apr 20 11:12:30 2018
//timerfd read cnt 1
//
//Fri Apr 20 11:12:33 2018
//timerfd read cnt 1
//
//Fri Apr 20 11:12:36 2018
//timerfd read cnt 1
//
//Fri Apr 20 11:12:39 2018
//timerfd read cnt 1
//
//Fri Apr 20 11:12:42 2018
//timerfd read cnt 1
//
//^C

timerfd+poll.cpp
#include<iostream>
#include<time.h>
#include<poll.h>
#include<sys/timerfd.h>
#include<string.h>
#include<unistd.h>
using namespace std;
int main()
{
        int timerfd = timerfd_create(CLOCK_REALTIME,0);
        struct itimerspec new_value;
        memset(&new_value,0,sizeof(new_value));
        new_value.it_interval.tv_sec = 1;  //时间间隔
        new_value.it_value.tv_sec = 5;  //初始时间
        timerfd_settime(timerfd,0,&new_value,NULL);
        struct pollfd fd;
        bzero(&fd,sizeof(pollfd));
        fd.fd = timerfd;
        fd.events = POLLIN;
        time_t t;
        time(&t);
        cout<<ctime(&t)<<endl;
        while(1)
        {
                int ret = poll(&fd,1,-1);  // 永久等待时间发生
                if(ret>0)
                {
                        time(&t);
                        cout<<ctime(&t)<<endl;
                        long long int tmp;
                        read(timerfd,&tmp,sizeof(long long int));  // 读走数据,不然会一直触发
                }
                else
                {
                        return -1;
                }
        }
}
//Fri Apr 20 11:29:48 2018
//
//Fri Apr 20 11:29:53 2018
//
//Fri Apr 20 11:29:54 2018
//
//Fri Apr 20 11:29:55 2018
//
//Fri Apr 20 11:29:56 2018
//
//^C

Linux使用定时器timerfd 和 eventfd接口实现进程线程通信的更多相关文章

  1. 【linux】mkfifo 命令创建命名管道实现进程之间通信

    mkfifo 命令 mkfifo命令创建一个FIFO特殊文件,是一个命名管道(可以用来做进程之间通信的桥梁) 管道也是一种文件,一般是linux中的一个页大小,4k,管道数据一旦被读取就没了.(管道大 ...

  2. 关于linux的一点好奇心(五):进程线程的创建

    一直以来,进程和线程的区别,这种问题一般会被面试官拿来考考面试者,可见这事就不太简单.简单说一点差异是,进程拥有独立的内存资源信息,而线程则共享父进程的资源信息.也就是说线程不拥有内存资源,所以对系统 ...

  3. 进程 | 线程 | 当Linux多线程遭遇Linux多进程

    背景 本文并不是介绍Linux多进程多线程编程的科普文,如果希望系统学习Linux编程,可以看[<Unix环境高级编程>第3版] 本文是描述多进程多线程编程中遇到过的一个坑,并从内核角度分 ...

  4. linux新的API signalfd、timerfd、eventfd使用说明

    原文:http://www.cfanz.cn/?c=article&a=read&id=46555注意很多当前(2013/8/6)线上运营的Linux内核可能不支持! 三种新的fd加入 ...

  5. [转] linux新的API signalfd、timerfd、eventfd使用说明

    http://blog.csdn.net/gdutliuyun827/article/details/8460417 三种新的fd加入linux内核的的版本: signalfd:2.6.22 time ...

  6. linux下定时器介绍1

    POSIX Timer 间隔定时器 setitimer 有一些重要的缺点,POSIX Timer 对 setitimer 进行了增强,克服了 setitimer 的诸多问题: 首先,一个进程同一时刻只 ...

  7. Linux内核定时器struct timer_list

    1.前言 Linux内核中的定时器是一个很常用的功能,某些需要周期性处理的工作都需要用到定时器.在Linux内核中,使用定时器功能比较简单,需要提供定时器的超时时间和超时后需要执行的处理函数. 2.常 ...

  8. 芯灵思Sinlinx A64开发板Linux内核定时器编程

    开发平台 芯灵思Sinlinx A64 内存: 1GB 存储: 4GB 开发板详细参数 https://m.tb.cn/h.3wMaSKm 开发板交流群 641395230 Linux 内核定时器是内 ...

  9. 全志A33开发板Linux内核定时器编程

    开发平台 * 芯灵思SinlinxA33开发板 淘宝店铺: https://sinlinx.taobao.com/ 嵌入式linux 开发板交流 QQ:641395230 Linux 内核定时器是内核 ...

随机推荐

  1. Asp.net core 学习笔记 ( Configuration 配置 )

    参考 : https://cnblogs.com/nianming/p/7083964.html 配置写在 appsettings.json 里头 比如 { "object": { ...

  2. nodejs初识

    提到nodejs总离不开npm,因此首先要学些和了解npm.而对于npm.nodejs的了解都来源于菜鸟教程. nodejs学习地址:http://www.runoob.com/nodejs/node ...

  3. if标签

    If标签如果php中if语句的作用,if是用于流程控制的. 在ThinkPHP中if标签也是用于流程控制的. If标签的语法格式: <if condition=’条件表达式’> 输出结果1 ...

  4. Getting Started with Processing 第四章总结

    为什么要使用变量: 我们使用变量的一个重要原因就是避免变成过程中的重复工作,如果你重复使用某一个数字超过了一次,就可以考虑使用一个变量来代替它,这样你的程序会更加通用并且易于更新. 定义变量 定义变量 ...

  5. Python Web简单加法器的实现--Python

    坚持写博客来记录学习过程,哪怕学习的东西多么简单!下面是python中cgi相关知识. Template.py:(模板引擎文件) #模板引擎def start_response(resp=" ...

  6. Linux中sudo的用法

    一.用户在/etc/sudoers文件中的写法语法规则:授权用户 主机=命令动作 这三个要素缺一不可,但在动作之前也可以指定切换到特定用户下,在这里指定切换的用户要用括号括起来,如果不需要密码直接运行 ...

  7. Linux命令详解-file

    file命令用来识别文件类型,也可用来辨别一些文件的编码格式.它是通过查看文件的头部信息来获取文件类型,而不是像Windows通过扩展名来确定文件类型的. 1.命令格式: file [ -bchikL ...

  8. D - Power Tower欧拉降幂公式

    题意:给你一个数组a,q次查询,每次l,r,要求 \(a_{l}^{a_{l+1}}^{a_{l+2}}...{a_r}\) 题解:由欧拉降幂可知,最多log次eu(m)肯定变1,那么直接暴力即可,还 ...

  9. 整合多个网络的拓扑结构并降维(Mashup)

    整合多个网络的拓扑结构并降维(Mashup) 介绍一个整合多个网络拓扑结构的方法,方法来源:Compact Integration of Multi-Network Topology for Func ...

  10. 剑指offer-调整数组内奇偶数顺序

    题目描述 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变. 解题思路 时间换 ...