简体   繁体   English

如何在Ruby中模仿execl()

[英]How to mimic execl() in Ruby

I have an old application written in C++ which I am porting to Ruby. 我有一个用C ++编写的旧应用程序,正在移植到Ruby。

One section of the code uses execl() , in order to replace the process with a[n updated] copy of itself while maintaining open file descriptors (this application is a network service). 该代码的一部分使用execl() ,以便在维护打开文件描述符(此应用程序是网络服务)的同时,用自身的[n更新]副本替换该进程。

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

Didn't take long to figure out that Ruby has no execl() equivalent, but that you can fake it in part using Process::spawn and the :close_others option. 很快就发现Ruby没有等效的execl() ,但是您可以使用Process::spawn:close_others选项部分地伪造它。 Or, at least I should be able to according to the documentation : 或者,至少我应该能够按照文档进行操作

file descriptor inheritance: close non-redirected non-standard fds (3, 4, 5, ...) or not :close_others => true : don't inherit 文件描述符继承:是否关闭非重定向的非标准fds(3、4、5 ...):close_others => true:不继承

So, it seems to me that the following should spawn a new process which has access to all open file descriptors of the parent: 因此,在我看来,以下内容应该产生一个新进程,该进程可以访问父级的所有打开的文件描述符:

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

Which will allow the child access to the descriptors... however I cannot figure out how to exit the parent process without closing all the descriptors. 这将允许子级访问描述符...但是,我无法弄清楚如何在不关闭所有描述符的情况下exit父进程。 In other words, if I cause the parent to exit at the end of the code: 换句话说,如果我导致父级在代码末尾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

Then the descriptors are also closed for the child. 然后,描述符也为孩子关闭。

I have a feeling this is more a problem with my approach to process management than something specific to Ruby but I don't see what I'm doing wrong. 我感觉这是我的过程管理方法的问题,而不是Ruby特有的问题,但是我看不出我做错了什么。


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

EDIT1 Just before my call to Process.spawn (or Process.exec as @mata points out) I have a diagnostic output: EDIT1在我对Process.spawn调用之前(或@mata指出的Process.exec ),我有一个诊断输出:

system 'lsof -c ruby'

And another call to that just inside my recover_from_reboot method. 然后在我的recover_from_reboot方法中再次调用它。 This is the tail of output pre-reboot, you can see the listening server port and a connected client on the last two lines: 这是输出预重新引导的末尾,您可以在最后两行看到侦听服务器端口和连接的客户端:

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)

And this is what I see post-reboot: 这就是我看到的重启后的内容:

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

Again this is whether I try spawn or exec . 同样,这是我尝试使用spawn还是exec


EDIT2 Given my diagnostic output, I see that the server keeps binding to fd 7, and the client to 8. By adding EDIT2给定我的诊断输出,我看到服务器与fd 7保持绑定,客户端与8保持绑定。

7 => 7,
8 => 8,

to my options array, I am able to successfully persist these sockets across reboot using exec . 到我的options阵列,我能够使用exec在重新启动期间成功持久保留这些套接字。 It is feasible for me to manually add the server and [client1, client2,...] fd s to the options hash, but this seems very dirty when :close_others is supposed to do the heavy lifting for me. 对我来说,将服务器和[client1, client2,...] fd手动添加到选项哈希中是可行的,但是当:close_others应该为我做繁重的工作时,这似乎很脏。

This certainly isn't the solution I was looking for, but in the absence of any revelations about the :close_options Hash and Process.spawn or Process.exec , I wound up manually adding all the file descriptors I cared about to the option s array, which did the trick: 这当然不是我想要的解决方案,但是在没有有关:close_options Hash和Process.spawnProcess.exec的任何启示的情况下,我将所有关心的文件描述符手动添加到option s数组中,成功了:

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

I'll leave this answer unaccepted for a while, in case anyone comes along to set the record straight. 我会暂时保留这个答案,以防万一有人来打破记录。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM