关闭正在轮询的文件描述符

时间:2012-05-12 06:32:03

标签: linux pthreads file-descriptor

如果我有两个线程(Linux,NPTL),并且我有一个线程在一个或多个文件描述符上进行轮询,而另一个线程正在关闭其中一个,这是一个合理的操作吗?我在MT环境中做了一些我不该做的事情吗?

我考虑这样做的主要原因是,我不一定要与轮询线程通信,中断它等等,我反而只想关闭描述符,无论出于何种原因,以及何时轮询线程醒来,我希望revents包含POLLNVAL,这表明文件描述符应该在下次轮询之前被线程抛弃。

我已经组织了一个简单的测试,它确实表明POLLNVAL正是将要发生的事情。但是,在这种情况下,POLLNVAL仅在超时到期时设置,关闭套接字似乎不会使poll()返回。如果是这种情况,我可以终止该线程以使poll()重新启动以返回。

#define _GNU_SOURCE

#include <stdio.h>
#include <pthread.h>
#include <poll.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>

static pthread_t main_thread;

void * close_some(void*a) {

    printf("thread #2 (%d) is sleeping\n", getpid());
    sleep(2);
    close(0);
    printf("socket closed\n");
    // comment out the next line to not forcefully interrupt
    pthread_kill(main_thread, SIGUSR1);
    return 0;

}

void on_sig(int s) {
    printf("signal recieved\n");
}

int main(int argc, char ** argv) {

    pthread_t two;
    struct pollfd pfd;
    int rc;

    struct sigaction act;
    act.sa_handler = on_sig;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGUSR1, &act, 0);

    main_thread = pthread_self();

    pthread_create(&two, 0, close_some, 0);

    pfd.fd = 0;
    pfd.events = POLLIN | POLLRDHUP;

    printf("thread 0 (%d) polling\n", getpid());

    rc = poll(&pfd, 1, 7000);

    if (rc < 0) {
        printf("error : %s\n", strerror(errno));
    } else if (!rc) {
        printf("time out!\n");
    } else {
        printf("revents = %x\n", pfd.revents);
    }
    return 0;

}

1 个答案:

答案 0 :(得分:4)

至少对于Linux来说,这似乎有风险。 close的手册页警告:

  

关闭文件描述符可能是不明智的   在同一进程中的其他线程中使用系统调用。既然一个   文件描述符可以重用,有一些模糊的竞争条件   这可能会导致意想不到的副作用。

由于您使用的是Linux,因此可以执行以下操作:

  • 设置eventfd并将其添加到投票
  • 如果要关闭fd
  • ,请发信号通知eventfd(写入)
  • 在投票中,当您在eventfd上看到活动时,您可以立即关闭fd并将其从poll
  • 中移除

或者,您可以简单地建立signal处理程序,并在errno == EINTR返回时检查poll。信号处理程序只需要将一些全局变量设置为你正在关闭的fd的值。


由于您使用的是Linux,因此您可能需要将epoll视为poll的优质非标准替代方案。