in reply to parent process stuck in read(2) on pipe opened to child process using backtick

I cannot reproduce the problem (neither with bash nor with dash).   I get

... read(3, "I am a.sh.......\n", 4096) = 17 read(3, "I am gonna die ........\n", 4096) = 24 --- SIGCHLD (Child exited) @ 0 (0) --- read(3, "", 4096) = 0 fstat(3, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0 close(3) = 0 ...

(Perl 5.12.4, current versions of dash/bash as they ship with Ubuntu 11.10)

Update: err wait... you're probably not killing the sleep, but the shell only (in which case the sleep subprocess may survive (depending on the shell settings)).  Try killing sleep directly, or kill the entire process group (use "ps fo pid,pgrp,comm" to find out, then "kill -15 -PGRP").

Replies are listed 'Best First'.
Re^2: parent process stuck in read(2) on pipe opened to child process using backtick
by Anonymous Monk on Feb 14, 2012 at 12:28 UTC

    You are absolutely right - can't thank you enough. I checked - sleep is holding on to the pipe that's why read(2) doesn't exit. I can get really dumb at times - how hard was that to figure out :) Also, when the child is a perl script instead of bash/sh, killing it takes away the sleep too. That explains why I saw the issue only with bash/sh. Shouldn't bash/sh do the same, i.e., take care of cleaning up it's child processes??? Maybe this is not the right forum for bash/sh but what's the harm in asking :)

      ... Maybe this is not the right forum for bash/sh but what's the harm in asking :)

      Yes, this is not a bash forum, and all in all, bash configuration is a rather complex topic...

      Anyhow, the easiest approach would probably be to add a line

      trap "kill 0" EXIT

      to your a.sh.  This sets up an exit handler which kills the current process group.

      Then run a.sh in a new process group (so you avoid killing the calling Perl script, too):

      my $x=`exec perl -e "setpgrp; exec '/root/a.sh'"`;

      (I'm not aware of any way to create a new process group from within the shell script itself, so I'm using Perl's setpgrp here.)

Re^2: parent process stuck in read(2) on pipe opened to child process using backtick
by onong (Initiate) on Feb 21, 2012 at 13:56 UTC

    Thanks, Guys, for the insights so far. Well, I have been trying to figure out the best way to work-around my original issue, based on the inputs I received, but today I stumbled onto something really weird.

    My perl thread is stuck in read(2) again but this time it is because some other completely unrelated thread has a reference to that pipe.

    [root@onong 5227]# strace -s 1024 -p 5227 Process 5227 attached - interrupt to quit read(28, The fd in question is a pipe: [root@onong tmp]# ls -l /proc/5227/fd total 0 lr-x------ 1 root root 64 Feb 21 02:02 0 -> /dev/null l-wx------ 1 root root 64 Feb 21 02:02 1 -> /tmp/abc.log . . lr-x------ 1 root root 64 Feb 21 02:02 28 -> pipe:[28443] Following are the processes which have a reference to this pipe: [root@onong tmp]# lsof | grep 28443 perl 5049 root 28r FIFO 0,6 + 28443 pipe ntpd 9936 ntp 28r FIFO 0,6 + 28443 pipe ntpd 9936 ntp 29w FIFO 0,6 + 28443 pipe dhcpd 10228 root 28r FIFO 0,6 + 28443 pipe java 15518 root 28r FIFO 0,6 + 28443 pipe

    As you can see, the ntpd process has the write-end of the pipe open!!!!

    Any ideas as to how this is even possible????

      Posting my findings with the hope that it would be of help to someone out there.

      system and backtick both fork a child process. Backtick differs from system in that it opens a pipe for gathering the child process/command’s output. And this is what perl doc on fork has to say about file descriptors:

      Any filehandles open at the time of the fork() will be dup()-ed. Thus, the files can be closed independently in the parent and child, but beware that the dup()-ed handles will still share the same seek pointer. Changing the seek position in the parent will change it in the child and vice-versa. One can avoid this by opening files that need distinct seek pointers separately in the child. On some operating systems, notably Solaris and Unixware, calling exit() from a child process will flush and close open filehandles in the parent, thereby corrupting the filehandles. On these systems, calling_exit() is suggested instead. _exit() is available in Perl through the POSIX module. Please consult your system's manpages for more information on this

      So, whatever file descriptors/handles were open at the time of executing system/backtick are inherited by the child process/command. Which means processes like ntpd/dhcpd/named etc inherit all of the parent perl process' open file descriptors including any pipes opened as part of backtick.

      Somehow ntpd, in my case, also inherited the write-end of the pipe which is the reason the parent process, which had the read-end of the pipe, was stuck in read(2) indefinitely because ntpd is a long running process. Now, the parent process closes the write-end of the pipe before forking but the whole operation of opening the pipe and closing the write-end is not atomic so .......

      perl process

      backtick

      pipe()

      <---------- GAP HERE

      close() write-end of pipe

      The solution I used was to write a perl wrapper script which closes all fds except 0/1/2 and then exec the command, like this:

      `closed.pl service xyz restart`;

      There are multiple ways of closing fds. I used the tips from the following discussion : http://www.perlmonks.org/?node_id=476086

      Here's a link which describes a similar issue : http://tdistler.com/2010/06/18/stop-stealing-my-file-descriptors