命名管道上的读取操作不会阻塞

13

我有以下一段C代码,它从一个管道中读取,然后应该阻塞,但它从未阻塞过。

int pipe_fd;
int res;
int open_mode = O_RDONLY;
char buf[100];
int bytes_read = 0;

memset (buf, '\0', sizeof(buf));
pipe_fd = open(FIFO_NAME, open_mode);

if (access(FIFO_NAME, F_OK) == -1)
{
    res = mkfifo(FIFO_NAME, 0777);
    if (res != 0)
    {
            fprintf (stderr, "Could not create fifo %s\n", FIFO_NAME);  
            exit (EXIT_FAILURE);
    }
}

for(;;)
{        
    do     
    {     
        res = read(pipe_fd, buf, sizeof(buf));
        bytes_read += res;
    }while (res > 0);

    // process data then go back and block
    ............
}

在bash脚本中,通过类似'./test 1'的代码向服务器发送一个简单的缓冲区。

#!/bin/bash

pipe=/tmp/pipe

if [[ ! -p $pipe ]]; then
    echo "Reader not running"
    exit 1
fi

if [[ "$1" ]]; then
     echo "some string" >$pipe
else
     echo "q" >$pipe
fi

我在gdb中运行C代码程序,一开始它会因为read而阻塞,但是只要我调用bash脚本,C代码就不再阻塞了,并且成功从缓冲区读取数据,但每次读取时都读取了0字节,所以不确定为什么它不再阻塞。 "某个字符串" 数据在另一端被正确接收。

我只需要让它等待数据处理并返回等待更多数据。

3个回答

18
我在gdb中运行C语言程序,初始时它会在读取时阻塞,但是一旦我调用bash脚本,C代码就不再阻塞,成功从缓冲区中读取数据,但每次读取时都会返回0字节,所以不确定为什么它不再阻塞。在另一端正确接收了“某些字符串”数据。0表示EOF。FIFO只有在有进程同时进行读写操作时才能进行读取或写入。当没有更多的写入者(您的shell脚本已终止)时,读取者通过返回EOF来得知这点。FIFO的行为方式是为了与shell管道逻辑兼容,例如:
$ mkfifo ./tmp1
$ cat < input > ./tmp1 &
$ cat < ./tmp1 > /dev/null

如果read()不返回EOF,第二个cat将会一直阻塞。

我只需要它等待数据处理完毕,然后再返回并继续等待更多数据。

在您的C程序中,您需要在read()第一次返回EOF后重新open() FIFO。

附注:在这里发现了一个非常好的FIFO总结,请查看第二页上的表格。


非常感谢,我没有意识到。在重新打开FIFO之前,在接收EOF后应该关闭它吗? - tech74
@tech74: 显然,open() 会分配新的文件描述符 - 旧的必须使用 close() 释放。即使是写者在重新打开FIFO之前也会被阻塞。 - Dummy00001

0

我认为编写右侧shell脚本时每次在输出内容后都会关闭管道。

因此,编写脚本需要打开管道并重复使用已打开的描述符来写入内容,最后关闭已打开的描述符。


0

你的 Bash 脚本关闭了管道,因此 C 会收到“eof”条件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接