man 3 exec says:

The exec() family of functions replaces the current process image with a new process image.

What this means is when you fork a process then run exec with a command. The new forked process will convert into the desired command.

For example:

pid = fork do
  exec('ls')
end

When the above ruby code is run. The a child process will be forked with shared file descriptors to $stdin, $stdout and $stderr. The exec call will convert the child ruby process into an ls process which will write the listing of the current directory to the stdout stream and immediately exit.

Here’s another example:

# exec.rb

$stdout.write("Process: #{Process.pid}. Press enter to continue.")
$stdin.readline

fork do
  $stdout.write("In child process: #{Process.pid}")
  exec("vim #{$0}")
end

Process.waitall

The output in pstree would look something like this: (pstree -p )

ruby(20435)-+-vim(20641)---{vim}(20644)
            `-{ruby}(20642)

The parent ruby process starts off by spawning another ruby process which then get’s converted to a vim process.

If you have ever wondered how the ruby ‘system’ method might work. It’s something like the following.

def my_system(command)
  pid = fork do
    begin
      exec(command)
    rescue Errno::ENOENT
      exit 1
    end
  end
  _, status = Process.wait2(pid)
  status.success? || nil
end

p my_system('ls')
p my_system('lol')

In unix if a process returns a 0 as it’s exit code then it is considered successful. Any non zero value is considered an error. In the above example the call to “Process.wait2” returns the child process id as well as a status object. You can inspect the exit status of the last executed process in bash by inspecting the “$?” value.

$ echo $?

resources

comments powered by Disqus