Question

In a ruby script, I start more virtual shells, each managed by a shell manager object, like so:

@shell = PTY.spawn 'env PS1="\w>" TERM=dumb COLUMNS=63 LINES=21 sh -i'

At some later point in time, I would like to destroy this instance and also kill the associated shell process. Sadly, I can't get anything to work properly. Here's what I tried, in order of probability to work:

  • Nothing, that is, expecting the shell proc gets closed when the managing object gets destroyed.
  • Killing all processes running on the shell (this works) with the kill command, and then killing the shell itself with system("kill #{@shell[2]"). This has no effect.
  • Using -9 in the above. This leaves the shell process defunct.

All the shells get closed when the ruby program exits, but I want to kill them while keeping the program running. Anyone encounter something like this before?

Was it helpful?

Solution

The problem is zombies. Yes, really.

All Unix-style kernel's leave the process around until someone waits for it. (That's in order to keep track of the PID, the exit status, and a bit of other stuff.) They are called zombies and have a Z state in the ps(1) listing. You can't kill them, because they are already dead. They go away when you wait for them.

So here is how to clean up your @shell object:

@shell[0].close
@shell[1].close
begin
  Process.wait @shell[2]
rescue PTY::ChildExited
end

You may not need the rescue block depending on whether you have higher level layers catching exceptions too broadly. (Sigh, like my irb.)

By the way, the reason your process finally vanished when the Ruby program exited is because then the zombie also became an orphan (no parent process) and either the shell or init(8) will eventually wait for all orphans.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top