系统相关
首页 > 系统相关> > 当在另一个线程上更改pollfd时,Linux和OS X之间poll()的差异

当在另一个线程上更改pollfd时,Linux和OS X之间poll()的差异

作者:互联网

我试图在OS X上的多线程环境中运行libwebsockets.我无法触发从主服务线程以外的其他线程发送数据.在libwebsocket docs上暗示这应该是可能的(demo code,mailinglist).所以我挖掘代码并在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 X,Linux)中找不到任何有关它的详细信息.

解决方法:

您似乎首先说,您发现了一些声称支持和定义行为的文档.我很想知道你在哪里阅读,因为我无法在poll(2)的Linux手册页中找到任何内容,也没有在POSIX man page for poll()文档中找到不同的线程实际上可以更改事件数组参数中的值的另一个线程传递给poll(),并且不同线程的更改实际上在原始线程的poll()调用中生效,而不管与内存屏障有关的任何问题等等.

在这个主题上,对我来说,这两个手册页似乎都是完全沉默的.它们不表示这是预期的,支持的还是定义的行为;或者这不是受支持或定义的行为.

一个不同的线程可以将参数修改为另一个线程发出的系统调用的命题,之后 – 另一个线程已经进入系统调用之后,对我来说似乎是反向交叉的.如果这是受支持的行为,我希望它是明确记录的,我在Linux或POSIX手册页中找不到任何对它的引用.

话虽如此:即使我将我的软件范围限制在Linux,即使我不需要关心其他平台;由于没有任何这方面的文档,即使我的测试显示Linux内核以这种方式实现poll(2),我也不希望有任何保证未来某些内核版本将继续以这种方式运行.我不能依赖这种行为,除了我测试过的特定内核构建.

那么,回答你的问题:对这个主题具有权威性的唯一文档是有问题的手册页.他们没有明确将此视为合法行为;虽然他们也没有明确说这是非法行为,但由于上述原因,我认为这是不受支持的,未定义的行为.

标签:linux,multithreading,sockets,macos,libwebsockets
来源: https://codeday.me/bug/20190623/1272668.html