Unix Programming with Perl
-
Upload
kazuho-oku -
Category
Technology
-
view
10.320 -
download
7
description
Transcript of Unix Programming with Perl
Unix Programming with Perl
Unix Programming with Perl
Cybozu Labs, Inc.
Kazuho Oku
Writing correct codeWriting correct code
tests aren’t enoughtests don’t ensure that the code is correct
writing correct code requires…knowledge of perl and knowledge of the OS
the presentation covers the ascpects of unix programming using perl, includingerrnofork and filehandlesUnix signals
Oct 16 2010 Unix Programming with Perl 2
Errno
Oct 16 2010 Unix Programming with Perl 3
The right way to “create a dir if not exists”The right way to “create a dir if not exists”
Is this OK?
if (! -d $dir) { mkdir $dir or die "failed to create dir:$dir:$!";}
Oct 16 2010 Unix Programming with Perl 4
The right way to “create a dir if not exists” (2)The right way to “create a dir if not exists” (2)
No!
if (! -d $dir) { # what if another process created a dir # while we are HERE? mkdir $dir or die "failed to create dir:$dir:$!";}
Oct 16 2010 Unix Programming with Perl 5
The right way to “create a dir if not exists” (3)The right way to “create a dir if not exists” (3)
The right way is to check the cause of the error when mkdir fails
Oct 16 2010 Unix Programming with Perl 6
The right way to “create a dir if not exists” (4)The right way to “create a dir if not exists” (4)
So, is this OK?
if (mkdir $dir) { # ok, dir created} elsif ($! =~ /File exists/) { # ok, directory exists} else { die "failed to create dir:$dir:$!";}
Oct 16 2010 Unix Programming with Perl 7
The right way to “create a dir if not exists” (5)The right way to “create a dir if not exists” (5)
No! The message stored in $! depends on OS and / or locale.
if (mkdir $dir) { # ok, dir created} elsif ($! =~ /File exists/) { # ok, directory exists} else { die "failed to create dir:$dir:$!";}
Oct 16 2010 Unix Programming with Perl 8
The right way to “create a dir if not exists” (6)The right way to “create a dir if not exists” (6)
The right way is to use Errno.
use Errno ();
if (mkdir $dir) { # ok, created dir} elsif ($! == Errno::EEXIST) { # ok, already exists} else { die "failed to create dir:$dir:$!";}
Oct 16 2010 Unix Programming with Perl 9
$! and Errno$! and Errno
$! is a dualvaris a number in numeric context (ex. 17)
equiv. to the errno global in C
is a string in string context (ex. “File exists”)equiv. to strerror(errno) in C
the Errno modulelist of constants (numbers) that errno ($! in
numeric context) may take
Oct 16 2010 Unix Programming with Perl 10
How to find the ErrnosHow to find the Errnos
perldoc -f mkdir doesn’t include a list of errnos it might return
see man 2 mkdirman mkdir will show the man page of the
mkdir commandspecify section 2 for system callsspecify section 3 for C library calls
Oct 16 2010 Unix Programming with Perl 11
How to find the ErrnosHow to find the Errnos
errnos on man include those defined by POSIX and OS-specific constants
the POSIX spec. can be found at opengroup.org, etc.http://www.opengroup.org/onlinepubs/
000095399/
Oct 16 2010 Unix Programming with Perl 12
Fork and filehandles
Oct 16 2010 Unix Programming with Perl 13
Filehandles aren’t cloned by forkFilehandles aren’t cloned by fork
fork clones the memory imageuses CoW (Copy-on-Write) for optimization
fork does not clone file handlesonly increments the refcount to the file
handle in the OSthe file is left open until both the parent and
child closes the file
seek position and lock states are shared between the processes
the same for TCP / UDP socketsOct 16 2010 Unix Programming with Perl 14
File handles aren’t cloned by fork (2)File handles aren’t cloned by fork (2)
Oct 16 2010 Unix Programming with Perl 15
memory
Parent Process
Operating System
File System
lock owner, etc.
File
memory
Another Process
memory
Child Process
fork
seek pos. / lock state, etc.
File Control Info.
open(file)
seek pos. / lock state, etc.
File Control Info.
open(file)
Examples of resource collisions due to forkExamples of resource collisions due to fork
FAQ“The SQLite database becomes corrupt”“MySQL reports malformed packet”
mostly due to sharing a single DBI connection created before calling forkSQLite uses file locks for access control
file lock needed for each process, however after fork the lock is shared between the processes
in the case of MySQL a single TCP (or unix) connection is shared between the processes
Oct 16 2010 Unix Programming with Perl 16
Examples of resource collisions due to fork (2)Examples of resource collisions due to fork (2)
The wrong code…
my $dbh = DBI->connect(...);
my $pid = fork;if ($pid == 0) { # child process $dbi->do(...);} else { # parent process $dbi->do(...);
Oct 16 2010 Unix Programming with Perl 17
How to avoid resource collisions after forkHow to avoid resource collisions after fork
close the file handle in the child process (or in the parent) right after forkonly the refcount will be decremented. lock
states / seek positions do not change
Oct 16 2010 Unix Programming with Perl 18
How to avoid collisions after fork (DBI)How to avoid collisions after fork (DBI)
undef $dbh in the child process doesn’t worksince the child process will run things such
as unlocking and / or rollbacks on the shared DBI connection
the connection needs to be closed, without running such operations
Oct 16 2010 Unix Programming with Perl 19
How to avoid collisions after fork (DBI) (2)How to avoid collisions after fork (DBI) (2)
the answer: use InactiveDestroy
my $pid = fork;if ($pid == 0) { # child process $dbh->{InactiveDestroy} = 1; undef $dbh; ...}
Oct 16 2010 Unix Programming with Perl 20
How to avoid collisions after fork (DBI) (3)How to avoid collisions after fork (DBI) (3)
if fork is called deep inside a module and can’t be modified, then…
# thanks to tokuhirom, kazeburoBEGIN { no strict qw(refs); no warnings qw(redefine); *CORE::GLOBAL::fork = sub { my $pid = CORE::fork; if ($pid == 0) { # do the cleanup for child process $dbh->{InactiveDestroy} = 1; undef $dbh; } $pid; };}
Oct 16 2010 Unix Programming with Perl 21
How to avoid collisions after fork (DBI) (4)How to avoid collisions after fork (DBI) (4)
other ways to change the behavior of forkPOSIX::AtFork (gfx)
Perl wrapper for pthread_atforkcan change the behavior of fork(2) called within
XS
forks.pm (rybskej)
Oct 16 2010 Unix Programming with Perl 22
Close filehandles before calling execClose filehandles before calling exec
file handles (file descriptors) are passed to the new process created by execsome tools (setlock of daemontools,
Server::Starter) rely on the feature
OTOH, it is a good practice to close the file handles that needn’t be passed to the exec’ed process, to avoid child process from accidentially using them
Oct 16 2010 Unix Programming with Perl 23
Close file handles before calling exec (2)Close file handles before calling exec (2)
my $pid = fork;if ($pid == 0) { # child process, close filehandles $dbh->{InactiveDestroy} = 1; undef $dbh; exec ...;...
Oct 16 2010 Unix Programming with Perl 24
Close file handles before calling exec (3)Close file handles before calling exec (3)
Some OS’es have O_CLOEXEC flagdesignates the file descriptors to be closed
when exec(2) is being calledis OS-dependent
linux supports the flag, OSX doesn’t
not usable from perl?
Oct 16 2010 Unix Programming with Perl 25
Unix Signals
Oct 16 2010 Unix Programming with Perl 26
SIGPIPESIGPIPE
“my network application suddenly dies without saying anything”
often due to not catching SIGPIPEa signal sent when failing to write to a
filehandleex. when the socket is closed by peer
the default behavior is to kill the process
solution: $SIG{PIPE} = 'IGNORE';downside: you should consult the return value
of print, etc. to check if the writes succeededOct 16 2010 Unix Programming with Perl 27
Using alarmUsing alarm
alarm can be used (together with SIG{ALRM}, EINTR) to handle timeouts
local $SIG{ALRM} = sub {};alarm($timeout);my $len = $sock->read(my $buf, $maxlen);if (! defined($len) && $! == Errno::EINTR) { warn 'timeout’; return;}
Oct 16 2010 Unix Programming with Perl 28
Pros and cons of using alarmPros and cons of using alarm
+ can be used to timeout almost all system calls (that may block)
− the timeout set by alarm(2) is a process-wide global (and so is $SIG{ALRM})use of select (or IO::Select) is preferable for
network access
Oct 16 2010 Unix Programming with Perl 29
Writing cancellable codeWriting cancellable code
typical use-case: run forever until receiving a signal, and gracefully shutdownex. Gearman::Worker
Oct 16 2010 Unix Programming with Perl 30
Writing cancellable code (2)Writing cancellable code (2)
make your module cancellable- my $len = $sock->read(my $buf, $maxlen);+ my $len;+ {+ $len = $sock->read(my $buf, $maxlen);+ if (! defined($len) && $! == Errno::EINTR) {+ return if $self->{cancel_requested};+ redo;+ }+ }...+ sub request_cancel {+ my $self = shift;+ $self->{cancel_requested} = 1;+ }
Oct 16 2010 Unix Programming with Perl 31
Writing cancellable code (3)Writing cancellable code (3)
The code that cancels the operation on SIGTERM$SIG{TERM} = sub { $my_module->request_cancel };$my_module->run_forever();
Or the caller may use alarm to set timeout$SIG{ALRM} = sub { $my_module->request_cancel };alarm(100);$my_module->run_forever();
Oct 16 2010 Unix Programming with Perl 32
Proc::Wait3Proc::Wait3
built-in wait() and waitpid() does not return when receiving signalsuse Proc::Wait3 instead
Oct 16 2010 Unix Programming with Perl 33
Further reading
Oct 16 2010 Unix Programming with Perl 34
Further readingFurther reading
this presentation is based on my memo for an article on WEB+DB PRESSif you have any questions, having problems
on Unix programming, please let me know
Oct 16 2010 Unix Programming with Perl 35