Signal 모듈

Signal 모듈

프로세스에 시그널(signal)을 보내는 건 많은 운영체제가 지원하는 기능이에요. 어떤 시그널은 프로세스에 정해진 효과를 주고, 어떤 시그널은 코드 수준에서 트랩(trap)해서 원하는 대로 처리할 수도 있어요. Signal 모듈은 바로 그 시그널을 다루는 기능을 제공해요.

출처: Ruby 4.0 API

본문

운영체제는 실행 중인 프로세스에 시그널을 보낼 수 있어요. 가령 여러분의 프로세스가 USR1 시그널을 트랩해서 디버깅 모드를 켜고 끄는 데 쓰고, TERM 시그널로는 제어된 종료(controlled shutdown)를 시작하게 할 수 있죠.

pid = fork do
  Signal.trap("USR1") do
    $debug = !$debug
    puts "Debug now: #$debug"
  end
  Signal.trap("TERM") do
    puts "Terminating..."
    shutdown()
  end
  # . . . do some work . . .
end

Process.detach(pid)

# Controlling program:
Process.kill("USR1", pid)
# ...
Process.kill("USR1", pid)
# ...
Process.kill("TERM", pid)

produces:

Debug now: true
Debug now: false
Terminating...

각 시그널 이름과 그 해석은 시스템마다 달라요. 시그널 전달 방식도 시스템에 따라 조금씩 다르고, 특히 시그널 전달이 항상 신뢰할 수 있는 건 아니라는 점을 기억해 두세요.

Public Class Methods

list → a_hash

시그널 이름을 그에 대응하는 시그널 번호로 매핑한 해시를 반환해요.

Signal.list   #=> {"EXIT"=>0, "HUP"=>1, "INT"=>2, "QUIT"=>3, "ILL"=>4, "TRAP"=>5, "IOT"=>6, "ABRT"=>6, "FPE"=>8, "KILL"=>9, "BUS"=>7, "SEGV"=>11, "SYS"=>31, "PIPE"=>13, "ALRM"=>14, "TERM"=>15, "URG"=>23, "STOP"=>19, "TSTP"=>20, "CONT"=>18, "CHLD"=>17, "CLD"=>17, "TTIN"=>21, "TTOU"=>22, "IO"=>29, "XCPU"=>24, "XFSZ"=>25, "VTALRM"=>26, "PROF"=>27, "WINCH"=>28, "USR1"=>10, "USR2"=>12, "PWR"=>30, "POLL"=>29}

signame(signo) → string or nil

시그널 번호를 시그널 이름으로 변환해요. 잘못된 시그널 번호라면 nil을 반환해요.

Signal.trap("INT") { |signo| puts Signal.signame(signo) }
Process.kill("INT", 0)

produces:

INT

시그널을 처리하는 블록이 시그널 번호를 받는 경우, 그 번호를 다시 사람이 읽을 수 있는 이름으로 바꿀 때 signame을 쓰면 돼요.

trap(signal, command) → obj

시그널의 처리 방식을 지정해요. 전에 해당 시그널에 등록돼 있던 핸들러를 반환해요.

인자 signal은 시그널 이름(문자열이나 SIGALRM, SIGUSR1 같은 심볼) 또는 정수 시그널 번호예요. 문자열이나 심볼일 때는 앞의 SIG 접두어를 생략할 수 있어요.

인자 command 또는 블록은 시그널이 발생했을 때 실행할 코드를 지정해요.

인자 command로는 다음 특수 값들을 담은 문자열이나 심볼을 줄 수도 있어요:

  • IGNORE, SIG_IGN: 시그널을 무시해요.
  • DEFAULT, SIG_DFL: Ruby의 기본 핸들러를 호출해요.
  • EXIT: 시그널로 프로세스를 종료해요.
  • SYSTEM_DEFAULT: 운영체제의 기본 핸들러를 호출해요.

특별한 시그널 이름 EXIT나 시그널 번호 0은 프로그램이 종료되기 직전에 호출돼요:

Signal.trap(0, proc { puts "Terminating: #{$$}" })
Signal.trap("CLD")  { puts "Child died" }
fork && Process.wait

Outputs:

Terminating: 27461
Child died
Terminating: 27460

번호 0에 등록한 블록은 프로세스 종료 시점에, CLD 핸들러는 자식 프로세스가 끝났을 때 각각 불리는 걸 확인할 수 있어요.