Brian Candler
11/1/2004 11:19:00 AM
> To better explain, run the command_runner_shell_test.rb (file attached)
> as follows, to see the wackiness:
>
> $ ruby command_runner_shell_test.rb > output.txt
> $ cat output.txt
> Hello, world! 2 times!
> Hello, world! 2 times!
> Hello, world! 2 times!
>
> It should only print two times...
I don't think you said anything about the platform you are running under.
When I run your test under ruby 1.8.2p2 and FreeBSD 4.10, I get:
$ ruby command_runner_shell_test.rb
Hello, world! 2 times!
$
and uncommenting the section labelled "more wackiness", I get
$ ruby command_runner_shell_test.rb
Hello, world! 2 times!
Hello, world! 2 times!
Hello, world! 3 times!
Hello, world! 3 times!
Hello, world! 3 times!
$
which looks pretty good to me too.
If you are running under cygwin plus some Microsoft operating system, then
there is a quotation provided to me by the 'fortune' program which I think
is relevant:
--------------------------------------------------------------------------
There's a long-standing bug relating to the x86 architecture that
allows you to install Windows.
-- Matthew D. Fuller
--------------------------------------------------------------------------
:-)
Seriously, Windows is badly broken with regard to processes and pipes.
> All the weird thread and waiting for the 'R' character, trickery is so
> that I can guaranty that by the time I am calling the waitpid function,
> the process and PID still exist. In other words, it is possible for the
> child process to be so quick that it finishes executing before waitpid()
> gets called). So on a sidenote, does anyone know a cleaner way of
> performing interprocess communication? Like how do I tell the forked,
> child process that the parent is ready and waiting.
Why is this a problem? You can call waitpid on the child pid, and if it has
already terminated, then it will still be in the process table (as a zombie)
waiting for you to reap its exit status. That's unless someone has messed
with SIGCHLD.
You can prove this for yourself with a simple test program:
child = fork do
exit 42 # die immediately
end
sleep 5
Process.waitpid(child)
p $?
When I run this under FreeBSD, I get a delay of 5 seconds followed by
#<Process::Status: pid=1365,exited(42)>
If you use ps to see what is happening during the sleep, what I see is:
brian 1386 0.0 0.4 2544 1956 p7 S 11:15AM 0:00.01 ruby forktest.rb
brian 0 0.0 0.0 0 0 p7 ZW - 0:00.00 (ruby)
^
the zombie, waiting for collection
And even if the process had gone completely, you'd just get an error from
waitpid saying that the child did not exist (which is fine, since you know
it did exist at the time you forked, so it must have died since then). Only
if another process on the system had forked and re-used the same PID in the
mean time would that be a problem. Sensible Unixes are fine for this because
they allocate PIDs sequentially, and it's likely to be several minutes
before the same PID is re-used; however OpenBSD is completely broken, and
*could* re-use a PID within a microsecond of the previous child dying.
Regards,
Brian.