簡體   English   中英

當在另一個線程上更改pollfd時,Linux和OS X之間poll()的差異

[英]Differences in poll() between Linux and OS X when pollfd is changed on another thread

我試圖在OS X上的多線程環境中運行libwebsockets。我無法觸發從主服務線程以外的其他線程發送數據。 在libwebsocket docs上暗示這應該是可能的( 演示代碼郵件列表 )。 所以我挖掘代碼並在poll()函數中發現了問題。

似乎poll()在作為參數給出的struct pollfd方面表現不同。 當poll()處於活動狀態時,libwebsockets依賴於更改fds.event字段的可能性。 這在Linux上工作正常,但不適用於OS X.

我寫了一個小測試程序來演示行為:

#include <unistd.h>
#include <netdb.h>
#include <poll.h>
#include <iostream>
#include <thread>

#define PORT "3490"

struct pollfd    fds[1];
bool connected = false;

void main_loop() {
    int sockfd, new_fd; 
    struct addrinfo hints, *servinfo, *p;
    socklen_t sin_size;
    int yes=1;
    char s[INET6_ADDRSTRLEN];
    int rv;

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE; 

    if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return;
    }

    for(p = servinfo; p != NULL; p = p->ai_next) {
        if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
            perror("server: socket");
            continue;
        }

        if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
            perror("setsockopt");
            exit(1);
        }

        if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
            close(sockfd);
            perror("server: bind");
            continue;
        }

        break;
    }

    freeaddrinfo(servinfo);

    if (p == NULL)  {
        fprintf(stderr, "server: failed to bind\n");
        exit(1);
    }

    if (listen(sockfd, 10) == -1) {
        perror("listen");
        exit(1);
    }

    printf("server: waiting for connections...\n");

    new_fd = accept(sockfd, NULL, &sin_size);
    if (new_fd == -1) {
        perror("accept");
        return;
    }

    fds[0].fd = new_fd;
    fds[0].events = POLLIN;
    connected = true;

    printf("event is %i\n", fds[0].events);
    int ret = poll(fds, 1, 5000);
    printf("event is %i\n", fds[0].events); //expecting 1 on Mac and 5 on Linux

    if (send(new_fd, "Hello, world!\n", 14, 0) == -1)
        perror("send");

    close(new_fd); 
    close(sockfd);
}

void second_thread()
{
    while(connected == false){}
    sleep(1);
    fds[0].events = POLLIN|POLLOUT;
    printf("set event to %i\n", fds[0].events);
}

int main() {

    std::thread t1(main_loop);
    std::thread t2(second_thread);

    t1.join();
    t2.join();

    return 0;
}

使用clang++ -std=c++11 -stdlib=libc++ -o poll poll.cpp在OS X上編譯,在Linux上使用g++ -std=c++11 -pthread -o poll poll.cpp

程序開始偵聽端口3490.如果連接到它(例如使用netcat localhost 3490 ),它將輪詢主線程上的輸入並嘗試更改第二個線程中的事件標志。 它將在5秒后退出。

OS X上的輸出:

server: waiting for connections...
event is 1
set event to 5
event is 1

Linux上的輸出:

server: waiting for connections...
event is 1
set event to 5
event is 5

所以我的問題是:有沒有可用的文檔來解釋這種行為? libwebsockets正在做什么,期望在輪詢活動時更改fds.events是合法的? 我在聯機幫助頁( OS XLinux )中找不到任何有關它的詳細信息。

您似乎首先說,您發現了一些聲稱支持和定義行為的文檔。 我很想知道你在哪里閱讀,因為我無法在poll(2)的Linux手冊頁中找到任何內容,也沒有在poll()POSIX手冊頁中找到任何文件,這些文件表明不同的線程實際上可以更改另一個線程傳遞給poll()的事件數組參數中的值,並且具有不同線程的更改實際上在原始線程的poll()調用中生效,而不管與內存屏障有關的任何問題等等。

在這個主題上,對我來說,這兩個手冊頁似乎都是完全沉默的。 它們不表示這是預期的,支持的還是定義的行為; 或者這不是受支持或定義的行為。

即不同的線程可以修改參數由另一個線程發出一個系統調用的命題, - -另一個線程已經進入系統調用,似乎相當反intertuitive給我。 如果這是受支持的行為,我希望它是明確記錄的,我在Linux或POSIX手冊頁中找不到任何對它的引用。

話雖如此:即使我將我的軟件范圍限制在Linux,即使我不需要關心其他平台; 由於沒有任何這方面的文檔,即使我的測試顯示Linux內核以這種方式實現poll(2),我也不希望有任何保證未來某些內核版本將繼續以這種方式運行。 我不能依賴這種行為,除了我測試過的特定內核構建。

那么,回答你的問題:對這個主題具有權威性的唯一文檔是有問題的手冊頁。 他們沒有明確將此視為合法行為; 雖然他們也沒有明確說這是非法行為,但由於上述原因,我認為這是不受支持的,未定義的行為。

暫無
暫無

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

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