What are Pipes in Operating Systems?
Learn what pipes are in OS design, how they use kernel buffering and flow control, with a C example and interview questions answered.
Expected Interview Answer
A pipe is a unidirectional, kernel-buffered byte stream that connects the standard output of one process directly to the standard input of another, letting related processes exchange data without ever touching a shared file on disk.
When a process calls pipe(), the kernel creates an in-memory circular buffer and hands back two file descriptors: one for reading, one for writing. Because pipes have no name in the filesystem, they only work between processes that share a common ancestor, typically a parent and the child it fork()s, since the child inherits the open descriptors. The kernel enforces flow control automatically: a writer blocks once the buffer fills, and a reader blocks once the buffer empties, so the two processes stay synchronized without either needing to poll. Data flows in one direction only, and once all write-end descriptors are closed the reader sees end-of-file, which is exactly how shell pipelines like ls | grep chain commands together seamlessly.
- Provides automatic flow control via kernel buffering
- Requires no filesystem entry or explicit synchronization code
- Powers shell pipelines connecting arbitrary commands
- Simple, low-overhead IPC for closely related processes
AI Mentor Explanation
A pipe is like a single relay baton tube between two fielders positioned in a fixed throwing line, where the ball can only travel from the first fielder to the second, never back the other way. The tube has a limited capacity, so if the receiving fielder is not ready, the thrower must pause and wait rather than flood the tube with balls. This works because both fielders were placed in that line by the same captain (the common parent process) — a fielder from another match cannot suddenly plug into that tube.
Step-by-Step Explanation
Step 1
Create the pipe
A process calls pipe(fds), and the kernel allocates an in-memory buffer, returning a read-end and a write-end file descriptor.
Step 2
Fork to share descriptors
The process calls fork(); the child inherits copies of both descriptors, giving parent and child a shared connection.
Step 3
Close unused ends
Each process closes the end it does not need — the writer closes the read end, the reader closes the write end.
Step 4
Stream data with flow control
The writer writes bytes into the buffer and the reader reads them out; the kernel blocks either side automatically when the buffer is full or empty.
What Interviewer Expects
- Clear definition of a pipe as a unidirectional, kernel-buffered byte stream
- Understanding that pipes require a common ancestor process
- Awareness of automatic blocking/flow control on full or empty buffers
- A concrete example, such as shell pipelines or fork-based parent/child communication
Common Mistakes
- Thinking a plain pipe can be used between two unrelated processes
- Forgetting to close unused file descriptor ends, causing hangs or missed EOF
- Believing pipes are bidirectional by default
- Confusing an anonymous pipe with a named pipe (FIFO)
Best Answer (HR Friendly)
“A pipe is a simple one-way channel the operating system sets up so one process’s output can flow directly into another process’s input, like water through an actual pipe. It only works between processes that are related, usually a parent and the child process it created, and the kernel automatically pauses either side so data never overflows or gets read before it exists.”
Code Example
int fds[2];
pipe(fds); /* fds[0] = read end, fds[1] = write end */
pid_t pid = fork();
if (pid == 0) {
/* child: writer */
close(fds[0]);
write(fds[1], "hello from child", 17);
close(fds[1]);
} else {
/* parent: reader */
close(fds[1]);
char buf[32];
int n = read(fds[0], buf, sizeof(buf));
buf[n] = '\0';
printf("parent read: %s\n", buf);
close(fds[0]);
}Follow-up Questions
- How does a named pipe (FIFO) differ from an anonymous pipe?
- What happens if a process writes to a pipe whose read end is closed?
- How does the shell implement `cmd1 | cmd2` using pipes and fork/exec?
- What is the typical buffer size of a pipe, and what happens when it fills up?
MCQ Practice
1. An anonymous pipe created with pipe() can be used between which processes?
Anonymous pipes have no filesystem name, so only processes that inherit the descriptors through fork() (a common ancestor) can use them.
2. What happens when a process tries to write to a full pipe buffer?
The kernel enforces flow control by blocking the writer until the reader drains enough of the buffer.
3. A pipe is best described as?
Pipes carry a stream of bytes in one direction only, buffered in kernel memory rather than on disk.
Flash Cards
What is a pipe in an OS? — A unidirectional, kernel-buffered byte stream connecting one process's output to another's input.
Who can use an anonymous pipe? — Only processes with a common ancestor, since descriptors are inherited via fork().
What happens when the pipe buffer is empty? — The reader blocks until the writer produces more data or closes the write end.
How does a reader detect the writer is done? — It sees end-of-file once all write-end descriptors are closed.