Search code examples
clinuxfreezestderr

Freeze when writing to `stderr`


In my program I get a freeze sometimes when writing to stderr in this case:

  1. Program starts (e.g. from Terminal)
  2. Program forks itself two times and using execvp to start each process with different parameters (original file is read from /proc/self/exe)
  3. The first started program quits.
  4. Now the two forked processes are still running
  5. Close the terminal the first program was started
  6. A few attempts using fprintf to write to stderr work, on some point I will get a complete lockup on my program. Debugger tells me its fprintf.

What is happening here? What I already tried is putting a SIG_IGN on SIGPIPE to prevent the program crash as soon as nobody is listening on the pipes anymore. But now I am stuck (the behavious with the Freeze is the same, with SIG_IGN and without it).

Any help is appreciated.


Solution

  • In a nutshell: The system sends your program signals to save you from a problem. You ignore those signals. Bad things happen.

    When your parent program was run, it had stdin (fd 0), stdout (fd 1) and stderr (fd 2) connected to the TTY of the shell that ran you (the terminal). These function much like pipes. When you closed the terminal, these fds are left hanging, with no one on the other side to be able to communicate with them.

    At first, nothing bad happens. You write to stderr, and the standard library caches those writes. No system calls are performed, so no problem.

    But then the buffers fill up, and stdlib tries to flush them. When it does that, it fills up the kernel buffers for the pipe or TTY. At first, that works fine as well. Sooner or later, however, these buffers fill up as well. When that happens, the kernel suspends your processes and waits for someone to read from the other end of those pipes. Since you closed the terminal, however, no one ever will, and your programs are suspended indefinitely.

    The standard way to avoid this problem is to disconnect the 0-2 file descriptors from the controlling TTY. Instead of telling you how to do that, I would like to suggest that what you are trying to do here, run a program so that it is completely disconnected from a TTY, has a name: daemonizing.

    Check out this question for more details on how to do that.

    Edited to add:

    It was not clear from your function whether the programs you are execveing are your own or not. If they are not, please be aware that many user programs are not designed to run as a daemon. The most obvious caveat is that if a program unconnected to any TTY opens a TTY file, and unless it passes O_NOCTTY to open, that TTY becomes the controlling TTY of the program. Depending on circumstances, that might lead to unexpected results.