Perl: creating zombies through open() without close()
-
10-10-2019 - |
Question
Here is the problem: I've a daemon which get requests from a client, executes a function (from some module) due to the request and returns an answer to the client. After the fork() I close STDIN,STDOUT and STDERR. One function is to check dmesg. For this I get the dmesg output through open(DMESG, "/bin/dmesg |"). I don't close this fh after reading from it, because I thought that it would close automatically after the function finished. But this doesn't happen and I get a zombie for each call of dmesg.
On How can I reinitialize Perl's STDIN/STDOUT/STDERR? I found "The problem with closing STDOUT instead of reopening is that if you open other files, they might get fd 0,1 or 2 - preventing you from reopening STDOUT in the future." by jmanning2k And I think that it has something to do with it but I don't really get it. I hope that someone can explain it to me.
I know that I can avoid the the problem e.g. by calling dmesg via qx(); or simply closing the fh but I want to understand where the zombies are coming from.
Solution
The form
open DMESG, "/bin/dmesg|";
Opens the pipe and assigns it to the dynamically scoped variable DMESG
. Dynamically scoped variables actually live "forever" in Perl, being saved as necessary whenever a local
is seen.
If you instead use the form
open my $dmesg, "/bin/dmesg|";
The lexical filehandle variable $dmesg
will be closed on scope exit, assuming there's no other reason to keep it alive (i.e. it's not passed back or otherwise stored in a global variable).
OTHER TIPS
open(DMESG, "/bin/dmesg |")
I don't close this fh after reading from it, because i thought that it would close automatically after the function finished.
For this to work, the handle must be lexical, so it can fall properly out of scope.
open my $dmesg, …
The problem has to do with the way Perl is implemented. Here's a piece of code from the function Perl_do_openn
in file doio.c
:
fd = PerlIO_fileno(IoIFP(io));
if (IoTYPE(io) == IoTYPE_STD) {
/* This is a clone of one of STD* handles */
result = 0;
}
else if (fd >= 0 && fd <= PL_maxsysfd) {
/* This is one of the original STD* handles */
saveifp = IoIFP(io);
saveofp = IoOFP(io);
savetype = IoTYPE(io);
savefd = fd;
result = 0;
}
If you open an existing filehandle, the filehandle will be closed and reopened by open()
. This doesn't happen with STD*
handles, as you can see from the code above. So Perl takes the next free handle for opening and the older one remains open.