簡體   English   中英

為什么EPOLLOUT會改變EPOLLIN的處理方式?

[英]Why EPOLLOUT changes how EPOLLIN is handled?

關於事件是否合並的文檔尚不清楚,我的測試顯示它們在某些情況下但並非總是如此。

考慮man 7 epoll

因為即使使用邊緣觸發的epoll,在收到多個數據塊時也可以生成多個事件,調用者可以選擇指定EPOLLONESHOT標志......

以及問答部分:

Q7如果epoll_wait(2)呼叫之間發生多個事件,它們是否合並或單獨報告?

A7他們將合並。

我假設手冊中的第一個語句意味着你可以在從套接字讀取,數據包到達,你讀取它,然后另一個數據包到達的情況下收到多個EPOLLIN事件。 問答部分的答案是討論EPOLLIN和EPOLLOUT等不同事件。 如果我錯了,請糾正我。

我正在玩一些代碼,以便更好地理解epoll如何工作,並且根據是否設置了另一個事件,它似乎在相同類型的事件上表現不同。 更確切地說,如果我只等待EPOLLIN,則多個輸入會生成單個事件,但如果我等待EPOLLIN和EPOLLOUT,則多個輸入會生成多個事件。

這是我用來測試這個的代碼:

#include <stdio.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char* argv[])
{
  struct epoll_event ev = {EPOLLIN|EPOLLOUT|EPOLLET};
  int epoll = epoll_create1(0);
  epoll_ctl(epoll, EPOLL_CTL_ADD, 0, &ev);

  //async stdin
  int flags = fcntl(0, F_GETFL);
  flags |= O_NONBLOCK;
  fcntl(0, F_SETFL, flags);

  while(1){
    struct epoll_event events[64];
    int n = epoll_wait(epoll, events, 64, -1);

    printf("Event count: %d\n", n);

    if(events[0].events == EPOLLIN)
      printf("EPOLLIN only\n\n");

    else
    if(events[0].events == (EPOLLIN|EPOLLOUT))
      printf("EPOLLIN and EPOLLOUT\n\n");

    else
      printf("EPOLLOUT only\n\n");

    char buffer[256];
    read(0, buffer, 256);

    sleep(1);
  }
  return 0;
}

按下返回后的輸出顯示接收到EPOLLIN和EPOLLOUT,此消息在按下返回時出現多次,然后它僅顯示正在生成的EPOLLOUT。

但是如果你在沒有EPOLLOUT標志的情況下編譯程序並多次按回車,則只會報告一次事件。

如果我刪除了read調用,則在設置EPOLLOUT時繼續報告EPOLLIN,但在僅設置EPOLLIN時不會報告。

行為取決於它正在等待的事件或我的測試代碼有什么問題嗎? 如果它是依賴的,我可以放心它將來不會改變嗎?

我相信您正在觀察未定義行為的影響,因為您濫用了API。

具體來說,您將STDIN_FILENO (即0 )傳遞給epoll_ctl並要求在EPOLLOUT上等待EPOLLOUT 可能發生的是操作系統試圖告訴您文件描述符的寫入方向有問題。

此外,使用邊緣觸發模式時,您應該繼續I / O,直到看到EAGAIN 當操作不再阻塞時, epoll_wait調用將返回。

我修改了你的程序以使用套接字,並從套接字讀取直到EAGAIN ,它的行為與我預期的一樣。

在我的版本中,我創建了一個套接字對,以及一個從STDIN_FILENO讀取並寫入該對套接字之一的線程。 main體循環然后epoll_wait那另一個插座上。

當我啟動程序時,它會在第一次調用epoll_wait返回報告可寫,但在下一次迭代時會阻塞:

 Event count: 1 EPOLLOUT only 

當我輸入輸入時,它報告可讀和可寫,然后按照預期在下一次迭代中在epoll_waitepoll_wait

 asdf Event count: 1 EPOLLIN and EPOLLOUT 

我使用的代碼如下。 一,線程:

static void * iothread (void *svp) {
    int *sv = svp;
    char buf[256];
    ssize_t r;
again:
    while ((r = read(0, buf, sizeof(buf))) > 0) {
        ssize_t n = r;
        const char *p = buf;
        while (n > 0) {
            r = write(sv[1], p, n);
            if (r < 0) {
                if (errno == EINTR) continue;
                break;
            }
            n -= r;
            p += r;
        }
        if (n > 0) break;
    }
    if (r < 0 && errno == EINTR) {
        goto again;
    }
    close(sv[1]);
    return NULL;
}

然后, main體:

int main(int argc, char* argv[]) {
  int sv[2];
  struct epoll_event ev = {EPOLLIN | EPOLLOUT | EPOLLET};
  int epoll = epoll_create1(0);
  pthread_t t;

  socketpair(AF_LOCAL, SOCK_STREAM, 0, sv);
  pthread_create(&t, NULL, iothread, sv);
  epoll_ctl(epoll, EPOLL_CTL_ADD, sv[0], &ev);
  while(1){
    struct epoll_event events[64];
    int n = epoll_wait(epoll, events, 64, -1);

    printf("Event count: %d\n", n);

    if(events[0].events == EPOLLIN)
      printf("EPOLLIN only\n\n");
    else
    if(events[0].events == (EPOLLIN|EPOLLOUT))
      printf("EPOLLIN and EPOLLOUT\n\n");
    else
      printf("EPOLLOUT only\n\n");

    char buffer[256];
    ssize_t r;
again:
    r = recv(sv[0], buffer, 256, MSG_DONTWAIT);
    if (r > 0) goto again;
    if (r < 0 && errno == EAGAIN) {
        sleep(1);
        continue;
    }
    break;
  }
  return 0;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM