hardburn has asked for the wisdom of the Perl Monks concerning the following question:
I have a script that opens a filehandle and then calls fork() and exec() to run a separate program for doing the reading. The trouble is that by default, Perl sets the close-on-exec flags on new filehandles. This can be changed with Fcntl:
use Fcntl; my $flags = fcntl $in, F_GETFD, 0 or die "fcntl F_GETFD: $!"; fcntl $in, F_SETFD, $flags & ~FD_CLOEXEC or die "fcntl F_SETFD: $!";
This doesn't work on Strawberry Perl on Windows, though, due to the F_GETFD macro being missing.
At Sebastian Riedel's suggestion, I tried local $^F = 10; instead. This makes Perl consider up to 10 open filehandles to be "system filehandles" which do not get the close-on-exec flag set (when using this, remember that you'll already have three filehandles open: STDIN, STDOUT, and STDERR).
This worked on Linux and Cygwin, but Strawberry Perl still failed to reopen the filedescriptor with "Bad file descriptor". This is the error you usually get when the close-on-exec flag is still set.
Strawberry Perl is built with threads:
This is perl 5, version 18, subversion 1 (v5.18.1) built for MSWin32-x +64-multi-thread
Since fork() would be emulated with threads in this case, perhaps that accounts for the difference with Cygwin?
Full example code below. The first one (fd_pass.pl) is passed a file to open and then forks off to exec the second one (fd_get.pl).
fd_pass.pl
#!/usr/bin/perl use v5.14; use warnings; #use Fcntl; my $FILE = shift or die "Need file to read\n"; # Don't set close-on-exec flag when we open a file handle local $^F = 10; say "Opening file"; open( my $in, '<', $FILE ) or die "Can't open '$FILE': $!\n"; # Clear the close-on-exec flag #my $flags = fcntl $in, F_GETFD, 0 or die "fcntl F_GETFD: $!"; #fcntl $in, F_SETFD, $flags & ~FD_CLOEXEC or die "fcntl F_SETFD: $!"; $SIG{CHLD} = 'IGNORE'; my $child_pid = fork(); if( $child_pid ) { # Parent while(1) { sleep 10 } } else { # Child say "Forking child process"; my $fd = fileno( $in ); exec( 'perl', './fd_get.pl', $fd ) or die "Could not exec: $!\n"; }
fd_get.pl
#!/usr/bin/perl use v5.14; use warnings; my $FD = shift or die "Need file descriptor\n"; open( my $in, '<&', $FD ) or die "Could not open file descriptor '$FD' +: $!\n"; while(<$in>) { chomp; say "Got in child: $_"; } close $in;
"There is no shame in being self-taught, only in not trying to learn in the first place." -- Atrus, Myst: The Book of D'ni.
|
---|
Replies are listed 'Best First'. | |
---|---|
Re: Passing a File Descriptor to a New Process on Windows
by Corion (Patriarch) on Apr 23, 2014 at 13:32 UTC | |
by tye (Sage) on Apr 23, 2014 at 14:26 UTC | |
by hardburn (Abbot) on Apr 24, 2014 at 01:59 UTC | |
Re: Passing a File Descriptor to a New Process on Windows
by eyepopslikeamosquito (Archbishop) on Apr 23, 2014 at 20:46 UTC |