【问题标题】:How to mimic execl() in Ruby如何在 Ruby 中模拟 execl()
【发布时间】:2014-04-19 23:59:31
【问题描述】:

我有一个用 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 => true : 不要继承

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

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(或@mata 指出的Process.exec)之前,我有一个诊断输出:

system 'lsof -c ruby'

在我的recover_from_reboot 方法中再次调用它。这是输出 pre-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,...] fds 添加到选项哈希中,但是当 :close_others 应该为我做繁重的工作时,这似乎很脏。

【问题讨论】:

  • 为什么不直接使用exec?它完成了 execl 在 C 中所做的一切......
  • 好电话@mata,不幸的是我仍然看到相同的结果。

标签: ruby linux process exec file-descriptor


【解决方案1】:

这当然不是我正在寻找的解决方案,但是在没有任何关于 :close_options 哈希和 Process.spawnProcess.exec 的信息的情况下,我最终手动添加了我关心的所有文件描述符options 数组,成功了:

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

我会暂时不接受这个答案,以防有人过来澄清事实。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-22
    • 2011-03-10
    • 1970-01-01
    • 2011-11-07
    相关资源
    最近更新 更多