So I do realize a similar question has been asked Find a process ID by name but the thing is, it really returns all PIDs with their names, including the spawned children of that process. My question is how can I only get the PID of the parent process and not one of it's children?
The sys-proctable gem can do that, here is a minimal example for getting the PID of the ruby.exe process on the system:
require 'sys/proctable'
def get_process_id(name)
Sys::ProcTable.ps.detect{|p| p.name == name}&.pid
end
puts get_process_id("ruby.exe")
That doesn't guarantee you will find the parent process tho, you will instead get the process with the lowest PID.
To actually find the "root process", you need to further select processes by checking if their parent process is either non-existent or a different process:
require 'sys/proctable'
def get_parent_process_id(name)
# generate a hash mapping pid -> process info
processes = Sys::ProcTable.ps.map{|p| [p.pid, p]}.to_h
# find the first matching process that has either no parent or a parent
# that doesn't match the process name we're looking for
processes.values.each do |p|
next if p.name != name
if processes[p.ppid].nil? || processes[p.ppid].name != name
return p.pid
end
end
nil
end
puts get_parent_process_id("chrome.exe")
Related
I am primarily a java guy tinkering with Process.spawn and have hit a bit of a wall. Basically I tossed together a few rspec classes that do puts to tinker with it. This worked pretty well. Then I tossed a sleep in and the whole process just dies when a sleep is reached. I am not sure why, I thought that spawning gets around the shared resource issues of threads. Below is what I was using to make the processes
test_files = Dir["../spec/*.rb"]
test_files.each do |file|
pid = Process.spawn("rspec #{file}")
p pid
end
Process.wait
There is very little going on with the tests in the spec, below is a sample one.
it 'should another thing' do
sleep(0.3)
p "another1"
end
Could someone be so kind as to explain where I am going wrong?
It worked for me, I'm assuming you think it's not working because you get something like this:
script.rb:6:in `wait': No child processes (Errno::ECHILD)
from script.rb:6:in `<main>'
And I'm assuming you get that because you have this script in maybe a lib directory or something, but you're running it from the root of your app, so when it does Dir["../spec/*.rb"] you're expecting it to go up to the root of your app, and then down into the spec directory, but it is based on your pwd, so it goes to the parent directory of the app and looks for a spec directory, which doesn't exist.
Some things I'd advocate in this scenario, check that it finds the files before trying to spawn them. Use absolute filepaths, unless you intentionally want it to be based on where you run the program from. If this is all true, try writing it like this:
spec_glob = File.expand_path('../spec/*.rb', __dir__)
test_files = Dir[spec_glob]
p test_files
test_files.each do |file|
pid = Process.spawn("rspec #{file}")
p pid
end
Process.wait
How do you get the parent process id of a process that is not the current process in Ruby?
I've checked Ruby's Process module, but it only seems to provide a means to access the PPID of the current process.
I also checked google for anything on the subject, but the first two pages seemed to only contain links regarding how to use the aforementioned Process module.
I was hoping to do this without having to rely too much on the underlying OS, but whatever works.
Shell out:
1.9.3p429 :001 > `ps -p 7544 -o ppid=`.strip
=> "7540"
Process.ppid returns the parent process id.
http://ruby-doc.org/core-2.4.1/Process.html#method-c-ppid
You can just remember it in a variable:
parent_pid = Process.pid
Process.fork do
child_pid = Process.pid
puts parent_pid, child_pid
# do stuff
exit
end
Process.wait
# 94791
# 94798
alternatively, if you need the information on the level of the parent process:
parent_pid = Process.pid
child_pid = Process.fork do
# do stuff
exit
end
Process.wait
puts parent_pid, child_pid
# 6361
# 6362
I am using the open4 gem to wrap system calls to a potentially long-running third-party command line tool. The tool may sometimes fail, keeping two processes busy, and partially blocking a pipeline, as the parent process is part of a pool of worker scripts (serving a Beanstalk queue). From outside of the system, I can identify a stuck worker script and its process id programatically, based on the data model of what is being processed. Inside the Open4.open4 block, I can identify the child process id.
I'd like to set up the Open4 block so that when I send a SIGTERM to the parent worker process, it forwards on the SIGTERM to the child. In addition, if the child process has still failed to exit after a short wait, I want to send a SIGKILL to the child process. In both cases, I'd then like the parent process to respond as normal to the SIGTERM it was sent.
This is all being done so I can expose a "stop" button in a customer services app, so non-technical team members have a tool to manage their way out of a situation with a blocked queue.
I have found some related questions in SO - e.g. How to make child process die after parent exits? - but the answers are not really usable for me from Ruby application code.
Here is a current implementation in Ruby that I have tested on my Mac:
Test stand-in for "bad" process that won't always respond to SIGTERM:
# Writing to a log file shows whether or not a detached process continues
# once the parent has closed IO to it.
$f = open( 'log.txt', 'w' );
def say m
begin
$f.puts m
$f.flush
$stderr.puts m
rescue Exception => e
# When the parent process closes, we get
# #<Errno::EPIPE: Broken pipe - <STDERR>> in this
# test, but with a stuck child process, this is not
# guaranteed to happen or cause the child to exit.
$f.puts e.inspect
$f.flush
end
end
Signal.trap( "TERM" ) { say "Received and ignored TERM" }
# Messages get logged, and sleep allows test of manual interrupts
say "Hello"
sleep 3
say "Foo Bar Baz"
sleep 3
say "Doo Be Doo"
sleep 3
say "Goodbye"
$f.close
Test Open4 block (part of a "worker" test script):
Open4.open4(#command) do | pid, stdin, stdout, stderr |
begin
stderr.each { |l|
puts "[#{pid}] STDERR: #{l}" if l
}
rescue SignalException => e
puts "[#{$$}] Received signal (#{e.signo} #{e.signm}) in Open4 block"
# Forward a SIGTERM to child, upgrade to SIGKILL if it doesn't work
if e.signo == 15
begin
puts "[#{$$}] Sending TERM to child process"
Process.kill( 'TERM', pid )
timeout(3.0) { Process.waitpid( pid ) }
rescue Timeout::Error
puts "[#{$$}] Sending KILL to child process"
Process.kill( 'KILL', pid )
end
end
raise e
end
end
Typical output if I start this up, and run e.g. kill -15 16854:
[16855] STDERR: Hello
[16854] Received signal (15 SIGTERM) in Open4 block
[16854] Sending TERM to child process
[16854] Sending KILL to child process
Contents of log file for same test:
Hello
Received and ignored TERM
Foo Bar Baz
The code is IMO a bit unwieldy, although it appears to work as I want. My questions:
Is the above attempt ok, or fatally flawed in the use case I need it for?
Have I missed a cleaner way of doing the same thing using existing Open4 and core Ruby methods?
I am trying to run multiple instances of the same code/script using the Daemons gem. I've been playing around with it in an IRB session and can't seem to get the functionality I'm looking for. I want to run the same script multiple times keeping track of the PID so that I can manually start and stop processes. More specifically, I have a rails model whose instance will control a single process and so I will need to start and stop it using something like
mydaemon = MyDaemon.create
mydaemon.start # starts the process
mydaemon.stop # stops the process
However, in order to achieve something like this I wanted to store the PIDs for the process in active record. When I run the following code in IRB:
require 'rubygems' # if you use RubyGems
require 'daemons'
task1 = Daemons.call(:multiple => true) do
loop {
File.open("/tmp/daemon_test.log", "w") {|f| f.write(Time.now.to_s + "\n")}
sleep 5
}
end
the task automatically starts (without ever calling start on task1), and instead of returning the pid, it returns the Daemons::Application object associated with task1. I only have access to the pid when I start a second instance of task1 i.e.:
task1.start
=> 574 # PID of process but now I have 2 proc's running, one of which I don't know the PID of
Am I using Daemons wrong? or is there no way to get the PID of the very first process that starts automatically when calling Daemons.call?
Thanks,
Tomek
The docs say that an Daemons::Application object has a public attribute pid which returns a Daemons::Pid object, so try using that task1.pid.pid.
I want to offload a block of code in my main process to child process to make it run concurrently. I also want to have the PID of the spawned child process so I can monitor and kill it if necessary.
In addition to Chris' great answer, remember to call Process.wait from your master in order to reap your child process, else you'll leave zombies behind.
Example as requested in comments:
pid = Process.fork do
puts "child, pid #{Process.pid} sleeping..."
sleep 5
puts "child exiting"
end
puts "parent, pid #{Process.pid}, waiting on child pid #{pid}"
Process.wait
puts "parent exiting"
You can use the fork kernel method. Here is an example:
#!/usr/bin/env ruby
puts "This is the master process."
child_pid = fork do
puts "This is the child process"
exit
end
puts "The PID of the child process is #{child_pid}"
The fork method returns the PID of the process it forks and executes any code in the block passed. Like regular Ruby blocks it keeps the bindings of the parent process.
It is a good idea to make your forked process exit.
In 1.9 you can use Process.spawn command.
See also http://en.wikibooks.org/wiki/Ruby_Programming/Running_Multiple_Processes
If you are happy to use Threads, rather than Processes, then something like this may be a bit more scaleable to more-than-one fork:
def doit(x)
sleep(rand(10))
puts "Done... #{x}"
end
thingstodo = ["a","b","c","d","e","f","g"]
tasklist = []
# Set the threads going
thingstodo.each { |thing|
task = Thread.new(thing) { |this| doit(this) }
tasklist << task
}
# Wait for the threads to finish
tasklist.each { |task|
task.join
}
Please see John Topley's excellent comments and reference, below, regarding the Ruby execution model and its restrictions.
Just edited to correct a glaring error (no assignment to task), and to follow #(Jason King)'s advice.