这是我第一次使用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)
答案 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()
将在没有数据可用时阻止。