Reputation: 3854
This is my first test on using unix named pipes. Below is a simple example which tries to read a given pipe every second and, on success, outputs "triggered" to stdout. Using a bash script I can write that value to the pipe which is read by the program as expected.
The problem is, that the program keeps reading the value on every following loop although it is only written once to the pipe. Every tutorial I could find tells me, everything which is read from the pipe is basically removed and if one wants to flush a pipe simply read everything. But the program below keeps outputting the value over and over without new input.
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
If I compile the program, start it, and execute the script after a few cycles, I receive the output below
sample output
fifo0
fifo0
fifo0
fifo0
fifo0
fifo0
fifo1 -> triggered
fifo1 -> triggered
fifo1 -> triggered
fifo1 -> triggered
fifo1 -> triggered
fifo1 -> triggered
while I would expect the following output
desired output
fifo0
fifo0
fifo0
fifo0
fifo0
fifo0
fifo1 -> triggered
fifo0
fifo0
fifo0
fifo0
fifo0
Can somebody tell me what I am doing wrong here?
g++ (Raspbian 4.9.2-10) on Raspbian 8
Upvotes: 0
Views: 2928
Reputation: 310913
int st = read(fifo, temp, sizeof(temp));
This returns -1 on error, zero at end of stream, or a positive integer denoting the number of bytes that have been transferred.
Therefore this:
if(st == 0){
int res = atoi(temp);
std::cout << "fifo" << res;
if(res == 1){
std::cout << " -> triggered";
close(fifo);
}
std::cout << std::endl;
}
makes no sense. This block is what you should do if st
is positive. If it is zero you should close the pipe and exit the loop; if it is -1 you should call perror()
, close the pipe, and exit the loop.
At present you are reading until end of stream, then printing the last thing you received, forever.
You should also open the pipe prior to the loop. You can also get rid of the sleep()
. The read()
will block while no data is available.
Upvotes: 1
Reputation: 409176
You have some serious flaws in your program:
An integer in text form may be much larger than the size of an int
. For example the text "12345678"
is eight bytes (not including string terminator!) which should be compared to the usual size of 4
for sizeof(int)
.
Nothing says that the data you read will be zero-terminated. That means all functions that treat the data you read as a zero-terminated string (like e.g. atoi
) will not work reliably.
The read
function returns 0
when the pipe has been closed by the other end. Which means the data you attempt to use doesn't really exist. The read
call (after returning 0
) haven't actually read anything.
You have a serious resource leak, as you open
the pipe over and over again without closing it.
You have no error handling what so ever.
Finally, that you seem to be reading the same data over and over is probably because of how the compiler have implemented your temp
array. It simply reuses the same memory. Since you just continue to loop over and over again you will see the same data over and over again since the location of the array is the same and the compiler (or your program) have cleared the memory in any way in between. The pipe is flushed, your memory contents is not.
Upvotes: 1