FIFO (named pipe)

Niko rushes into the office. “Nini! Help! Our client’s server has a process that is stuck! It just hangs and never finishes!”

Nini looked up from her screen 黑人問號 and asked. “A stuck process? Or is it waiting for something?”

“Let me teach you some basics about pipe. This will be a piece of fish.”

Basic Pipe Behavior

“First, let’s create a simple named pipe,” Nini said.

Now it’s your turn. Open a terminal and follow along with Niko’s debugging session.

Your Task: Create a pipe and try to write the current time into it.

mkfifo F
date +%s > F

After running the second command, you will see that your terminal is “stuck”. It doesn’t return to the command prompt. This is what Niko saw on the client’s server. This is blocking. The date command is waiting because no process is reading from the other end of the pipe.

Your Task: Press Ctrl+C to interrupt the command and get your prompt back. Then, verify that you created a pipe using ls -l. The file F should have a type p.

Investigating Multiple Blocked Processes

Nini continued: “Now let’s see what happens with multiple writers.”

Your Task: Create two new pipes, A and B.

mkfifo A
mkfifo B

Your Task: Now, run the following commands. The & symbol will run the first two commands in the background. Your terminal will block on the third command. Take note of the Process IDs (PIDs) that are printed for the background jobs.

date +%s > A &
date +%s > B &
date +%s > C

“Good,” said Nini. “Now, let’s dig deeper and investigate one of the background processes.”

Your Task: In a new terminal window (since your other one is blocked), investigate one of the PIDs you noted down. Replace [PID] with the actual process ID.

  1. Check the process list: See if your process is running.
ps aux | grep [PID]
  1. Trace the system calls: Use strace to see what the process is asking the kernel to do.
strace -p [PID]

Look at the output. What system call is your process stuck in? It is waiting for something to happen.

  1. Look inside /proc: The /proc filesystem gives you a direct look into the kernel’s data about processes.
cat /proc/[PID]/cmdline # show the process's command
ls /proc/[PID]/fdinfo/   # list open file descriptors
cat /proc/[PID]/fdinfo/* # see details about the file descriptors

Do you see the file descriptor for the pipe you opened? Is the process name what you expect?

Reading from the Pipes

“The writers are all patiently waiting,” Nini explained. “They will remain blocked until a reader opens the other end of the pipe. Let’s unblock them.”

Your Task: In the terminal where you ran the strace and ps commands, run cat. This command will read from the pipes.

cat -n A B
cat C

As soon as you run cat, you will see the timestamps appear. cat reads the data, which unblocks the waiting date processes, allowing them to finish. Your original terminal will also become unblocked.

Question: Look at the output. Are the timestamps from A and C the same?

A Test of Precision with Python

“The date command only has a precision of one second,” Nini explained. “Let’s try an experiment with more precise timing. We will use a simple Python command to get microseconds.”

Your Task: First, clean up the old pipes and create new ones.

rm A B
mkfifo A B

Next, create a shell alias. This is a shortcut for a longer command.

alias P='python3 -c "from datetime import datetime; print(datetime.now().strftime(\"%H:%M:%S.%f\"))"'

Your Task: Now, run the writers again, just like before.

P > B &
P > A &
P > C

And then, read the results.

cat -n A B C

Question: Compare the microsecond precision of the timestamps in your output. Are they identical or different? How do they compare to the date command results from earlier?

Question: Why might Python show different microsecond values while the date command showed identical timestamps?

Order Dependency

“Okay, final test,” Nini said. “You have seen what happens when the writer starts before the reader. What do you predict will happen if you start the reader before any writers?”

Your Task: Let’s run the experiment. First, start over again:

rm A B
mkfifo A B

Now, run the reader (cat) in the background first, then run the writers.

cat -n A B &
date +%s > A &
date +%s > B &

Observe what happened in your terminal.

  1. What was different this time compared to the first experiment?
  2. Did the writer processes (date) block, or did they execute immediately?
  3. How does this outcome help you understand the blocking behavior of pipes?

Questions

Nini smiled. “You’ve done well. You have seen how processes communicate with pipes. Now, to make sure you understood, answer these questions.”

  1. When you ran date +%s > A &, what was the process name shown by ps? Was it date? Why or why not? (Hint: Think about what the shell does before it runs the date command).

  2. Why did your Python experiment show different microsecond timestamps while the date commands showed identical timestamps? (Hint: Think about how many processes are created and when the timestamps are generated).

  3. Here is a simplified C code snippet that shows what the shell does to run a command like date > A. Explain what the dup2 system call does. At which point — (1), (2), (3), or (4) — does the writer process block if no reader is present?

int main() {
    pid_t pid;

    pid = fork();
    if (pid == 0) { // This is the child process
        /* (1) */
        int fd = open("A", O_WRONLY);

        /* (2) */
        dup2(fd, STDOUT_FILENO); // STDOUT_FILENO is file descriptor 1
        close(fd);

        /* (3) */
        execlp("date", "date", NULL); // Replace this process with 'date'
    }

    /* (4) */ // This is the parent process
}
Back to top