๐ FIFOs โ Named Pipes
mkfifo(), Filesystem Entry, Unrelated Process IPC
Part 6 of 9
FIFOs
FIFOs
Topic
Named Pipes
Named Pipes
Level
Intermediate
Intermediate
What Is a FIFO?
A FIFO (First In, First Out) is also called a named pipe. Unlike anonymous pipes which exist only in memory, a FIFO has a name in the filesystem โ you can see it with ls -l where it shows up with type p.
The key advantage: any two unrelated processes on the same system can communicate through a FIFO, as long as they agree on the FIFO’s pathname. No shared ancestry is required.
Key Concepts
mkfifo() mknod() S_IFIFO open() blocking O_RDONLY O_WRONLY unlink() filesystem entry
๐ Anonymous Pipe vs FIFO
๐ Anonymous Pipe
โ
Created with pipe()
โ No filesystem entry
โ Only related processes
โ Faster (no fs overhead)
โ Auto-destroyed when all fds closed
โ No filesystem entry
โ Only related processes
โ Faster (no fs overhead)
โ Auto-destroyed when all fds closed
๐ FIFO (Named Pipe)
โ
Created with mkfifo()
โ Has a path (e.g., /tmp/myfifo)
โ Any unrelated processes can use
โ Persists until unlink() called
โ Needs filesystem access
โ Has a path (e.g., /tmp/myfifo)
โ Any unrelated processes can use
โ Persists until unlink() called
โ Needs filesystem access
Both are kernel-buffered, byte-stream, unidirectional IPC mechanisms.
โ๏ธ Creating a FIFO
There are two ways to create a FIFO:
#include <sys/types.h>
#include <sys/stat.h>
/* Method 1: mkfifo() โ preferred */
int mkfifo(const char *pathname, mode_t mode);
/* Returns 0 on success, -1 on error */
/* Method 2: mknod() with S_IFIFO */
int mknod(const char *pathname, mode_t mode, dev_t dev);
/* For FIFO: dev = 0, mode includes S_IFIFO */
Or from the shell:
# Shell commands to create a FIFO:
mkfifo /tmp/myfifo # create with default permissions
mkfifo -m 0666 /tmp/myfifo # create with explicit permissions
# View the FIFO:
ls -l /tmp/myfifo
# prw-rw-rw- 1 ravi ravi 0 Jun 7 12:00 /tmp/myfifo
# ^--- 'p' means FIFO/pipe type
Example 1: Create and verify a FIFO
/* create_fifo.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#define FIFO_PATH "/tmp/ep_demo_fifo"
int main(void)
{
struct stat st;
/* Create the FIFO if it doesn't exist */
if (mkfifo(FIFO_PATH, 0666) == -1) {
perror("mkfifo");
/* Continue if it already exists */
}
/* Verify it's a FIFO */
if (stat(FIFO_PATH, &st) == -1) {
perror("stat");
return 1;
}
printf("File type: ");
if (S_ISFIFO(st.st_mode))
printf("FIFO (named pipe) โ\n");
else
printf("Not a FIFO!\n");
printf("Permissions: %o\n", st.st_mode & 0777);
printf("FIFO path: %s\n", FIFO_PATH);
printf("Now you can open it from two separate processes.\n");
/* Cleanup */
unlink(FIFO_PATH);
printf("FIFO removed with unlink()\n");
return 0;
}
๐ Opening a FIFO โ Blocking Semantics
A FIFO is opened with the standard open() call. The important difference from regular files: opening a FIFO blocks by default until the other end is also opened.
FIFO open() Blocking Behavior
Reader opens first (O_RDONLY)
โ open() BLOCKS
โ Waits until writer opens FIFO with O_WRONLY
โ Both unblock together
โ Data can now flow
โ Waits until writer opens FIFO with O_WRONLY
โ Both unblock together
โ Data can now flow
Writer opens first (O_WRONLY)
โ open() BLOCKS
โ Waits until reader opens FIFO with O_RDONLY
โ Both unblock together
โ Data can now flow
โ Waits until reader opens FIFO with O_RDONLY
โ Both unblock together
โ Data can now flow
O_NONBLOCK flag
O_RDONLY | O_NONBLOCK โ returns immediately even if no writer
O_WRONLY | O_NONBLOCK โ fails with ENXIO if no reader
O_WRONLY | O_NONBLOCK โ fails with ENXIO if no reader
Example 2: FIFO Writer Process
/* fifo_writer.c โ run in one terminal */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#define FIFO_PATH "/tmp/ep_chat_fifo"
int main(void)
{
int fd;
char buf[256];
/* Create FIFO (ignore EEXIST error) */
mkfifo(FIFO_PATH, 0666);
printf("[Writer] Opening FIFO for writing...\n");
printf("[Writer] Blocking until a reader opens the other end...\n");
/* This open() BLOCKS until reader opens same FIFO with O_RDONLY */
fd = open(FIFO_PATH, O_WRONLY);
if (fd == -1) {
perror("open");
return 1;
}
printf("[Writer] Reader connected! Start typing (Ctrl+D to stop):\n");
/* Read from stdin, write to FIFO */
while (fgets(buf, sizeof(buf), stdin) != NULL) {
ssize_t n = write(fd, buf, strlen(buf));
if (n == -1) {
perror("write");
break;
}
}
close(fd);
printf("[Writer] Done. Closing FIFO.\n");
return 0;
}
Example 3: FIFO Reader Process
/* fifo_reader.c โ run in another terminal */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#define FIFO_PATH "/tmp/ep_chat_fifo"
int main(void)
{
int fd;
char buf[256];
ssize_t n;
printf("[Reader] Opening FIFO for reading...\n");
printf("[Reader] Blocking until a writer opens the other end...\n");
/* This open() BLOCKS until writer opens same FIFO with O_WRONLY */
fd = open(FIFO_PATH, O_RDONLY);
if (fd == -1) {
perror("open");
return 1;
}
printf("[Reader] Writer connected! Receiving data:\n");
/* Read data from FIFO until EOF */
while ((n = read(fd, buf, sizeof(buf) - 1)) > 0) {
buf[n] = '\0';
printf("[Reader] Received: %s", buf);
}
close(fd);
printf("[Reader] EOF received. Writer closed the FIFO.\n");
return 0;
}
/* To test:
Terminal 1: gcc -o fifo_reader fifo_reader.c && ./fifo_reader
Terminal 2: gcc -o fifo_writer fifo_writer.c && ./fifo_writer
(Both will unblock when the other opens the FIFO)
*/
๐ป Example 4: FIFO Communication Between Parent and Child
/* fifo_fork.c โ parent and child communicate via FIFO */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#define FIFO_PATH "/tmp/ep_fork_fifo"
int main(void)
{
/* Create the FIFO before forking */
mkfifo(FIFO_PATH, 0666);
pid_t pid = fork();
if (pid == -1) { perror("fork"); return 1; }
if (pid == 0) {
/* ===== CHILD: reads from FIFO ===== */
int fd = open(FIFO_PATH, O_RDONLY);
char buf[128];
ssize_t n = read(fd, buf, sizeof(buf) - 1);
buf[n] = '\0';
printf("[Child PID=%d] Received: \"%s\"\n", getpid(), buf);
close(fd);
_exit(0);
} else {
/* ===== PARENT: writes to FIFO ===== */
int fd = open(FIFO_PATH, O_WRONLY);
const char *msg = "Hello child, from parent via FIFO!";
write(fd, msg, strlen(msg));
printf("[Parent PID=%d] Sent: \"%s\"\n", getpid(), msg);
close(fd);
wait(NULL);
}
/* Cleanup */
unlink(FIFO_PATH);
return 0;
}
๐ฏ Interview Questions โ FIFOs
Q1. What is a FIFO and how is it different from an anonymous pipe?
A: A FIFO is a named pipe with a filesystem entry. Unlike anonymous pipes (visible only to related processes), any two processes can communicate via a FIFO as long as they know its pathname. FIFOs persist in the filesystem until explicitly removed with unlink().
A: A FIFO is a named pipe with a filesystem entry. Unlike anonymous pipes (visible only to related processes), any two processes can communicate via a FIFO as long as they know its pathname. FIFOs persist in the filesystem until explicitly removed with unlink().
Q2. What system call creates a FIFO?
A:
A:
mkfifo(pathname, mode) creates a FIFO at the given path with the given permissions. Alternatively, mknod(path, S_IFIFO | mode, 0) can be used.Q3. What happens when a process opens a FIFO with O_RDONLY?
A: By default, the open() call blocks until another process opens the same FIFO with O_WRONLY. This synchronization ensures both ends are ready before data flows.
A: By default, the open() call blocks until another process opens the same FIFO with O_WRONLY. This synchronization ensures both ends are ready before data flows.
Q4. What does the ‘p’ character mean in ls -l output?
A: The ‘p’ in the first character of the permissions field indicates the file is a FIFO (pipe). For example:
A: The ‘p’ in the first character of the permissions field indicates the file is a FIFO (pipe). For example:
prw-rw-rw- 1 user group 0 ... /tmp/myfifo.Q5. How do you remove a FIFO?
A: Use
A: Use
unlink(pathname) to remove the FIFO’s directory entry. Active connections are unaffected until all processes close their file descriptors.Q6. What happens with O_NONBLOCK when opening a FIFO for writing with no reader?
A: open(path, O_WRONLY | O_NONBLOCK) fails immediately with errno = ENXIO if no process has the FIFO open for reading. This is different from O_RDONLY | O_NONBLOCK, which succeeds even without a writer.
A: open(path, O_WRONLY | O_NONBLOCK) fails immediately with errno = ENXIO if no process has the FIFO open for reading. This is different from O_RDONLY | O_NONBLOCK, which succeeds even without a writer.
