Server program を Thread に頼らず作る

訳あって、Server program を Thread に頼らず、fork を使って作ってみることにした。

#!/usr/bin/env ruby

require "socket"
require 'time'

Signal.trap('CHLD') do
  puts "waiting"
  Process.waitall
end


print $$.to_s + ": parent pid\n"
accepter = TCPserver.open(12345)

loop do
  io = accepter.accept
  fork do
    io.write(Time.now.iso8601 + "\n")
    io.close
    print $$.to_s + ": child pid after fork\n"
    exit!(0)
  end

  io.close
  print $$.to_s + ": parent pid after fork\n"

end

ポイントは 2 つ。

  1. socket は子プロセス側だけではなく、親プロセス側も close すること。そうでないと、クライアント側からみて、切断がされない。
  2. シグナル SIGCHLD に Process.wait を実行する trap を仕掛けること。そうでないと、ゾンビプロセスを看取ることができない(=ゾンビが残る状態になる)

後者についてはこれまで良く理解していなかった。15年ぐらい前に C で作った Server program はたぶんこの問題の影響か、数日稼働すると停止するという困った動きをしてくれていた。