C fifo一直阻塞

C fifo keeps blocked

我目前正在用 C 学习多线程,但是对于我们的命名管道 excersize 有一些我不太理解的东西。
我们应该做一个文件搜索系统的实现,用一个进程找到文件并添加到缓冲区,第二个进程应该从第一个进程的线程中获取文件名,在该文件中找到搜索查询和 returns 位置首先通过管道处理。我几乎完成了所有工作,但我对如何在两个进程之间进行通信感到困惑。

这是我进行通信的代码:
main.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 只是为了确保它能正常工作,所以就在这里

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 更改为打开模式,线程将被阻塞并且不打印任何响应。然后它打印 "Error opening pipe no such device or address" 错误,所以我知道我无法打开 minion 中的 recvPipe 但我不知道错误是什么

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

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

您的代码存在的问题之一是 apparent 对 execlp() 用法的误解。成功时,这个函数不会return,所以它后面的代码永远不会被执行。通常先 fork()s,然后在 child 过程中执行 execlp(),如果 execlp() 失败,一定要使 child 终止。 parent 进程最终可能还需要等待分叉的 child。

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

解决 execlp() 问题后,您还会遇到竞争条件。 parent 进程依赖于 child 来创建其中一个 FIFO,但不会等待该进程这样做。如果 parent 在 child 完成其 mkfifo().

之前达到其打开尝试,您将不会获得所需的行为

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

或者您可以使用普通(匿名)管道(参见 pipe())代替 FIFO。这些在两端都是开放的,它们对于继承相关的进程之间的通信更自然。

无论如何,一定要检查函数调用的 return 值。几乎所有这些函数都可能失败,预先检测和处理它比理清当您错误地假设每个调用都成功时可能形成的混乱要好得多。