kubo39's blog

ただの雑記です。

FFI gemでclone(2)を試してみた

ふと思い立ってclone(2)をFFI gemを使って呼び出してみた。 コードはこんなかんじ。

require 'ffi'

module LinuxClone
  extend FFI::Library
  ffi_lib 'libc.so.6'
  callback :f_c, [:void], :int
  attach_function :clone, [:f_c, :pointer, :int], :int
  attach_function :getpid, [], :int
end

stack = FFI::MemoryPointer.new(:char, 8096)
stack_top = FFI::Pointer.new(stack.address + 8096)

f = proc do
  puts LinuxClone.getpid
  return 0
end

puts LinuxClone.clone(f, stack_top, 0x20000000)
$ ruby -v
ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-linux]
 $ sudo strace ruby clone.rb 2>&1 > /dev/null | grep clone
execve("/usr/local/bin/ruby", ["ruby", "clone.rb"], [/* 18 vars */]) = 0
clone(child_stack=0x7f3e0220afb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f3e0220b9d0, tls=0x7f3e0220b700, child_tidptr=0x7f3e0220b9d0) = 17068
open("clone.rb", O_RDONLY|O_CLOEXEC)    = 7
lstat("/home/username/dev/kubo39/utils/clone.rb", {st_mode=S_IFREG|0664, st_size=401, ...}) = 0
clone(child_stack=0x7f3e001c1fb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f3e001c29d0, tls=0x7f3e001c2700, child_tidptr=0x7f3e001c29d0) = 17069
clone(child_stack=0x7f3e0400bac0, flags=CLONE_NEWPID) = 17070
^C

ちゃんとclone(2)を呼び出せているが、SIGINTを送らないと終了しなかった(straceなしならexit status: 0で終了する)。 ちょっと調べてもよくわからなかったので断念。

それと、このプログラムはgetpidがまれに表示できない。原因は単に親が先に死ぬため。

$ for n in {0..9}
> do
> sudo ruby clone.rb 
> done
clone(2) retval= 2827
getpid: 1
clone(2) retval= 2833
getpid: 1
clone(2) retval= 2838
getpid: 1
clone(2) retval= 2843
clone(2) retval= 2848
getpid: 1
clone(2) retval= 2853
getpid: 1
clone(2) retval= 2858
getpid: 1
clone(2) retval= 2863
getpid: 1
clone(2) retval= 2868
getpid: 1
clone(2) retval= 2873