You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Ruby 2.6.5如何调用POSIX的tcsetpgrp设置终端前台进程组?

Solution for Ruby 2.6.5: Implementing Process Group & Foreground Terminal Control

Great question! Since Ruby 2.6.5 doesn't include built-in wrappers for tcsetpgrp, we have two reliable approaches to achieve your goal—both aligning with your Python implementation's logic. Let's dive in:

1. Use FFI to Call POSIX Functions Directly (No Compilation Hassle)

The easiest way to avoid writing and troubleshooting custom .so files is using Ruby's FFI (Foreign Function Interface) to directly invoke the tcsetpgrp and tcgetpgrp functions from the system's libc library. This skips manual compilation entirely.

Step-by-Step Implementation

First, install the ffi gem:

gem install ffi

Then use this code to replicate your Python logic:

require 'ffi'

# Wrap libc's terminal process group functions
module LibC
  extend FFI::Library
  ffi_lib FFI::Library::LIBC  # Auto-links to system libc

  attach_function :tcgetpgrp, [:int], :pid_t
  attach_function :tcsetpgrp, [:int, :pid_t], :int
end

def become_tty_fg
  # Create a new process group (equivalent to Python's os.setpgrp())
  Process.setpgid(0, 0)

  # Ignore SIGTTOU temporarily to avoid being stopped when modifying the terminal
  old_handler = Signal.trap(:SIGTTOU, :IGNORE)

  # Open the controlling terminal to get a file descriptor
  tty_file = File.open('/dev/tty', 'r+')
  tty_fd = tty_file.fileno

  # Set the current process group as the terminal's foreground group
  LibC.tcsetpgrp(tty_fd, Process.getpgid(0))

  # Restore SIGTTOU handling so Ctrl+Z can suspend the child process
  Signal.trap(:SIGTTOU, old_handler)
  tty_file.close
end

# Example usage: Spawn a child process with the foreground terminal control
spawn('sleep 60', pgroup: false, preexec: method(:become_tty_fg))
Process.wait

Key Notes:

  • Process.setpgid(0, 0) makes the child process the leader of a new process group (matches Python's os.setpgrp()).
  • The pgroup: false flag in spawn tells Ruby not to auto-create a process group, letting us handle it manually.
  • FFI handles linking to libc automatically, so you won't run into missing .so file issues.

2. Fix the C Extension .so Problem (If You Prefer Compiled Code)

If you want to write a custom C extension, your earlier issue with missing .so files likely stems from incorrect compilation. Use Ruby's built-in mkmf tool to generate a proper Makefile that links to libc automatically.

Step-by-Step Implementation

  1. Create a C source file (ttygrp.c):
#include "ruby.h"
#include <unistd.h>

// Wrapper for tcsetpgrp
static VALUE tcsetpgrp_wrapper(VALUE self, VALUE fd, VALUE pgrp) {
  int ret = tcsetpgrp(NUM2INT(fd), NUM2PIDT(pgrp));
  return INT2NUM(ret);
}

// Wrapper for tcgetpgrp
static VALUE tcgetpgrp_wrapper(VALUE self, VALUE fd) {
  pid_t pgrp = tcgetpgrp(NUM2INT(fd));
  return PIDT2NUM(pgrp);
}

// Register functions with Ruby's Process module
void Init_ttygrp(void) {
  VALUE mProcess = rb_const_get(rb_cObject, rb_intern("Process"));
  rb_define_module_function(mProcess, "tcsetpgrp", tcsetpgrp_wrapper, 2);
  rb_define_module_function(mProcess, "tcgetpgrp", tcgetpgrp_wrapper, 1);
}
  1. Create an extconf.rb file to generate the Makefile:
require 'mkmf'
create_makefile('ttygrp')
  1. Compile the extension:
ruby extconf.rb
make
  1. Use the extension in your Ruby script:
require './ttygrp'

def become_tty_fg
  Process.setpgid(0, 0)
  old_handler = Signal.trap(:SIGTTOU, :IGNORE)
  
  tty_fd = File.open('/dev/tty', 'r+').fileno
  Process.tcsetpgrp(tty_fd, Process.getpgid(0))
  
  Signal.trap(:SIGTTOU, old_handler)
end

# Spawn child process with foreground control
spawn('sleep 60', pgroup: false, preexec: method(:become_tty_fg))
Process.wait

Key Notes:

  • mkmf automatically handles linking to libc, so the compiled .so will have all necessary dependencies.
  • The extension adds Process.tcsetpgrp and Process.tcgetpgrp directly to Ruby's built-in Process module for a native feel.

Critical Reminders

  • Always run these operations in a child process's preexec callback (or immediately after fork) — setpgid and tcsetpgrp can only modify the current process or unexecuted child processes.
  • Opening /dev/tty ensures you're interacting with the controlling terminal, not a redirected stdin/stdout.

内容的提问来源于stack exchange,提问作者user3286792

火山引擎 最新活动