Return to BSD News archive
Xref: sserve comp.unix.ultrix:17360 comp.unix.programmer:8913 comp.unix.bsd:11794 Newsgroups: comp.unix.ultrix,comp.unix.programmer,comp.unix.bsd Path: sserve!newshost.anu.edu.au!munnari.oz.au!constellation!osuunx.ucc.okstate.edu!moe.ksu.ksu.edu!zaphod.mps.ohio-state.edu!uwm.edu!msuinfo!uchinews!ellis!besp From: besp@ellis.uchicago.edu (Anna Pluzhnikov) Subject: Re: Peculiar behavior of pclose() -- mistery solved (long) Message-ID: <1993Apr11.044800.6582@midway.uchicago.edu> Sender: news@uchinews.uchicago.edu (News System) Reply-To: besp@midway.uchicago.edu Organization: University of Chicago References: <PRZEMEK.93Mar28212112@rrdstrad.nist.gov> Date: Sun, 11 Apr 1993 04:48:00 GMT Lines: 158 In article <PRZEMEK.93Mar28212112@rrdstrad.nist.gov> przemek@rrdstrad.nist.gov (Przemek Klosowski) writes: > >I came across a peculiar behavior of pclose(); when only one file is >popen()'ed, everything works as the man page and Stevens' book >claim: pclose() closes the pipe and kills the child process. pclose() doesn't kill the child, it just closes pipe to the child, so the child gets EOF and exits by itself. > >However, if two filehandles are popen()'ed, the subsequent pclose() >never returns (on Ultrix it sits forever in wait()). > >Does anyone have an idea what causes it and how to get around it? > >This behavior has been checked on ConvexOS and Ultrix 4.3. I enclose a >short program demonstrating this behaviour. > As well as on Linux, but not on SunOS. > >------------------------cut here------------------------------------- > >#include <stdio.h> > >int >main(){ > FILE * fp1, *fp2; > > /* this works fine (it better!!!) */ > fp1 = popen ("cat","w"); > fprintf (fp1,"This is output to first filehandle \n"); fflush (fp1); > pclose(fp1); > > /* this is broken (nobody ran two filters at the same time, eh?) */ > fp1 = popen ("cat","w"); > fp2 = popen ("cat","w"); > fprintf (fp1,"This is output to first filehandle\n"); fflush (fp1); > fprintf (fp2,"This is output to second filehandle\n"); fflush (fp2); > > pclose(fp1); /* this call never returns */ > pclose(fp2); >} This was a fun problem to solve. I might even use it when I next interview somebody claiming to be a UNIX wizard :-) Let's see what's going on. (My system is Linux mozart 0.99.pl6-2 03/07/93 i486) 21:35:10 > cc -o tmp tmp.c 21:35:23 > strace tmp uselib("/usr/lib//libc.so.4") = -1 (No such file or directory) uselib("/lib//libc.so.4") = 0 brk(0) = 0x2000 brk(5000) = 0x5000 brk(6000) = 0x6000 /* this is inside first call to popen() */ pipe([3,4]) = 0 fork() = 1666 close(3) = 0 fstat(4, [dev 0 0 ino 0 nlnks 1 ...]) = 0 brk(7000) = 0x7000 write(4, "This is output to first filehand".., 36This is output to first filehandle ) = 36 /* and this is inside first call to pclose() */ close(4) = 0 sgetmask() = 0 ssetmask([SIGHUP SIGINT SIGQUIT]) = 0 wait4(-1, - [SIGCHLD] EXITED(0), 0, (struct rusage *)0) = 1666 sgetmask([SIGHUP SIGINT SIGQUIT]) = 0x7 ssetmask(0) = 0x7 /* second popen() */ pipe([3,4]) = 0 fork() = 1668 close(3) = 0 /* third popen() */ pipe([3,5]) = 0 fork() = 1670 close(3) = 0 fstat(4, [dev 0 0 ino 0 nlnks 1 ...]) = 0 write(4, "This is output to first filehand".., 35This is output to first filehandle ) = 35 fstat(5, [dev 0 0 ino 0 nlnks 1 ...]) = 0 write(5, "This is output to second filehan".., 36This is output to second filehandle ) = 36 /* second pclose() */ close(4) = 0 sgetmask() = 0 ssetmask([SIGHUP SIGINT SIGQUIT]) = 0 wait4(-1, /* second pclose() never returns ??? */ And here is sits forever ... In another xterm: 21:35:40 > ps -l F UID PID PPID PRI NI SIZE RSS WCHAN STAT TT TIME COMMAND 4 101 1664 58 1 0 50 148 wait4 S p2 0:00 strace tmp 34 101 1665 1664 1 0 30 156 wait4 S p2 0:00 tmp 4 101 1668 1665 1 0 270 296 pause S p2 0:00 sh -c cat 4 101 1669 1668 1 0 26 88 pipe_read S p2 0:00 cat 4 101 1670 1665 1 0 270 296 pause S p2 0:00 sh -c cat 4 101 1671 1670 1 0 26 88 pipe_read S p2 0:00 cat 4 101 1681 1677 24 0 81 176 R p1 0:00 ps -l (Irrelevant processes deleted) So, for some reason the first "cat" (pid 1669) doesn't die even though it's parent close()d the necessary file. Let's look some more: 21:35:55 > fstat USER COMMAND PID FD DEV INUM SZ|DV MODE NAME (this is the parent, as expected it only has a pipe to the second shell) anna tmp 1665 wd 3,2 750 480 drwx--x--x anna tmp 1665 text 3,2 5667 14104 -rwxr-xr-x anna tmp 1665 lib 3,2 1457 623620 -rwxr-xr-x /lib/libc.so.4.3.3 anna tmp 1665 0 3,2 161 4,194 crw--w--w- /dev/ttyp2 anna tmp 1665 1 3,2 161 4,194 crw--w--w- /dev/ttyp2 anna tmp 1665 2 3,2 161 4,194 crw--w--w- /dev/ttyp2 anna tmp 1665 5* (pipe -> 0x75d000 0 1r 1w) (this is the first shell, and it still has the pipe open !!! to whom ???) anna sh 1668 wd 3,2 750 480 drwx--x--x anna sh 1668 text 3,2 6 218112 -r-xr-xr-x anna sh 1668 lib 3,2 206 173060 -r-xr-xr-x /lib/libc.2.2.2 anna sh 1668 0* (pipe <- 0x1ac000 0 1r 1w) anna sh 1668 1 3,2 161 4,194 crw--w--w- /dev/ttyp2 anna sh 1668 2 3,2 161 4,194 crw--w--w- /dev/ttyp2 anna cat 1669 wd 3,2 750 480 drwx--x--x anna cat 1669 lib 3,2 1457 623620 -rwxr-xr-x /lib/libc.so.4.3.3 anna cat 1669 0* (pipe <- 0x1ac000 0 1r 1w) anna cat 1669 1 3,2 161 4,194 crw--w--w- /dev/ttyp2 anna cat 1669 2 3,2 161 4,194 crw--w--w- /dev/ttyp2 (AHA!!! this is the second shell, and it has it's stdin going from pipe from "tmp", but it also has an open pipe going to the first shell, which it inherited from "tmp" when we fork()ed inside of the third popen()) anna sh 1670 wd 3,2 750 480 drwx--x--x anna sh 1670 text 3,2 6 218112 -r-xr-xr-x anna sh 1670 lib 3,2 206 173060 -r-xr-xr-x /lib/libc.2.2.2 anna sh 1670 0* (pipe <- 0x75d000 0 1r 1w) anna sh 1670 1 3,2 161 4,194 crw--w--w- /dev/ttyp2 anna sh 1670 2 3,2 161 4,194 crw--w--w- /dev/ttyp2 anna sh 1670 4* (pipe -> 0x1ac000 0 1r 1w) anna cat 1671 wd 3,2 750 480 drwx--x--x anna cat 1671 lib 3,2 1457 623620 -rwxr-xr-x /lib/libc.so.4.3.3 anna cat 1671 0* (pipe <- 0x75d000 0 1r 1w) anna cat 1671 1 3,2 161 4,194 crw--w--w- /dev/ttyp2 anna cat 1671 2 3,2 161 4,194 crw--w--w- /dev/ttyp2 anna cat 1671 4* (pipe -> 0x1ac000 0 1r 1w) So, to solve the problem, we should just reverse the order of pclose()s, and everything would be happy ever after. But! There is yet another mistery to solve: how come the original program works under SunOS ?