读取命名管道不会刷新它

时间:2017-04-18 07:15:58

标签: c++ linux named-pipes

这是我第一次使用unix命名管道的测试。下面是一个简单的例子,它试图每秒读取一个给定的管道,并在成功时,输出"触发"到stdout。使用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

有人可以告诉我这里我做错了吗?

Raspbian 8上的

g ++(Raspbian 4.9.2-10)

2 个答案:

答案 0 :(得分:1)

您的计划存在严重缺陷:

  • 文本形式的整数可能 大于int的大小。例如,文本"12345678"八个字节(不包括字符串终止符!),应该与4的{​​{1}}的通常大小进行比较。

  • 没有任何内容表明您阅读的数据将被终止。这意味着将您读取的数据视为零终止字符串的所有函数(例如sizeof(int))将无法可靠地运行。

  • 当管道另一端关闭时,atoi函数返回read。这意味着您尝试使用的数据并不存在。 0来电(返回read后)并未实际阅读任何内容。

  • 您有严重的资源泄漏,因为0管道一遍又一遍而不关闭它。

  • 您没有任何错误处理方式。

最后,您似乎一遍又一遍地读取相同的数据可能是因为编译器已经实现了open数组。它只是重用相同的内存。由于你只是一遍又一遍地循环,你会一遍又一遍地看到相同的数据,因为数组的位置是相同的,并且编译器(或你的程序)以任何方式清除了内存。管道被冲洗,你的记忆内容不是。

答案 1 :(得分:1)

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

错误时返回-1,流结束时返回零,或者表示已传输的字节数的正整数。

因此:

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

毫无意义。如果st 为正,则应该执行此块。如果为零,则应关闭管道并退出循环;如果为-1,则应调用perror(),关闭管道,然后退出循环。

目前您正在阅读直到流结束,然后永久打印您收到的最后一件事。

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