Reputation: 105
I have built a IPC prgram with FIFO(named piped).
A very interesting problem is that my written message may be lost. the following is the code snippet.
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
int main()
{
char buffer[2000] = {0};
strcpy(buffer, "abc");
char *write_path = "test-123";
mkfifo(write_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
int dummy = open(write_path, O_RDONLY | O_NONBLOCK);
int fd = open(write_path, O_WRONLY | O_NONBLOCK);
int bytes = write(fd, buffer, 2000);
printf("write_path:%d %s %d %d\n", bytes, write_path, dummy, fd);
close(fd);
close(dummy);
}
how to reproduce?
ubuntu 1804
gcc main.c -o main
./main
cat < test-123
it will be pending. I think, it should output abc.
Upvotes: 0
Views: 390
Reputation: 101
You open the FIFO in nonblocking mode, which in means I/O functions can fail with (-1 and errno set to) EAGAIN or EWOULDBLOCK, instead of blocking (waiting) for the function to complete. In Linux, open descriptors to a FIFO have the same semantics as pipes.
If you bothered to check for errors (printing strerror(errno)
whenever open() or write() returns -1 indicating an error), you'd already know the reason why this fails. As is, the code shown does not even reproduce the problem. The following code,
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
int main(int argc, char *argv[])
{
const char *msg = "The Example Message.\n";
const size_t msg_len = strlen(msg);
int rfd, wfd;
if (argc != 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
const char *mypath = (argc > 0 && argv && argv[0] && argv[0][0]) ? argv[0] : "(this)";
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", mypath);
fprintf(stderr, " %s FIFO\n", mypath);
fprintf(stderr, "\n");
fprintf(stderr, "This program tests opening and writing a short message to the FIFO.\n");
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
rfd = open(argv[1], O_RDONLY | O_NONBLOCK);
if (rfd == -1) {
fprintf(stderr, "%s: Cannot open FIFO for reading: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
}
wfd = open(argv[1], O_WRONLY | O_NONBLOCK);
if (wfd == -1) {
fprintf(stderr, "%s: Cannot open FIFO for writing: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
}
ssize_t n = write(wfd, msg, msg_len);
if (n == -1) {
fprintf(stderr, "%s: Cannot write to FIFO: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
} else
if (n != (ssize_t)msg_len) {
fprintf(stderr, "%s: Wrote only %zd of %zu bytes to the FIFO.\n", argv[1], n, msg_len);
return EXIT_FAILURE;
} else {
printf("Success!\n");
}
close(wfd);
close(rfd);
return EXIT_SUCCESS;
}
compiles (gcc -Wall -Wextra -O2 source.c -o binary
) cleanly, and implements the shown code (but with error checking, and using a command line parameter for the name of the FIFO to be opened). It does not verify the named file is a FIFO, though. If run on a non-existent FIFO, it complains "FIFO: Cannot open FIFO for reading: No such file or directory."
If you create the FIFO (mkfifo test-fifo
) and run the test program, there are no errors; the only output is "Success!".
If you create the FIFO, and repeatedly write data to it but never read from it, at some point the kernel buffer for the FIFO becomes full, and running the test program will report "test-fifo: Cannot write to FIFO: Resource temporarily unavailable." (which means that write() returned -1 with errno==EWOULDBLOCK or errno==EAGAIN).
You can simulate this by running e.g. bash -c 'exec 4<>test-fifo ; dd if=/dev/zero of=test-fifo bs=1 oflag=nonblock status=progress'
, which opens the test-fifo
FIFO read-write (which in Linux always succeeds) to descriptor 4, then runs dd
to fill it with zeroes, using Bash (sub-)shell. On my system, a FIFO can hold 65536 bytes (64k). After filling the FIFO like this, running the test program (./binary test-fifo
) will fail as described.
Hopefully, you'll see the light and the importance and usefulness of error checking. It is NOT something you should consider as "I'll add them in later when/if I have time"; they are also an important development tool.
Upvotes: 3