Linux中带有NONBLOCK模式的命名管道+ SELECT

Named pipe + SELECT with NONBLOCK mode in Linux

本文关键字:管道 SELECT 模式 NONBLOCK Linux      更新时间:2023-10-16

我试图用O_NONBLOCK模式创建命名管道,并在单独的线程中使用"SELECT"方法侦听读取事件。有一个问题,当我试图在主线程一些睡眠时间后关闭程序。我期望当使用close方法关闭命名管道的文件描述符时,选择操作应该立即停止并返回一些值。但不幸的是,当文件描述符关闭时,选择操作没有反应,执行选择方法的线程只是挂起…

有办法解决吗?示例代码如下:

#include <pthread.h>
#include <limits.h>
#include <cctype>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sys/shm.h>
#include <sys/time.h>
#include <sys/timeb.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <exception>
#define BUFFER PIPE_BUF
#define LPVOID void *
#define BOOL int
#define TRUE 1
#define CONST const
#define CHAR char
class CPipeTest
{
    public:
    int fd;
    int nfd;
    fd_set rfd;
    pthread_t t;
    CPipeTest() {};
    ~CPipeTest() {};
    static LPVOID fnExecuteThread(LPVOID lpParam)
    {
        ((CPipeTest*)lpParam)->fnRunThread();
        return NULL;
    }
    BOOL fnRunThread()
    {
        printf("Going to listen...rn");
        select(nfd, &rfd, NULL, NULL, NULL);
        printf("Close listener...rn");
        return TRUE;
    }
    void fnInit()
    {
        CONST CHAR * name = "./test_fifo1";
        mkfifo(name, 0777);
        fd = open(name, O_NONBLOCK | O_RDONLY);
        nfd = fd + 1;
        FD_ZERO(&rfd);
        FD_SET(fd, &rfd);
        pthread_create( &t, NULL, fnExecuteThread, (LPVOID)this);
        sleep(30);
        printf("Close file descriptor - listener should be closed automatically and immediatelyrn");
        close(fd);
        printf("Descriptor closed wait for thread to to be closedrn");
        pthread_join(t, NULL);
        printf("Thread is closed - everything is finern");
    }
};
int main()
{
    CPipeTest pi;
    pi.fnInit();
    return 0;
}

应该有两个文件描述符,一个用于读取,另一个用于写入。

对于阻塞模式,写线程(在初始线程中关闭)应该在初始线程中打开,在启动"reader"线程之后打开。reader线程中打开读取的。对于非阻塞模式,可以在同一个线程中完成,如果首先打开读取,然后写入(或者ENXIO将返回打开没有读取器的writer)。

当您关闭写侧时,读侧将收到select通知。(如果有一个真正的数据交换,下面的read将读取零字节,这就是你如何检测EOF)。

如果您切换到匿名管道,您将自动从pipe调用获得一对描述符。

尽量避免在一个线程中写入一个变量,而在另一个线程中读取它。

我说的避免是指不要那样做。:)

fnRunThread使用的每个变量只能被该函数访问,除非同步访问它。

这里有人在做你做过的事:如果在一个单独的线程中关闭(2)一个文件描述符,select(2)会做什么?并指出了未定义行为。

解决这个问题的一个好方法是有一个"停止阅读"命令,你可以通过你的fifo。当读取线程获得它时,它继续停止读取,然后关闭文件。(注意,读线程关闭文件句柄——如果读线程正在从文件句柄中读取,那就意味着它拥有文件句柄。除非有一些额外的同步,一旦启动线程,您应该只读取或写入线程内线程拥有的变量,而不应该读取或写入线程外的这些变量)。

问题是FIFO(一种特殊类型的文件)必须在两个结束时打开,然后才能用它做任何事情。这是没有意义的,你关闭相同的一端,并期望其他线程作出反应。

下面的代码应该做你想做的:

    ....
    nfd = fd + 1;
    FD_ZERO(&rfd);
    FD_SET(fd, &rfd);
    int write_fd = open(name, O_WRONLY);   // open the other end
    pthread_create( &t, NULL, fnExecuteThread, (LPVOID)this);
    sleep(30);
    printf("Close file descriptor - listener should be closed automatically and immediatelyrn");
    close(write_fd);                       // close the other end
    ....