I'm a beginner who recently started system programming. I am writing an article to fix my knowledge, so if you have any mistakes or advice, it would be very helpful if you could point them out in the comments.
This article explains the FIFO and how to use it with code examples. The programs appearing in this article have been confirmed to work in the following environments.
Ubuntu 18.04.4 LTS (Bionic Beaver)
gcc version 9.2.1
In addition, I will leave the particularly detailed explanations (detailed errors and their countermeasures, etc.) such as the API introduced in this article to the target document, and point out the minimum necessary points and addiction points that I needed to use FIFO. I will focus on the explanation.
** FIFO ** is a type of interprocess communication, also called named pipe **, and has been extended so that anonymous pipes, which could only be used between processes with a common ancestor, can be used as a means of communication between different processes. Thing.
FIFO is a kind of file in Unix-like OS, and it can be operated like a normal file **.
The FIFO is basically created by specifying the path with mkfifo or mkfifoat.
mkfifo
  int ret = mkfifo("test", 0666);
  int (ret == -1) {
    perror("mkfifo");
  }
mkfifoat creates a FIFO based on the specified file descriptor.
mkfifoat
  int fd, ret;
  const char* home_path = getenv("HOME"); //Get the HOME path from an environment variable
  
  fd = open(home_path, O_RDONLY); //Get fd of HOMEDir
  if (fd == -1) {
    perror("open");
    exit(0);
  }
  
  ret = mkfifoat(fd, "test", 0666); //Create FIFO based on HOME
  if (ret == -1) {
    perror("mkfifo");
  }
Since FIFO is a kind of file, its existence can be confirmed by ls.
Also, if a FIFO with the same name already exists, ʻerrno is set to ʻEEXIST.
FIFOs use a file system, so you can use ʻopen, write, and read` to manipulate files.
(However, the place where the data is written is the buffer in the kernel.)
open
Just like opening a file, specify the path to the FIFO and open it.
open
  #include <sys/fcntl.h>
  int fd1 = open("test", O_RDONLY); //Read Only
  int fd2 = open("test", O_WRONLY); //Write Only
  int fd3 = open("test", O_RDWR); //Both reading and writing are possible
The FIFO cannot pass data until both ends are opened (= open is blocked).
This specification is inconvenient because the FIFO cannot be opened until the server accepts the communication from the client when communicating between the server and the client using the FIFO.  Therefore, you can open the FIFO non-blocking by specifying ʻO_NONBLOCK of ʻopen`.
nonblocking
  #include <sys/fcntl.h>
  int fd_nb1 = open("test", O_RDONLY | O_NONBLOCK); //mode is specified by OR
  int fd_nb2 = open("test", O_RDWR | O_NONBLOCK);
As a caveat when specifying NONBLOCK, when opening a FIFO in write mode, ʻENXIO (no such device or address) is returned even if one is open.  That's why we're using ʻO_RDWR instead of ʻO_WRONLY` (although POSIX doesn't have O_RDWR rules, so it can only be used on Linux and should be ported with caution).
read You can read the data from the beginning of the FIFO by the specified number of bytes using the file descriptor when the FIFO is ʻopen`.
read(Blocking)
  //An example of read
  struct Packet {
    int data1;
    double data2;
  };
  void* buf = malloc(sizeof(Packet));
  int len = read(fd, buf, sizeof(Packet));
  
  Packet p = reinterpret_cast<Packet*>(buf);
Also, for non-blocking FIFOs, if blocked by read, ʻerrno is set to ʻEAGAIN and read returns -1.
read(NonBlocking)
  int len = read(fd, buf, SIZE);
  if (len < 0) {
    if (errno == EAGAIN) {
      fprintf(stderr, "avoid blocking\n");
    } else {
      perror("read");
    }
  } else if (len == 0) {
    printf("EOF\n"); // len ==When it is 0, it means that the entire contents of the pipe are being read.
  }
  
  // len >I read the data for len bytes at 0.
Also, when I experimented with the return value of read with FIFO by myself
| Number of processes that opened the pipe in write mode | return value of read | 
|---|---|
| 0 | 0 (EOF) | 
| 1 | -1 (EAGAIN)or number of bytes read | 
The result was that.
write
You can write the specified bytes to the FIFO in the same way as read.
write(Blocking)
  //An example of write
  struct Packet {
    int data1;
    double data2;
  }
  Packet p {10, 2.2};
  int len = write(fd, &p, sizeof(Packet));
For non-blocking FIFOs, ʻEAGAINis set when blocked bywrite as well as read`.
remove The FIFO must be explicitly removed.
remove
  int ret = remove("test");
  if (ret == -1) {
    perror("remove");
  }
I'm using remove to remove it, please let me know if there is another good way.
, read, write, remove`.[Wikipedia Named Pipe](https://ja.wikipedia.org/wiki/%E3%83%91%E3%82%A4%E3%83%97_(%E3%82%B3%E3%83%B3) % E3% 83% 94% E3% 83% A5% E3% 83% BC% E3% 82% BF)) FIFO (Japanese translation of FIFO7) FIFO(7) Detailed UNIX Programming 3rd Edition
Recommended Posts