C fifo一直被阻塞

Posted

技术标签:

【中文标题】C fifo一直被阻塞【英文标题】:C fifo keeps blocked 【发布时间】:2017-10-05 02:05:44 【问题描述】:

我目前正在研究 C 的多线程,但对于我们的命名管道 excersize,我不太了解。 我们期望执行文件搜索系统的实现,该系统使用一个进程查找文件并添加到缓冲区,第二个进程应该从第一个线程的线程中获取文件名,在该文件中找到搜索查询并将位置返回给第一个进程通过管道。我做了几乎所有的事情,但我很困惑如何在两个进程之间进行通信。

这是我进行通信的代码:ma​​in.c

void *controller_thread(void *arg) 
    pthread_mutex_lock(&index_mutex);
    int index = t_index++; /*Get an index to thread*/
    pthread_mutex_unlock(&index_mutex);
    char sendPipe[10];
    char recvPipe[10];
    int fdsend, fdrecv;
    sprintf(sendPipe, "contrl%d", (index+1));
    sprintf(recvPipe, "minion%d", (index+1));
    mkfifo(sendPipe, 0666);
    execlp("minion", "minion", sendPipe, recvPipe, (char*) NULL);
    if((fdsend = open(sendPipe, O_WRONLY|O_CREAT)) < 0)
        perror("Error opening pipe");
    if((fdrecv = open(recvPipe, O_RDONLY)) < 0)
        perror("Error opening pipe");
    while(1) 
        char *fileName = pop(); /*Counting semaphore from buffer*/
        if(notFile(fileName))
            break;
        write(fdsend, fileName, strlen(fileName));
        write(fdsend, search, strlen(search));
        char place[10];
        while(1) 
            read(fdrecv, place, 10);
            if(notPlace(place)) /*Only checks if all numeric*/
                break;
            printf("Minion %d searching %s in %s, found at %s\n", index, 
                    search, fileName, place);
        
    

从我找到的网上资源来看,我认为这是在main里面处理fifo的方法。我试图编写一个测试奴才只是为了确保它可以工作,所以在这里

minion.c

int main(int argc, char **argv) 
    char *recvPipe = argv[1];
    char *sendPipe = argv[2];
    char fileName[100];
    int fdsend, fdrecv;
    return 0;
    fdrecv = open(recvPipe, O_RDONLY);
    mkfifo(sendPipe, 0666);
    fdsend = open(sendPipe, O_WRONLY|O_CREAT);
    while(1) 
        read(fdrecv, fileName, 100);
        write(fdsend, "12345", 6);
        write(fds, "xxx", 4);
    
    return 0;

当我以这种方式运行时,如果我将 O_NONBLOCK 更改为打开模式,线程会被阻塞并且不打印任何响应。然后它打印“错误打开管道没有这样的设备或地址”错误,所以我知道我无法在 minion 中打开 recvPipe 但我不知道是什么错误

【问题讨论】:

【参考方案1】:

Fifos 需要在开放时间进行一些同步。默认情况下,open(s) 是阻塞的,因此读取的打开被阻塞,直到其他人打开相同的 fifo 进行写入,反之亦然(这让对等方同步进行通信)。您可以使用O_NONBLOCK 在没有实际打开对等方的情况下打开读取,但反过来是错误的,因为在没有读取对等方的情况下打开写入会导致错误(让进程在没有读取方的情况下尝试写入被认为是无意义的)。

例如,您可以阅读Linux Fifo manual entry。

【讨论】:

我不这么认为,我理解你的答案是正确的,所以你建议我对 minion.c 做fdrecv = open(recvPipe, O_RDONLY|O_NONBLOCK); 吗?【参考方案2】:

您的代码存在的问题之一是对execlp() 的使用存在明显的误解。成功后,这个函数不会返回,所以它后面的代码永远不会被执行。通常是先fork()s,然后在子进程中执行execlp(),如果execlp() 失败,肯定会让子进程终止。父进程可能最终也需要等待分叉的子进程。

此外,每个进程在尝试打开 FIFO 的写入端时都会传递O_CREAT 标志,这很奇怪,而且可能是不可取的。这应该是不必要的,因为每个人都刚刚用mkfifo() 创建了FIFO。即使mkfifo() 失败或其他进程在打开它之前将其删除,您也不希望使用O_CREAT 打开它,因为那样会得到一个常规文件,而不是FIFO。

一旦您解决了execlp() 问题,您还会遇到竞争条件。父进程依赖子进程创建 FIFO 之一,但不等待该进程这样做。如果父级在子级完成其mkfifo() 之前达到其打开尝试,您将不会获得所需的行为。

我建议在创建子进程之前让父进程创建两个 FIFO。孩子和父母必须合作,先打开一个FIFO的两端,然后再打开另一个FIFO的两端。一个打开读取将阻塞,直到另一个打开相同的 FIFO 写入。

或者您可以使用普通(匿名)管道(请参阅pipe())而不是 FIFO。它们在两端都是开放的,它们更自然地用于通过继承相关的进程之间的通信。

无论如何,一定要检查你的函数调用的返回值。几乎所有这些函数都可能失败,最好提前检测并处理它,而不是理清当您错误地假设每个调用都成功时可能形成的混乱。

【讨论】:

感谢您的详细回答,让我了解了 exec 的问题部分。所以对于匿名管道部分,我可以通过 exec 参数传递管道吗?我不认为这是可能的,因为所有参数都转换为 char *

以上是关于C fifo一直被阻塞的主要内容,如果未能解决你的问题,请参考以下文章

POSIX FIFO 在阻塞模式下打开时冻结

Lua 对空 FIFO 的非阻塞读访问

Java 阻塞和同步队列

Linux 非阻塞 fifo(按需日志记录)

read() 不会阻塞在没有 O_NONBLOCK 标志的情况下打开的空 FIFO

PHP中管道的非阻塞打开