如何在Ruby中模仿execl()

时间:2014-04-19 23:59:32

标签: ruby linux process exec file-descriptor

我有一个用C ++编写的旧应用程序,我正在移植到Ruby。

代码的一部分使用execl(),以便在维护打开文件描述符的同时用自身的[n更新]副本替换进程(此应用程序是网络服务)。

  if ( execl( "./my-app", "-restart", fd.c_str(), NULL ) < 0 ) {

没有花很长时间才弄清楚Ruby没有execl()等价物,但您可以使用Process::spawn:close_others选项部分伪造它。或者,至少我应该能够根据documentation

  

文件描述符继承:关闭非重定向的非标准fds(3,4,5,...)或不   :close_others =&gt;是的:不要继承

所以,在我看来,以下应该会产生一个新进程,它可以访问父进程的所有打开文件描述符:

server_fd = @server.to_i
env = {
  "APP_REBOOT"    => "true",
  "APP_SERVER_FD" => server_fd.to_s,
}
command = "ruby my-app.rb"
options = {
  :in           => :in,
  :out          => :out,
  :err          => :err,
  :close_others => false,
}
pid = Process.spawn env, command, options
Process.detach pid

这将允许孩子访问描述符......但是我无法弄清楚如何在不关闭所有描述符的情况下exit父进程。换句话说,如果我在代码末尾导致父exit

server_fd = @server.to_i
env = {
  "APP_REBOOT"    => "true",
  "APP_SERVER_FD" => server_fd.to_s,
}
command = "ruby my-app.rb"
options = {
  :in           => :in,
  :out          => :out,
  :err          => :err,
  :close_others => false,
}
pid = Process.spawn env, command, options
Process.detach pid
exit # ADDED THIS LINE

然后,孩子们也关闭了描述符。

我觉得这对我的流程管理方法来说比Ruby的特定问题更严重,但我不知道自己做错了什么。


$ ruby -v
ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-linux]

EDIT1 就在我打电话给Process.spawn(或Process.exec @mata指出)之前,我有一个诊断输出:

system 'lsof -c ruby'

在我的recover_from_reboot方法内再次调用它。这是输出预重启的尾部,您可以在最后两行看到监听服务器端口和连接的客户端:

ruby    8957 chris    0u   CHR    136,1      0t0        4 /dev/pts/1
ruby    8957 chris    1u   CHR    136,1      0t0        4 /dev/pts/1
ruby    8957 chris    2u   CHR    136,1      0t0        4 /dev/pts/1
ruby    8957 chris    3r  FIFO      0,8      0t0 12213372 pipe
ruby    8957 chris    4w  FIFO      0,8      0t0 12213372 pipe
ruby    8957 chris    5r  FIFO      0,8      0t0 12213373 pipe
ruby    8957 chris    6w  FIFO      0,8      0t0 12213373 pipe
ruby    8957 chris    7u  IPv4 12213374      0t0      TCP localhost.localdomain:boks-servc (LISTEN)
ruby    8957 chris    8u  IPv4 12213423      0t0      TCP localhost.localdomain:boks-servc->localhost.localdomain:45249 (ESTABLISHED)

这就是我在重新启动后看到的内容:

ruby    8957 chris    3r  FIFO    0,8      0t0 12203947 pipe
ruby    8957 chris    4w  FIFO    0,8      0t0 12203947 pipe
ruby    8957 chris    5r  FIFO    0,8      0t0 12203948 pipe
ruby    8957 chris    6w  FIFO    0,8      0t0 12203948 pipe

这是我尝试spawn还是exec


EDIT2 鉴于我的诊断输出,我看到服务器保持绑定到fd 7,客户端保持绑定到8.通过添加

7 => 7,
8 => 8,

到我的options数组,我可以使用exec在重新启动时成功保留这些套接字。我可以手动将服务器和[client1, client2,...] fd添加到选项哈希中,但是当:close_others应该为我做繁重的工作时,这似乎很脏。

1 个答案:

答案 0 :(得分:0)

这当然不是我想要的解决方案,但是在没有关于:close_options Hash和Process.spawnProcess.exec的任何启示的情况下,我结束了手动添加所有文件描述符我关心option的数组,这就是诀窍:

server_fd  = self.server.to_i
client_fds = self.clients.map { |c| c.get_fd }
env = {
  "APP_REBOOT"     => "true",
  "APP_SERVER_FD"  => server_fd.to_s,
  "APP_CLIENT_FDS" => Marshal.dump(client_fds),
}
options = {
  :in           => :in,
  :out          => :out,
  :err          => :err,
  :close_others => false,
}
# Add the server socket to the options Hash.
options[server_fd] = server_fd
# Add all the client sockets to the options Hash.
@clients.each { |c| options[c.get_fd] = c.get_fd }
# Begin anew.
Process.exec env, App.binary.to_s, options

我会暂时不接受这个答案,以防有人提出直接记录。