读取命名管道不会刷新它

Reading named pipe does not flush it

这是我第一次使用 unix 命名管道进行测试。下面是一个简单的示例,它尝试每秒读取给定的管道,并在成功时将 "triggered" 输出到标准输出。使用 bash 脚本,我可以将该值写入程序按预期读取的管道。

问题是,尽管只将值写入管道一次,但程序会在接下来的每个循环中继续读取该值。我能找到的每个教程都告诉我,从管道读取的所有内容基本上都被删除了,如果想要刷新管道,只需阅读所有内容即可。但是下面的程序在没有新输入的情况下不断输出值。

main.cpp

// std
#include <iostream>
// unix
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>


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

    // create named pipe
    mkfifo("/tmp/alarm_motion", 0666);

    for(;;){

        int fifo = open("/tmp/alarm_motion", O_RDONLY | O_NONBLOCK);
        char temp[sizeof(int)];
        int st = read(fifo, temp, sizeof(temp));
        if(st == 0){
            int res = atoi(temp);
            std::cout << "fifo" << res;
            if(res == 1){
                std::cout << " -> triggered";
                    close(fifo);
            }
            std::cout << std::endl;
        }
        sleep(1);
    }
}

test.sh

#!/bin/bash
pipe=/tmp/alarm_motion
echo 1 > $pipe

如果我编译程序,启动它,并在几个周期后执行脚本,我会收到下面的输出

示例输出

fifo0
fifo0
fifo0
fifo0
fifo0
fifo0
fifo1 -> triggered
fifo1 -> triggered
fifo1 -> triggered
fifo1 -> triggered
fifo1 -> triggered
fifo1 -> triggered

虽然我希望得到以下输出

期望输出

fifo0
fifo0
fifo0
fifo0
fifo0
fifo0
fifo1 -> triggered
fifo0
fifo0
fifo0
fifo0
fifo0

有人能告诉我我做错了什么吗?

g++ (Raspbian 4.9.2-10) Raspbian 8

你的程序有一些严重的缺陷:

  • 文本形式的整数可能比 int 的大小 很多。例如,文本 "12345678"8 字节(不包括字符串终止符!),这应该与 sizeof(int)4 的通常大小进行比较。

  • 没有人说你读取的数据会以零结尾。这意味着所有将您读取的数据视为以零结尾的字符串(例如 atoi)的函数都不会可靠地工作。

  • 当管道被另一端关闭时,read函数returns0。这意味着您尝试使用的数据并不真正存在。 read 调用(返回 0 后)实际上没有读取任何内容。

  • 你有一个严重的资源泄漏,因为你 open 管道一遍又一遍而没有关闭它。

  • 你处理任何事情都没有错误。

最后,您似乎一遍又一遍地读取相同的数据可能是因为编译器如何实现您的 temp 数组。它只是重复使用相同的内存。因为你只是继续一遍又一遍地循环,你会一遍又一遍地看到相同的数据,因为数组的位置是相同的,并且编译器(或你的程序)已经以任何方式清除了两者之间的内存。管道被冲洗了,你的内存内容没有。

int st = read(fifo, temp, sizeof(temp));

此 returns -1 错误,流末尾为零,或正整数表示已传输的字节数。

因此:

    if(st == 0){
        int res = atoi(temp);
        std::cout << "fifo" << res;
        if(res == 1){
            std::cout << " -> triggered";
                close(fifo);
        }
        std::cout << std::endl;
    }

没有意义。如果 stpositive,这个块就是你应该做的。 如果它是零,你应该关闭管道并退出循环;如果它是 -1 你应该调用 perror(),关闭管道,然后退出循环。

目前您正在阅读直到流结束,然后打印您收到的最后一个东西,永远。

您还应该在循环之前打开管道。您也可以去掉 sleep()read() 将在没有可用数据时阻塞。