親子関係のないプロセス間でファイルディスクリプタを共有する - @tmtms のメモ
id:tmtms さんの日記に出てたFDを飛ばすやつでCGI書いてみました。
元ネタはいつものdRubyを使ったCGIで、オリジナルはdRubyで$stdin, $stdoutを公開してました。
このバージョンは$stdin, $stdoutを分散オブジェクトにする代わりに、FDを送ります。
#!/usr/local/bin/ruby require 'socket' def to_cgi(path) soc = UNIXSocket.new(path) env = Marshal.dump(ENV.to_hash) soc.write([env.size].pack('N')) soc.write(env) soc.send_io($stdin) soc.send_io($stdout) soc.read rescue nil end if __FILE__ == $0 path = "/tmp/#{File.basename($0, '.rb')}.soc" to_cgi(path) end
サーバ側の受け口はこれ。ちょっと長いよ。
require 'socket' class ToCGIServer def initialize(path, cgi, config={}) File.unlink(path) rescue nil @server = UNIXServer.new(path) owner = config[:owner] group = config[:group] if owner || group require 'etc' owner = Etc.getpwnam(owner).uid if owner group = Etc.getgrnam(group).uid if group File.chown(owner, group, path) end mode = config[:mode] File.chmod(mode, path) if mode @cgi = cgi end def run while true Thread.new(@server.accept) do |soc| begin on_client(soc) rescue ensure soc.close end end end end def on_client(soc) sz = soc.read(4) buf = soc.read(sz.unpack('N')[0]) env = Marshal.load(buf) sin = soc.recv_io sout = soc.recv_io @cgi.start(env, sin, sout) ensure sin.close if sin sout.close if sout end end
FDを送ることで、サーバからCGIプロセスへのコネクションとデータの転送が減るので効率が良くなります。
ENVをMarshalでない形式で送れば、CGIプロセス側をCで書くこともできるなー。mod_なんとか を作るのもありか。
dRubyを使ったCGIのサーバの形式だったらこんな風に直すだけ。
if __FILE__ == $0 book = WikiR::Book.new ui = WikiR::UI.new(book) DRb.start_service('druby://localhost:50830', ui) ToCGIServer.new('/tmp/to_cgi.soc', ui, {:mode => 0666}).run end
https://github.com/seki/ToCGIのappの下に感想戦で使ったWiki_Rを改造したものがあります。
100行もあるけど貼っちゃおう。
require 'kramdown' require 'webrick' require 'webrick/cgi' require 'drb/drb' require 'erb' require 'monitor' require 'to_cgi_server' class WikiR class Book include MonitorMixin def initialize super() @page = {} end def [](name) @page[name] || Page.new(name) end def []=(name, src) synchronize do page = self[name] @page[name] = page page.set_src(src) end end end class Page def initialize(name) @name = name set_src("# #{name}\n\nan empty page. edit me.") end attr_reader :name, :src, :html, :warnings def set_src(text) @src = text km = Kramdown::Document.new(text) @html = km.to_html @warnings = km.warnings end end class UI < WEBrick::CGI include ERB::Util extend ERB::DefMethod def_erb_method('to_html(page)', ERB.new(<<EOS)) <html> <head> <title>Kramdown</title> <script language="JavaScript"> function open_edit(){ document.getElementById('edit').style.display = "block"; } </script> </head> <body> <%= page.html %> <a href='javascript:open_edit()'>[edit]</a> <div id='edit' style='display:none;'> <form method='post'> <textarea name='text' rows="40" cols="50"><%=h page.src %></textarea> <input type='submit' name='ok' value='ok'/> </form> </div> </body> </html> EOS def initialize(book, *args) super(*args) @book = book end def do_GET(req, res) do_request(req, res) build_page(req, res) end alias :do_POST :do_GET def do_request(req, res) text ,= req.query['text'] return if text.nil? || text.empty? text = text.force_encoding('utf-8') @book[req.path_info] = text rescue end def build_page(req, res) res['content-type'] = 'text/html; charset=utf-8' res.body = to_html(@book[req.path_info]) end end end if __FILE__ == $0 book = WikiR::Book.new ui = WikiR::UI.new(book) DRb.start_service('druby://localhost:50830', ui) ToCGIServer.new('/tmp/to_cgi.soc', ui, {:mode => 0666}).run end
- Rubyist Magazine - Ruby コードの感想戦 【第 1 回】 WikiR
- Rubyist Magazine - Ruby コードの感想戦 【第 2 回】 WikiR
- 親子関係のないプロセス間でファイルディスクリプタを共有する - @tmtms のメモ

The dRuby Book: Distributed and Parallel Computing with Ruby
- 作者: Masatoshi Seki,Makoto Inoue
- 出版社/メーカー: Pragmatic Bookshelf
- 発売日: 2012/03/19
- メディア: ペーパーバック
- クリック: 1回
- この商品を含むブログ (24件) を見る