ruby list child pids - ruby

How can I get pids of all child processes which were started from ruby script?

You can get the current process with:
Process.pid
see http://whynotwiki.com/Ruby_/_Process_management for further details.
Then you could use operating specific commands to get the child pids. On unix based systems this would be something along the lines of
# Creating 3 child processes.
IO.popen('uname')
IO.popen('uname')
IO.popen('uname')
# Grabbing the pid.
pid = Process.pid
# Get the child pids.
pipe = IO.popen("ps -ef | grep #{pid}")
child_pids = pipe.readlines.map do |line|
parts = line.lstrip.split(/\s+/)
parts[1] if parts[2] == pid.to_s and parts[1] != pipe.pid.to_s
end.compact
# Show the child processes.
puts child_pids
Tested on osx+ubuntu.
I admit that this probably doesn't work on all unix systems as I believe the output of ps -ef varies slightly on different unix flavors.

Process.fork responds with the PID of the child spawned. Just keep track of them in an array as you spawn children. See http://ruby-doc.org/core/classes/Process.html#M003148.

Can be also done using sys-proctable gem:
require 'sys/proctable'
Sys::ProcTable.ps.select{ |pe| pe.ppid == $$ }

This is actually quiet complicated and is platform specific. You actually cannot find all sub-processes if they deliberately try to hide.
If you want to just kill spawned processes there are many options. For a test framework I chose two:
1. spawn processes with pgid => true
2. insert variable MY_CUSTOM_COOKIE=asjdkahf, then find procs with that cookie in environment and kill it.
FYI using ps to find out process hierarchy is very unreliable. If one process in the chain exits, then its sub-processes get a parent pid of 1 (on linux at least). So it's not worth implementing.

Related

Killing process group from Ruby kills my whole computer

I have a script (script.sh) that spawns a whole lot of child processes. If I run the script from the shell via ./script.sh, I can kill the whole process tree via
kill -- -<PID>
where PID is the process ID of the script.sh process (this apparently equals the group ID).
However, if I spawn the script from Ruby via
pid = Process.spawn(script.sh)
I cannot manage to kill the process tree.
Process.kill(9,pid)
only kills the parent process. And even worst, the following
Process.kill(9,-Process.getpgid(pid)) ### Don't try this line at home
terminates my computer.
Trying to kill the processes via
system("kill -- -#{pid}")
also fails.
How am I supposed to kill this process tree from Ruby?
I think I have found the solution. Spawning the process as
pid = Process.spawn(script.sh, :pgroup => true)
makes me able to kill the process group via
Process.kill(9,-Process.getpgid(pid))
It looks like bash groups processes by default, while Spawn doesn't enable this by default.

Get process status by pid in Ruby

Is there a way to get a process's child process status based on its PID in Ruby?
For example, in Python you can do psutil.Process(pid).status
I don't know of a portable ruby method to get process state of a running process. You can do Process.wait and check $?.exitstatus, but that doesn't look like what you want. For a posix solution, you could use
`ps -o state -p #{pid}`.chomp
to get the letter code ps produces for process state
PROCESS STATE CODES
Here are the different values that the s, stat and state output specifiers
(header "STAT" or "S") will display to describe the state of a process.
D Uninterruptible sleep (usually IO)
R Running or runnable (on run queue)
S Interruptible sleep (waiting for an event to complete)
T Stopped, either by a job control signal or because it is being traced.
W paging (not valid since the 2.6.xx kernel)
X dead (should never be seen)
Z Defunct ("zombie") process, terminated but not reaped by its parent.
I was looking for the same thing. It's a shame ProcessStatus doesn't seem to be able to get initialized from a live pid. This is vital stuff if you want to do anything like a safe timed kill of a child process.
In any case,
it's the second line in /proc/$pid/status if you're on Linux.:
status_line = File.open("/proc/#{pid}/status") {|f| f.gets; f.gets }
Most likely much much faster than anything involving an external program.
On OS X, I setup a string:
outputstring="ps -O=S -p #{mypid}"
then execute it in a %x call:
termoutput=%x[#{outputstring}]
I can display that if needed, or just keep the output clean and act on the State I found with the call.

Ruby run two processes, output results to terminal, and end safely

In Ruby, I'm running a system("command here") that is constantly watching changes for files, similar to tail. I'd like my program to continue to run and not halt at the system() call. Is there a way in Ruby to create another process so both can run independently, output results to the terminal, and then when you exit the program all processes the application created are removed?
Just combine spawn and waitall:
spawn 'sleep 6'
spawn 'sleep 8'
Process.waitall
You don't want to use system as that waits for the process to complete. You could use spawn instead and then wait for the processes (to avoid zombies). Then, when you want to exit, send a SIGTERM to your spawned processes. You could also use fork to launch your child processes but spawn is probably easier if you're using external programs.
You could also use process groups instead of tracking all the process IDs, then a single Process.kill('TERM', -process_group_id) call would take care of things. Your child processes should end up in the same process group but there is Process.setpgid if you need it.
Here's an example that uses fork (easier to get it all wrapped in one package that way).
def launch(id, sleep_for)
pid = Process.fork do
while(true)
puts "#{id}, pgid = #{Process.getpgid(Process.pid())}, pid = #{Process.pid()}"
sleep(sleep_for)
end
end
# No zombie processes please.
Process.wait(pid, Process::WNOHANG)
pid
end
# These just forward the signals to the whole process group and
# then immediately exit.
pgid = Process.getpgid(Process.pid())
Signal.trap('TERM') { Process.kill('TERM', -pgid); exit }
Signal.trap('INT' ) { Process.kill('INT', -pgid); exit }
launch('a', 5)
launch('b', 3)
while(true)
puts "p, pgid = #{Process.getpgid(Process.pid())}, pid = #{Process.pid()}"
sleep 2
end
If you run that in one terminal and then kill it from another (using the shell's kill command)you'll see that the children are also killed. If you remove the "forward this signal to the whole process group" Signal.trap stuff, then a simple SIGTERM will leave the children still running.
All of this assumes that you're working on some sort of Unixy system (such as Linux or OSX), YMMV anywhere else.
One more vote for using Spawn. We use it in Production a lot and it's very stable.

Find a process ID by name

How can I find a pid by name or full command line in Ruby, without calling an external executable?
I am sending SIGUSR2 to a process whose command line contained ruby job.rb. I would like to do the following without the call to pgrep:
uid = Process.uid
pid = `pgrep -f "ruby job.rb" -u #{uid}`.split("\n").first.to_i
Process.kill "USR2", pid
How to do this depends on your operating system. Assuming Linux, you can manually crawl the /proc filesystem and look for the right command line. However, this is the same thing that pgrep is doing, and will actually make the program less portable.
Something like this might work.
def get_pid(cmd)
Dir['/proc/[0-9]*/cmdline'].each do|p|
if File.read(p) == cmd
Process.kill( "USR2", p.split('/')[1] )
end
end
end
Just be careful poking around in /proc.
A quick google search came up with sys_proctable, which should let you do this in a portable way.
Disclaimer: I don't use Ruby, can't confirm if this works.
Debian based systems find pid with pidof command.
Some kill proccess function with ruby:
def killPid(cmd)
pid=exec("pidof #{cmd}")
Process.kill "USR2", pid
end

how to controller (start/kill) a background process (server app) in ruby

i'm trying to set up a server for integration tests (specs actually) via ruby and can't figure out how to control the process.
so, what i'm trying to do is:
run a rake task for my gem that executes the integration specs
the task needs to first start a server (i use webrick) and then run the specs
after executing the specs it should kill the webrick so i'm not left with some unused background process
webrick is not a requirement, but it's included in the ruby standard library so being able to use it would be great.
hope anyone is able to help!
ps. i'm running on linux, so having this work for windows is not my main priority (right now).
The standard way is to use the system functions fork (to duplicate the current process), exec (to replace the current process by an executable file), and kill (to send a signal to a process to terminate it).
For example :
pid = fork do
# this code is run in the child process
# you can do anything here, like changing current directory or reopening STDOUT
exec "/path/to/executable"
end
# this code is run in the parent process
# do your stuffs
# kill it (other signals than TERM may be used, depending on the program you want
# to kill. The signal KILL will always work but the process won't be allowed
# to cleanup anything)
Process.kill "TERM", pid
# you have to wait for its termination, otherwise it will become a zombie process
# (or you can use Process.detach)
Process.wait pid
This should work on any Unix like system. Windows creates process in a different way.
I just had to do something similar and this is what I came up with. #Michael Witrant's answer got me started, but I changed some things like using Process.spawn instead of fork (newer and better).
# start spawns a process and returns the pid of the process
def start(exe)
puts "Starting #{exe}"
pid = spawn(exe)
# need to detach to avoid daemon processes: http://www.ruby-doc.org/core-2.1.3/Process.html#method-c-detach
Process.detach(pid)
return pid
end
# This will kill off all the programs we started
def killall(pids)
pids.each do |pid|
puts "Killing #{pid}"
# kill it (other signals than TERM may be used, depending on the program you want
# to kill. The signal KILL will always work but the process won't be allowed
# to cleanup anything)
begin
Process.kill "TERM", pid
# you have to wait for its termination, otherwise it will become a zombie process
# (or you can use Process.detach)
Process.wait pid
rescue => ex
puts "ERROR: Couldn't kill #{pid}. #{ex.class}=#{ex.message}"
end
end
end
# Now we can start processes and keep the pids for killing them later
pids = []
pids << start('./someprogram')
# Do whatever you want here, run your tests, etc.
# When you're done, be sure to kill of the processes you spawned
killall(pids)
That's about all she wrote, give it a try and let me know how it works.
I have tried fork, but it has kind of problems when ActiveRecord is involved in both the processes. I would suggest Spawn plugin (http://github.com/tra/spawn). It does fork only but takes care of ActiveRecord.

Resources