시스템 프로그래밍 시험 공부하면서 정리한 내용이다. 내용 갱신은 앞으로 없다.
Signals
Signals
Signal
- 프로세스에 이벤트를 알릴때 보내는 것
- 프로세스나 프로세스 그룹에 간단한 메세지를 보낼때 사용하는 IPC
- number : 시그널 번호
- Remember, No payload. 인자는 지원하지 않는다.
- 간단하고 효율적이라서 널리 쓰임
- 일반적으로 프로세서는 시그널에 반응할때 user-space 함수(signal handler)를 호출
- 인터럽트 <=> 커널 … 시스널 <=> 프로세스
목적
- 특정한 이벤트가 발생했다는 것을 프로세스에 알려준다.
- 프로세스가 시그널 핸들러를 실행하도록 강요할때 사용.
시그널의 종류
- signal number -> SIGxxx macro
- “real-time” signal은 POSIX 표준에 정의
프로레스가 시그널을 처리하는 방법
- 커널 기본을 사용. (시그널별로 기본 행동이 정의되어 있다. Terminate, Dump, …)
- 시그널 무시
- 시그널 Block (나중에 unblock할때까지 미룬다)
- 별도의 시그널 핸들러
시그널의 특징
- 시그널은 프로세스에 언제든지 보낼 수 있다
- non-running 상태의 프로세스에도 보낼 수 있다.
- blocked도 시그널을 받을 수 있다
- 프로세스가 실행 상태로 돌아올때까지 커널이 저장해둔다.
- Running 상태로 돌아오면 받은 시그널 처리
- 현재 실행중인 프로세스(current process)에서 시그널 생성 가능
- 타입별 시그널은 1개씩만 저장 가능
- bitmap을 이용해서 시그널 저장하기때문
- 시그널은 프로세스에 의해서 선택적으로 blocked 가능
- 블럭한거 처리하기전까지 프로세스는 해당 시그널을 받지 않는다
- 시그널 핸들러 함수가 실행되면 함수가 끝날때까지 시그널 블럭
- 시그널 핸들러는 다른 시그널에 의해 interrupt 되지 않는다.
Signal Handling
- 시그널 핸들링에 필요한 자료구조
- 시그널 핸들러 배열
- 대기중인(pending) 시그널 목록
- 블럭된(blocked) 시그널 마스크 (bitmask)
- 대기중인 시그널 (pending signal)
- 받았지만 아직 처리할수 없는 시그널은 pending 된다
- 시그널이 언제 처리되는가?
- 인터럽트나 예외에서 돌아올때 (시스템콜 포함)
- 시그널은 타입별로 1개만 대기 목록에 들어갈 수 있다.
- 시그널이 언제 처리될지는 보장 못한다
- 커널의 시그널 구현
- 각각의 프로세스에서 어떤 시그널을 블럭했는지 기억
- 커널모드에서 유저모드로 넘어갈때 도착한 시그널이 있는지 확인하기
- timer interrupt를 이용해서 대략 1ms마다 계속 확인
- 시그널을 무시해도 되는지 결정
- 시그널 처리
- 프로세스 실행 도중에 시그널 핸들러로 전환
- 이전 실행 상태는 저장해놨다가 시그널 핸들러 처리 후 복구
Date Structures for Signal Handling
task_struct에는 lightweight process용 시그널 자료구조와 thread group용 시그널 자료구조가 같이 들어있다. 내부 구조는 서로 비슷하다. thread-group용 시그널은 shared signal이라고도 부른다
- struct task_struct
- struct sigpending pending : 프로세스용 pending 시그널 목록
- struct signal_struct *signal : thread-group용 시그널 디스크립터
- sturct sighand_struct *sighand : 시그널 핸들러
- sigset_t blocked : blocked signal bitmassk
- struct sighand_struct
- struct k_sigaction action[64]
- signal handler : task_struct
task->sighand->action[x].sa_handler
- struct sigpending:
- struct list_head list : linked list. sigqueue로 연결
- sigset_t signal : bitmask
- struct sigqueue
- struct sigqueue *next : linked list
- siginfo_t info : pending 시그널의 정보
- struct siginfo_t
- si_signo : signal number
- si_code : 누가 시그널을 발생시켰는가 (유저? 커널? timer?)
Signal Transmission
- 시그널 생성 (Signal Generation)
- 커널은 목적 프로세스의 디스크립터를 갱신해서 새로운 시그널이 왔다는 것을 저장한다
- 시그널 보내기 (Signal Action)
- 커널은 목적 프로세스가 시그널에 반응하도록 강요한다. 2가지 작업 중 하나, 또는 둘다 수행.
- 목적 프로세스의 실행 상태를 바꾼다. (예를 들면 프로세스 죽이기)
- 시그널 핸들러를 실행한다.
Signal Generation
- 시그널 보내는 프로세스 디스크립터 갱신
- 시그널의 종류와 프로세스의 상태에 따라서 시그널 함수는 프로세스를 깨우고 시그널 처리를 강요한다.
- 대상에 따라 2종류로 분류
- 프로세스로 시스널 보내는 함수
- 쓰레드 그룹으로 시그널 보내는 함수
specific_send_sig_info()
- 시그널 액션을 무시할수 있는지 확인
- 시그널을 프로세스에 보내기.
send_signal()
호출 - 시그널이 블럭되지 않았으면
signal_wake_up()
호출 - SIGKILL, SIGSTOP, 등등은 특별한 처리
send_signal()
- 시그널을 목적 프로세스의 pending list에 추가하는 함수
- 새로운 list 요소용 메모리를 할당
- 시그널 정보를 채우고 목록에 추가
- pending 시그널 목록 갱신
signal_wake_up()
- 프로세스에 새로운 pending singal이 등록되었다는 것을 알려주는 함수
TIF_SIGPENDING
플래그 설정 (Signal Action에서 이용)
Signal Action
- 프로세스가 interrupt, exception에서 돌아올때
ret_from_intr()
,syscall_exit
는 유저모드로 돌아오기 전에 블럭되지 않은 pending 시그널이 있는지 확인 (TIF_SIGPENDING
의 존재를 확인)- 플래그가 있으면
do_signal()
do_signal()
- pending list에서 시그널 빼내기 (blocked 제외)
- 시그널을 적절히 처리 (3가지중 한가지 행동 선택)
- nonblocked pending signal이 없을때까지 do_signal()을 반복
- 시그널 처리하는 행동
- 시그널 무시
- 시그널의 기본 행동 실행
- Terminate : 프로세스 끝내기 (kill)
- Dump : 프로세스 끝내고 core file 생성
- Ignore : 시그널 무시
- Stop : TASK_STOPPED
- Continue : 프로세스가 멈춰있으면 TASK_RUNNING 상태로 변경
- signal handler 호출.
handle_signal()
- SIGKILL, SIGSTOP는 무조건 기본 행동을 수행
- 무시, blocked, signal handler 불가능
handle_signal()
- 시그널 핸들러를 실행하는 것은 유저모드와 커널 모드를 넘나드는 복잡한 작업이다.
- 시그널 처리의 시작인
do_signal()
은 커널모드인데 시그널 핸들러는 유저모드이다. - 커널모드에서 유저모드 함수를 호출해야 한다.
- 리눅스의 방식
- 현재 프로세스의 커널모드 스택의 Hardware context를 유저모드 스택으로 복사
- 시그널 핸들러가 끝날때
sigreturn()
자동으로 호출해서 hardware context를 커널스택으로 복사. 유저모드 스택을 원래도 되돌림
Real-time Signals in Linux
- POSIX 표준에 정의되어 있다.
- 32~63번
- queue되는 시그널
- 리눅스는 편법으로 real-time signal을 지원
- 진정한 real-time signal이 아니다
- real-time signal은 pending signal list에 같은 타입의 시그널을 1개 이상 추가하는 것을 허용