zoukankan      html  css  js  c++  java
  • 线程间通信之eventfd

    man手册中的解释:
    eventfd()创建了一个“eventfd对象”, 通过它能够实现用户态程序间(我觉得这里主要指线程而非进程)的等待/通知机制,以及内核态向用户态通知的机制(未考证)。
    此对象包含了一个被内核所维护的计数(uint64_t), 初始值由initval来决定。


    int eventfd(unsigned int initval, int flags);创建一个eventfd文件描述符
    int eventfd_read(int fd, eventfd_t *value); 向eventfd中写入一个值
    int eventfd_write(int fd, eventfd_t value); 从eventfd中读出一个值

    例一、子线程多次写入多个值,主线程一次读出所有值的和

     1 #include <sys/eventfd.h>
     2 #include <unistd.h>
     3 #include <stdlib.h>
     4 #include <stdio.h>
     5 #include <stdint.h>  
     6 
     7 int main(int argc, char**argv[])
     8 {
     9     int efd, j;
    10     uint64_t u;
    11     ssize_t s;
    12     
    13     if (argc < 2)
    14     {
    15         printf("number of argc is wrong!
    ");
    16         return 0;
    17     }
    18     
    19     efd = eventfd(0,0);
    20     if (-1 == efd)
    21     {
    22         printf("failed to create eventfd
    ");
    23     }
    24     
    25     switch(fork())
    26     {
    27         case 0:
    28         {
    29             for(j=1; j<argc;j++)
    30             {
    31                 printf("child writing %s to efd
    ", argv[j]);
    32                 u = strtoull(argv[j], NULL, 0);
    33                 s = write(efd, &u, sizeof(uint64_t));
    34                 if (s!=sizeof(uint64_t))
    35                 {
    36                     printf("write efd failed
    ");
    37                 }
    38             }
    39             printf("Child completed write loop
    ");
    40             exit(0);
    41         }
    42         default:
    43             sleep(2);
    44             printf("Parents about to read
    ");
    45             s = read(efd, &u, sizeof(uint64_t));
    46             if (s != sizeof(uint64_t))
    47             {
    48                 printf("read efd failed
    ");
    49             }
    50             printf("Parents first read %llu (0x%llx) from efd
    ", u, u);
    51             exit(0);
    52         case -1:
    53         {
    54             printf("fork error
    ");
    55         }
    56     }
    57     
    58     return 0;
    59 }
    60 
    61 运行结果
    62 kane@kanelinux:/mnt/hgfs/kanelinuxshare/eventfd$ ./a.out 1 2 3 4
    63 child writing 1 to efd
    64 child writing 2 to efd
    65 child writing 3 to efd
    66 child writing 4 to efd
    67 Child completed write loop
    68 Parents about to read
    69 Parents first read 10 (0xa) from efd
    70 
    71 如果有写入操作,但是并没有导致初始值变化,则主线程会一直挂在read操作上
    72 kane@kanelinux:/mnt/hgfs/kanelinuxshare/eventfd$ ./a.out 0 0 0 0
    73 child writing 0 to efd
    74 child writing 0 to efd
    75 child writing 0 to efd
    76 child writing 0 to efd
    77 Child completed write loop
    78 Parents about to read
    79 ^C

    例二、eventfd可以被epoll监控, 一旦有状态变化,可以触发通知

      1 #include <sys/eventfd.h>
      2 #include <unistd.h>
      3 #include <stdlib.h>
      4 #include <stdio.h>
      5 #include <stdint.h>  
      6 #include <sys/epoll.h>  
      7 #include <string.h>  
      8 #include <pthread.h>  
      9 
     10 int g_iEvtfd = -1;
     11 
     12 void *eventfd_child_Task(void *pArg)
     13 {
     14     uint64_t uiWrite = 1;
     15     
     16     while(1)
     17     {
     18         sleep(2);
     19         if (0 != eventfd_write(g_iEvtfd, uiWrite))
     20         {
     21             printf("child write iEvtfd failed
    ");
     22         }    
     23     }
     24 
     25     return;
     26 }
     27 
     28 int main(int argc, char**argv[])
     29 {
     30     int iEvtfd, j;
     31     uint64_t uiWrite = 1;
     32     uint64_t uiRead;
     33     ssize_t s;
     34     int iEpfd;
     35     struct epoll_event stEvent;
     36     int iRet = 0;
     37     struct epoll_event stEpEvent;
     38     pthread_t stWthread;
     39     
     40     iEpfd = epoll_create(1);
     41     if (-1 == iEpfd)
     42     {
     43         printf("Create epoll failed.
    ");
     44         return 0;
     45     }
     46     
     47     iEvtfd = eventfd(0,0);
     48     if (-1 == iEvtfd)
     49     {
     50         printf("failed to create eventfd
    ");
     51         return 0;
     52     }
     53     
     54     g_iEvtfd = iEvtfd;
     55     
     56     memset(&stEvent, 0, sizeof(struct epoll_event));
     57     stEvent.events = (unsigned long) EPOLLIN;
     58     stEvent.data.fd = iEvtfd;
     59     iRet = epoll_ctl(iEpfd, EPOLL_CTL_ADD, g_iEvtfd, &stEvent);
     60     if (0 != iRet)
     61     {
     62         printf("failed to add iEvtfd to epoll
    ");
     63         close(g_iEvtfd);
     64         close(iEpfd);
     65         return 0;
     66     }
     67     
     68     iRet = pthread_create(&stWthread, NULL, eventfd_child_Task, NULL);
     69     if (0 != iRet)
     70     {
     71         close(g_iEvtfd);
     72         close(iEpfd);
     73         return;
     74     }
     75     
     76     for(;;)
     77     {
     78         iRet = epoll_wait(iEpfd, &stEpEvent, 1, -1);
     79         if (iRet > 0)
     80         {
     81             s = eventfd_read(iEvtfd, &uiRead);
     82             if (s != 0)
     83             {
     84                 printf("read iEvtfd failed
    ");
     85                 break;
     86             }
     87             printf("Read %llu (0x%llx) from iEvtfd
    ", uiRead, uiRead);
     88         }
     89     }
     90     
     91     close(g_iEvtfd);
     92     close(iEpfd);
     93     return 0;
     94 }
     95 运行结果
     96 kane@kanelinux:/mnt/hgfs/kanelinuxshare/eventfd$ ./a.out
     97 Read 1 (0x1) from iEvtfd
     98 Read 1 (0x1) from iEvtfd
     99 Read 1 (0x1) from iEvtfd
    100 Read 1 (0x1) from iEvtfd
    101 Read 1 (0x1) from iEvtfd
    102 Read 1 (0x1) from iEvtfd
    103 Read 1 (0x1) from iEvtfd
    104 Read 1 (0x1) from iEvtfd
    105 ^C

    例三、被epoll监控的eventfd,如果在子线程中被多次写入,在主线程中是怎么读的?

    #include <sys/eventfd.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <stdint.h>  
    #include <sys/epoll.h>  
    #include <string.h>  
    #include <pthread.h>  
    
    int g_iEvtfd = -1;
    
    void *eventfd_child_Task(void *pArg)
    {
        uint64_t uiWrite = 1;
        
            while(1)
            {
                sleep(2);
                eventfd_write(g_iEvtfd, uiWrite);
                eventfd_write(g_iEvtfd, uiWrite);
            }
    
        return;
    }
    
    int main(int argc, char**argv[])
    {
        int iEvtfd, j;
        uint64_t uiWrite = 1;
        uint64_t uiRead;
        ssize_t s;
        int iEpfd;
        struct epoll_event stEvent;
        int iRet = 0;
        struct epoll_event stEpEvent;
        pthread_t stWthread;
        
        iEpfd = epoll_create(1);
        if (-1 == iEpfd)
        {
            printf("Create epoll failed.
    ");
            return 0;
        }
        
        iEvtfd = eventfd(0,0);
        if (-1 == iEvtfd)
        {
            printf("failed to create eventfd
    ");
            return 0;
        }
        
        g_iEvtfd = iEvtfd;
        
        memset(&stEvent, 0, sizeof(struct epoll_event));
        stEvent.events = (unsigned long) EPOLLIN;
        stEvent.data.fd = iEvtfd;
        iRet = epoll_ctl(iEpfd, EPOLL_CTL_ADD, g_iEvtfd, &stEvent);
        if (0 != iRet)
        {
            printf("failed to add iEvtfd to epoll
    ");
            close(g_iEvtfd);
            close(iEpfd);
            return 0;
        }
        
        iRet = pthread_create(&stWthread, NULL, eventfd_child_Task, NULL);
        if (0 != iRet)
        {
            close(g_iEvtfd);
            close(iEpfd);
            return;
        }
        
        for(;;)
        {
            iRet = epoll_wait(iEpfd, &stEpEvent, 1, -1);
            if (iRet > 0)
            {
                s = eventfd_read(iEvtfd, &uiRead);
                if (s != 0)
                {
                    printf("read iEvtfd failed
    ");
                    break;
                }
                printf("Read %llu (0x%llx) from iEvtfd
    ", uiRead, uiRead);
            }
        }
        
        close(g_iEvtfd);
        close(iEpfd);
        return 0;
    }
    
    运行结果:
    kane@kanelinux:/mnt/hgfs/kanelinuxshare/eventfd$ ./a.out
    Read 1 (0x1) from iEvtfd
    Read 1 (0x1) from iEvtfd
    
    Read 1 (0x1) from iEvtfd
    Read 1 (0x1) from iEvtfd
    
    Read 1 (0x1) from iEvtfd
    Read 1 (0x1) from iEvtfd
    
    Read 1 (0x1) from iEvtfd
    Read 1 (0x1) from iEvtfd
    
    ^C

    例一中并没有epoll做监控,
    因此在read前,如果eventfd被写多次,在read的时候也是一次全部读出。

    注:eventfd中的SEMAPHORE标志用法

    * If EFD_SEMAPHORE was not specified and the eventfd counter
    has a nonzero value, then a read(2) returns 8 bytes contain‐
    ing that value, and the counter's value is reset to zero.

    * If EFD_SEMAPHORE was specified and the eventfd counter has a
    nonzero value, then a read(2) returns 8 bytes containing the
    value 1, and the counter's value is decremented by 1.

    通过测试发现。如果eventfd在创建的时候传入EFD_SEMAPHORE 标志,则会按上面man手册中提到的那样,每次在eventfd_read的时候只减一,并不是把值一次性全部读出。见下例 :

     1 #include <sys/eventfd.h>
     2 #include <unistd.h>
     3 #include <stdlib.h>
     4 #include <stdio.h>
     5 #include <stdint.h>  
     6 #include <sys/epoll.h>  
     7 #include <string.h>  
     8 #include <pthread.h>  
     9 
    10 int g_iEvtfd = -1;
    11 
    12 void *eventfd_child_Task(void *pArg)
    13 {
    14     uint64_t uiWrite = 1;
    15     int i=10;
    16 
    17     while(i>0)
    18     {
    19         eventfd_write(g_iEvtfd, uiWrite);
    20         i--;
    21     }
    22     
    23 
    24     return;
    25 }
    26 
    27 int main(int argc, char**argv[])
    28 {
    29     int iEvtfd, j;
    30     uint64_t uiWrite = 1;
    31     uint64_t uiRead;
    32     ssize_t s;
    33     int iEpfd;
    34     struct epoll_event stEvent;
    35     int iRet = 0;
    36     struct epoll_event stEpEvent;
    37     pthread_t stWthread;
    38     
    39     iEpfd = epoll_create(1);
    40     if (-1 == iEpfd)
    41     {
    42         printf("Create epoll failed.
    ");
    43         return 0;
    44     }
    45     
    46     iEvtfd = eventfd(0,EFD_SEMAPHORE);
    47     if (-1 == iEvtfd)
    48     {
    49         printf("failed to create eventfd
    ");
    50         return 0;
    51     }
    52     
    53     g_iEvtfd = iEvtfd;
    54     
    55     memset(&stEvent, 0, sizeof(struct epoll_event));
    56     stEvent.events = (unsigned long) EPOLLIN;
    57     stEvent.data.fd = iEvtfd;
    58     iRet = epoll_ctl(iEpfd, EPOLL_CTL_ADD, g_iEvtfd, &stEvent);
    59     if (0 != iRet)
    60     {
    61         printf("failed to add iEvtfd to epoll
    ");
    62         close(g_iEvtfd);
    63         close(iEpfd);
    64         return 0;
    65     }
    66     
    67     iRet = pthread_create(&stWthread, NULL, eventfd_child_Task, NULL);
    68     if (0 != iRet)
    69     {
    70         close(g_iEvtfd);
    71         close(iEpfd);
    72         return;
    73     }
    74     
    75     for(;;)
    76     {
    77         sleep(2);
    78         iRet = epoll_wait(iEpfd, &stEpEvent, 1, -1);
    79         if (iRet > 0)
    80         {
    81             s = eventfd_read(iEvtfd, &uiRead);
    82             if (s != 0)
    83             {
    84                 printf("read iEvtfd failed
    ");
    85                 break;
    86             }
    87             printf("Read %llu (0x%llx) from iEvtfd
    ", uiRead, uiRead);
    88         }
    89     }
    90     
    91     close(g_iEvtfd);
    92     close(iEpfd);
    93     return 0;
    94 }
    eventfd_SEMAPHORE
  • 相关阅读:
    面试问烂的 MySQL 四种隔离级别,看完吊打面试官!
    一周 GitHub 开源项目推荐:阿里、腾讯、陌陌、bilibili……
    干货收藏 | Java 程序员必备的一些流程图
    IntelliJ IDEA 快捷键终极大全,速度收藏!
    我的天!史上最烂的项目:苦撑 12 年,600 多万行代码...
    模板中如何添加不定个数的常数
    SFINAE简单实例
    Sequentially-consistent ordering
    hierarchical_mutex函数问题(C++ Concurrent in Action)
    不同AI学科之间的联系
  • 原文地址:https://www.cnblogs.com/zhengchunhao/p/5335914.html
Copyright © 2011-2022 走看看