Linux程式間通訊-eventfd

haozhn發表於2018-04-17

eventfd是linux 2.6.22後系統提供的一個輕量級的程式間通訊的系統呼叫,eventfd通過一個程式間共享的64位計數器完成程式間通訊,這個計數器由在linux核心空間維護,使用者可以通過呼叫write方法向核心空間寫入一個64位的值,也可以呼叫read方法讀取這個值。

建立方法

#include <sys/eventfd.h>
int eventfd(unsigned int initval, int flags);
複製程式碼

建立的時候可以傳入一個計數器的初始值initval。 第二個引數flags在linux 2.6.26之前的版本是沒有使用的,必須初始化為0,在2.6.27之後的版本flag才被使用。

EFD_CLOEXEC(2.6.27~):

A copy of the file descriptor created by eventfd() is inherited by the child produced by fork(2). The duplicate file descriptor is associated with the same eventfd object. File descriptors created by eventfd() are preserved across execve(2), unless the close-on-exec flag has been set.

eventfd()會返回一個檔案描述符,如果該程式被fork的時候,這個檔案描述符也會複製過去,這時候就會有多個的檔案描述符指向同一個eventfd物件,如果設定了EFD_CLOEXEC標誌,在子程式執行exec的時候,會清除掉父程式的檔案描述符

EFD_NONBLOCK(2.6.27~): 就如它字面上的意思,如果沒有設定了這個標誌位,那read操作將會阻塞直到計數器中有值。如果沒有設定這個標誌位,計數器沒有值的時候也會立即返回-1;

EFD_SEMAPHORE(2.6.30~): 這個標誌位會影響read操作,具體可以看read方法中的解釋

提供的方法

read: 讀取計數器中的值

  • 如果計數器中的值大於0
    • 設定了EFD_SEMAPHORE標誌位,則返回1,且計數器中的值也減去1。
    • 沒有設定EFD_SEMAPHORE標誌位,則返回計數器中的值,且計數器置0。
  • 如果計數器中的值為0
    • 設定了EFD_NONBLOCK標誌位就直接返回-1。
    • 沒有設定EFD_NONBLOCK標誌位就會一直阻塞直到計數器中的值大於0。

write: 向計數器中寫入值

  • 如果寫入值的和小於0xFFFFFFFFFFFFFFFE,則寫入成功
  • 如果寫入值的和大於0xFFFFFFFFFFFFFFFE
    • 設定了EFD_NONBLOCK標誌位就直接返回-1。
    • 如果沒有設定EFD_NONBLOCK標誌位,則會一直阻塞知道read操作執行

close: 關閉檔案描述符

一個小Demo

#include <sys/eventfd.h>
#include <unistd.h>
#include <iostream>

int main() {
    int efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
    eventfd_write(efd, 2);
    eventfd_t count;
    eventfd_read(efd, &count);
    std::cout << count << std::endl;
    close(efd);
}
複製程式碼

上面的程式中我們建立了一個eventfd,並將它的檔案描述符儲存在efd中,然後呼叫eventfd_write向計數器中寫入數字2,然後呼叫eventfd_read讀取計數器中的值並列印處理,最後關閉eventfd。 執行結果:

count=2
複製程式碼

多次read和write

#include <sys/eventfd.h>
#include <unistd.h>
#include <iostream>

int main() {
    int efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
    eventfd_write(efd, 2);
    eventfd_write(efd, 3);
    eventfd_write(efd, 4);
    eventfd_t count;
    int read_result = eventfd_read(efd, &count);
    std::cout << "read_result=" << read_result << std::endl;
    std::cout << "count=" << count << std::endl;
    read_result = eventfd_read(efd, &count);
    std::cout << "read_result=" << read_result << std::endl;
    std::cout << "count=" << count << std::endl;
    close(efd);
}
複製程式碼

執行結果:

read_result=0
count=9
read_result=-1
count=9
複製程式碼

從執行結果我們可以看出當多次呼叫eventfd_write的時候,計數器一直在累加,但是eventfd_read只需呼叫一次就可以將計數器中的數取出來,如果再次呼叫eventfd_read將會返回失敗。

EFD_NONBLOCK標誌位的作用

#include <sys/eventfd.h>
#include <unistd.h>
#include <iostream>

int main() {
    int efd = eventfd(0, EFD_CLOEXEC);
    eventfd_write(efd, 2);
    eventfd_write(efd, 3);
    eventfd_write(efd, 4);
    eventfd_t count;
    int read_result = eventfd_read(efd, &count);
    std::cout << "read_result=" << read_result << std::endl;
    std::cout << "count=" << count << std::endl;
    read_result = eventfd_read(efd, &count);
    std::cout << "read_result=" << read_result << std::endl;
    std::cout << "count=" << count << std::endl;
    close(efd);
}
複製程式碼

執行結果:

read_result=0
count=9
複製程式碼

和前一個執行結果直接返回-1相比,如果去掉EFD_NONBLOCK標誌位,程式會在計數器沒有值的情況下一直阻塞在eventfd_read方法。

EFD_SEMAPHORE標誌位的作用

#include <sys/eventfd.h>
#include <unistd.h>
#include <iostream>

int main() {
    int efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC | EFD_SEMAPHORE);
    eventfd_write(efd, 2);
    eventfd_t count;
    int read_result = eventfd_read(efd, &count);
    std::cout << "read_result=" << read_result << std::endl;
    std::cout << "count=" << count << std::endl;
    read_result = eventfd_read(efd, &count);
    std::cout << "read_result=" << read_result << std::endl;
    std::cout << "count=" << count << std::endl;
    read_result = eventfd_read(efd, &count);
    std::cout << "read_result=" << read_result << std::endl;
    std::cout << "count=" << count << std::endl;
    close(efd);
}
複製程式碼

執行結果:

read_result=0
count=1
read_result=0
count=1
read_result=-1
count=1
複製程式碼

可以看到設定了EFD_SEMAPHORE後,每次讀取到的值都是1,且read後計數器也遞減1。

相關文章