Opening named pipe blocks forever, if pipe is deleted without being connected

fifofile-descriptorsiopipeprocess

Try the following shell commands:

mkfifo /tmp/test.pipe

ls -1 /tmp > /tmp/test.pipe &

rm /tmp/test.pipe
mkfifo /tmp/test.pipe
cat /tmp/test.pipe &

jobs

The ls command is just an example and could be any process, that wants to write into the named pipe. The point here is, that the process will block trying to open the pipe for writing. Now, another process comes along and removes the pipe. A new pipe of the same name gets created, and the cat process (i.e. any process trying to open it for reading) will block waiting for another process to write into the new pipe of the same name. Both processes are waiting for their respective pipe connection. This can be verified by listing the background jobs.

I'm not so much concerned about the blocking cat (or whatever process is trying to read from the pipe), because in reality the process reading from the pipe is actually the one replacing it in the first place. In fact, I don't care about reading processes at all. The cat only serves my example to prove that indeed both process refer to the same pipe filename, but are obviously using distinct pipe instances. However, note that this deadlock example would block in the inverse case, as well, i.e. a reading process would block forever if the pipe gets deleted without ever being opened for writing.

My focus is on the blocking write process (the ls in my example). It never gets a chance to realize that the original pipe it is trying to write to has been replaced by a new one of the same name.

Is there any Linux way to identify such situations, i.e. a situation where a process blocks for writing access to a named pipe that doesn't exist anymore? You may assume root privileges, and that I know the PIDs of all processes involved. In particular, I know the writing process that may or may not have run into such a situation. However, I don't know (which is what I want to find out), whether the process actually ran into that problem or not, so I can terminate it. How can I find processes that are blocking for access to a named pipe that doesn't physically exist on the file system anymore?

Apparently, ls /proc/<PID>/fd doesn't list the file descriptor for either process' access to either pipe, except when both ends of a pipe are connected, i.e. ls /proc/<PID>/fd will list the file descriptors to a pipe for both processes only after the system call has successfully returned on both its ends. Since in my example there are two distinct half-connected pipes of the same name, neither is listed for either process.

Best Answer

As suggested by Julie Pelletier, I'm making this answer about the workaround we found in our discussion.

You cannot easily identify deadlocked situations as described in my question, but you can pre-emptively vent the named pipe as a workaround before anyone deletes it (if you really cannot avoid such a deletion, as in my case). This venting should allow any writer currently blocked trying to open the pipe, to succeed with the open operation but fail during the actual write operation (-> broken pipe). Failing might be better than a deadlock. In order to not immediately run into the next deadlock, you should move the pipe before venting it, and delete it afterwards.

# rename the pipe, i.e. move it out of the way
mv -f /tmp/test.pipe /tmp/test.pipe~ 2>/dev/null

# vent the pipe, i.e. shortly open it for reading but don't read from it.
# call the subshell dd and empty echo calls in the background to avoid 
# deadlocking on redundant venting.
(dd if=/tmp/test.pipe~ count=0 2>/dev/null & echo -n "" >/tmp/test.pipe~ &)

# delete the old pipe
rm -f /tmp/test.pipe~

If you know which program executes the faulty deletion, you might want to wrap that program into a small script, that does the venting and forgoes the deletion.

Related Question