관리 메뉴

너와 나의 스토리

[Unix] CH6. Signal and signal processing 본문

Unix/이론

[Unix] CH6. Signal and signal processing

노는게제일좋아! 2019. 11. 10. 14:50
반응형

Signal Concepts

  • kernel에서 발생하는 일:
    • 커널은 키보드 입력으로부터 인터럽트 캐릭터를 확인하고 
    • SIGINT라고 불리는 시그널을 (foreground group인)모든 프로세스들에게 보낸다
    • cc가 시그널을 받으면, sigint와 연관된 디폴트 액션을 수행하고 종료한다.
  • 시그널은 소프트웨어 인터럽트이다
  • 시그널은 비동기식 이벤트를 다루는 방법을 제공한다.
  • 시그널은 에러가 아니므로 에러처리와 혼동하지 말아야 한다.
  • 프로세스는 시그널을 받으면 시그널을 처리하기 위한 액션을 취해야 한다.
  • 모든 시그널은 이름을 가지며, 이 이름은 SIG-로 시작한다
    • 이 이름은 <signal.h> 헤더에 양수인 상수로 정의된다 

 

 

signal

  • SIGABRT: abort 함수(비정상 종료)를 호출함으로써 생성됨. 호출 프로세스 자기 자신에게 SIGABRT를 날린다.
    • 디폴트 액션: 프로세스 종료
  • SIGALRM: alarm 함수에 의해 발생. alarm(t)에 의해 지정된 시간이 만료되면 호출 프로세스에게 SIGALRM을 날린다.
    • 디폴트 액션: 프로세스 종료
  • SIGCHLD: 자식프로세스가 죽었을 때, 부모프로세스에게 이를 알리는 시그널
    • 디폴트 액션: 무시
  • SIGCONT: 정지했던 프로세스가 재개되었을 때, 정지했던 프로세스에게 보내는 시그널.
    • 디폴트 액션: 프로세스 재개나 무시
  • SIGFPE: 0으로 나누었을 때같은 산술적 예외로 발생
    • 디폴트 액션: 종료 및 코어덤프 생성
  • SIGINT: 인터럽트 키(Ctrl+C)가 눌러졌을 때, foreground 프로세스 그룹에 있는 모든 프로세스들에게 시그널을 날린다.
    • 디폴트 액션: 종료
  • SIGKILL: 프로세스를 죽임. ->can't be caught or ignored
    • 디폴트 액션: 종료 (디폴트 액션 변경 불가)
  • SIGPIPE: 파이프 I/O시 발생하는 시그널
    • 디폴트 액션: 종료
  • SIGSEGV: 불법적인 메모리 접근 시 발생.
    • 디폴트 액션: 종료 및 코어 덤프
  • SIGTERM: 프로세스 종료
    • 디폴트 액션: 종료
  • SIGTSTP: (Ctrl+z)시 발생하는 시그널. 프로세스를 멈첨
  • SIGUSR1: 사용자가 정의한 시그널1. 
    • 디폴트 액션: 종료
  • SIGSTOP: job 컨트롤하는 시그널. can't be caught or ignored.

 

SIGNAL 처리

  • SIG_DFL: 신호에 대한 디폴트 액션 발생
  • SIG_IGN: 신호 무시
signal(SIGINT, sigint_handler); // sigint_handler라는 사용자 정의 함수로 시그널 처리
signal(SIGQUIT, SIG_IGN); // ctrl + '\' 를 무시
signal(SIGTSTP, SIG_IGN); // ctrl + 'z'를 무시
signal(SIGTTOU, SIG_IGN); // 제어권을 넘겨주기 위해 사용

 

 

Signal Concepts

  • 시그널은 프로세스의 이벤트에 대해 소프트웨어가 발생시키는 것
  • 시그널 발생: 시그널을 유발하는 이벤트 발생시
  • 시그널 전달: 시그널을 받은 프로세스가 액션을 취할 때
  • 시그널 lifetime: 시그널 발생에서 전달까지의 기간
  • 시그널 pending: 시그널은 발생되었지만 아직 액션을 취하지 않은 상태(즉, 전달이 안된 상태)
  • 시그널 catch: 시그널 받은 프로세스가 사용자 정의함수(시그널 핸들러)를 이용하여 액션을 치ㅜ할 경우
  • 시그널 핸들러(sigaction) install: 시그널 핸들러 등록

 

 

 

Signal handling

  • 프로세스가 시그널을 받았을 때, 반드시 다음 3가지 액션 중 하나를 취해야 한다.
    • Ignore action
      • SIGKILL과 SIGSTOP은 절대 무시될 수 없다.
    • User-defined action
      • 커널에게 시그널 발생시 호출할 함수를 말해줘야 함 (signal handler)
      • 시그널 잡아서 내가 정한대로 처리
      • SIGKILL과 SIGSTOP은 catch되지 않음
    • Default action
      • 모든 시그널은 디폴트 액션을 가진다.
      • 디폴트 액션은 보통 프로세스를 종료하는 것이다

 

 

Signal handling: User-defined action

  • 캐치되고 있는 신호가 프로세스에 의해 처리될 때, 프로세스에 의해 실행되는 정상적인 명령 시퀀스는 시그널 핸들러에 의해 일시적으로 중단된다.
  • 그런 다음, 프로세스는 계속 실행되지만 이제 시그널 핸들러의 명령어가 실행된다.

  • 시그널 핸들러에 의한 시그널 처리는 인터럽트 처리 루틴과 유사하다.
  • 위의 처리 루틴은 signal()을 이용할 경우 유닉스 시스템 구현에 따라 다른다.
  • 시그널 핸들러가 (exit나 longjmp를 호출하는 대신) 리턴하면 시그널이 캐치될 때 프로세스가 실행하고 있던 일반적인 명령 시퀀스가 계속 실행된다.
  • 그러나 시그널 핸들러에서는 시그널이 캐치될 때 프로세스가 어디에서 실행되고 있는지 알 수 없다.

 

 

Process signal mask - process attribute

  • 시그널이 생성될 때 취해지는 조치는 해당 시그널의 현재 시그너 핸들러와 프로세스 시그널 마스크에 따라 다르다.
  • 시그널 마스크에는 현재 블락된 시그널 목록이 있다
  • 프로그램은 sigprocmask를 사용하여 프로세스 시그널 마스크를 변경하여 신호를 차단한다.
  • 프로세스는 fork()와 exec() 후에 시그널 마스크를 상속한다.
  • 프로세스 signal mask는 프로세스 attribute로서 signal blocking list이다. 이 목록에 있는 시그널들은 blocking된다.

 

 

Signal block

  • process signal mask == 현재 블락된 시그널의 리스트
  • signal catching 함수가 호출되기 전에 프로세스의 시그널 마스크를 추가

 

 

(Ctrl+c)가 입력되면 SIGINT 시그널이 발생되고 이는 sig_int 함수를 호출한다.

 

 

시그널 처리 중 (^C)를 계속 입력해도 한 개만 입력으로 저장해 뒀다가 sleep 끝나면 처리

 

 

 

 

Signal handling & exec

  • 프로그램이 실행될 때 모든 시그널의 상태는 디폴트이거나 무시된다.
  • exec를 호출하는 프로세스에 의해 캐치되는 새로운 프로그램에서 동일한 함수에 의해 캐치될 수 없다.
    • 호출자에서 시그널 캐치 함수의 주소는 실행되는 새 프로그램 파일에서 의미가 없을 것이기 때문.
    • 코드가 달라지니까 signer handler가 전이랑 다름
  • 프로세스가 fork를 호출하면 자식은 부모의 시그널 처리(signal dispositions)를 상속 받는다. 
  • 여기서, 자식은 부모의 메모리 이미지의 복사본으로 시작하기 때문에 시그널 캐치 함수의 주소는 자식에게 유의미하다. 

 

Signal Sets

  • 우리는 이것을 sigprocmaks와 같은 함수를 함께 사용하여 커널에서 세트 내의 어떤 시그널도 발생하지 않도록 지시한다.

 

 

Sigaction() system call

  • sigaction() 함수는 특정 시그널과 관련된 액션을 조사하고 수정하게 한다.
  • 인자
    • signo: signal number
    • act: 액션 수정
    • oact: 이전 액션

  • sa_handler: signal-catching 함수 주소
  • sa_mask: 시그널 캐시 함수가 호출되기 전에 프로세스의 시그널 마스크를 추가하는 시그널 셋
  • sa_flags: 시그널을 핸들링하기 위한 다양한 옵션
    • sa_flags = SA_INTERRUPT | SA_NOCLDSTOP | SA_NOCLDWAIT 과 같이 표현 가능
    • SA_INTERRUPT: 리눅스에서만 지원, 시그널에 의해 인터럽트된 시스템 콜은 재시작되지 않음
    • SA_NOCLDSTOP: SIGCHLD와 함께 이 옵션을 주면 자식은, SIGSTOP을 받았을 때 부모에게 SIGCHLD을 보내지 않음
    • SA_NOCLDWAIT: 부모가 wait()을 하지 않아도 자식은 좀비가 되지 않음
    • SA_NODERER: 시그널은 시그널 핸들러 수행 시 프로세스 시그널 마스크에 포함됨. 즉, 핸들러 수행 시 해당 시그널은 블락됨. 그러나 이 설정을 하면 해당 시그널을 프로세스 시그널 마스크에 포함되는 것을 방지함 -> unreliable해짐
    • SA_ONSTACK: 시그널 핸들러 호출 시 핸들러는 프로세스의 스택 공간을 사용하지 않고, sigaltstack()에 의해 연결된 스택공간을 사용. 만약 프로세스에 스택공간이 부족하게 되면 시그널 핸들러가 동작하기 위한 스택이 할당 되지 않으므로 핸들러가 동작하지 않고 디폴트로 시그널이 처리된다.

 

Signal and system call

  • 만약 (read, write처럼 중요한)시스템 콜이 수행 중일 때, 프로세스가 시그널을 보낸다면?
    • 시스템 콜이 완료될 때까지 시그널이 영향을 미치지 않고, 다 끝난 후 시그널 수행
  • 만약 pause(), wait() 처럼 별로 안 중요한 "slow" 시스템 콜을 수행 중인데 프로세스가 시그널을 캐치했다면, 시스템 콜은 중단된다.
  • 시스템 콜은 두 가지 카테고리로 나뉜다.
    • "slow" system call (영원히 블락될 수 있는)
      • pipes, terminal devices, and network devices,
      • pause(), wait()
      • 특정 ioctl 연산들
      • 몇몇 interprocess communication functions
      • disk I/O와 관련되지 않는 것
    • All the other
  • sigaction은 시스템 콜이 중단되었을 때 자동적으로 재시작하는 것을 허용한다.
  • sigaction 구조체에서 sa_flags 변수를 SA_RESTART로 설정하면 된다.
  • 만약 이 플래그가 설정된 후 시스템 콜이 다시 시작되고 errno가 설정되지 않는다.

 

 

Additional information about the signal

  • 시그널 캐치 함수가 리턴되면 프로세스의 시그널 마스크는 이전 값으로 리셋된다.
    • 시그널 핸들링 함수에 진입할 때 핸들링 시그널은 프로세스의 시그널 마스크에 등록되고 리턴될 때 이전 값으로 복귀
  • 시그널 핸들러가 유발될때마다 특정 시그널을 블락할 수 있다. (struct sigaction.sa_mask)
  • os는 핸들러가 작동될 때 시그널 마스크로 시그널이 전달되는 것을 포함한다.
    • 시그널 핸들러 동작은 액션이므로 시그널이 전달된 것을 의미한다.
  • 우리가 특정 시그널을 처리할 때마다, 첫 번째 시그널 처리가 완료될 때까지 동일한 신호의 다른 발생이 차단된다.
    • 즉, 시그널 핸들러가 동작 중일 때 핸들링 시그널이 프로세스에게 다시 전달되면 singal mask에 의해 blocking된다. blocking된 이 시그널을 pending 시그널이라고 한다.
  • 아직 핸들러의 동작이 완료되지 않은 시점에서 다시 또 핸들링 시그널이 전달되면 이 시그널은 queue되지 않는다.
  • 왜냐하면 pending bit로 pending 시그널을 표시하기 때문이다.

 

 

sigsetjmp() and siglongjmp()

  • 이 두 함수는 시그널 핸들러에서 분기할 때(branching) 항상 사용해야 한다.
  • 만약 savemask가 0이 아니면, sigsetjmp는 프로세스의 현재 시그널 마스크도 env 안에 저장한다.
  • siglongjmp가 호출될 때, 0이 아닌 savemask로 sigsetjmp를 호출하여 env 인자가 저장된 경우 siglongjmp는 저장된 시그널 마스크를 복원한다.
  • sigsetjmp는 현재 프로세스의 sigsetjmp호출 위치와 이때 signal mask를 env에 저장한다. 이때 리턴값은 직접적인 sigsetjmp 호출에 의한 리턴인지 아니면 siglongjmp에 의한 리턴인지에 따라 달라진다.
    • 1) 직접호출에 의한 리턴: 0을 리턴
    • 2) siglongjmp에 의한 리턴: siglongjmp의 val 값
  • siglongjmp는 sigsetjmp에서 저장했던 env를 이용해 jump한다.

 

 

Signal blocking

 

sigprocmask() system call

  • 프로세스 시그널 마스크를 변경하는 시스템 콜
  • set이 널 포인터이면 프로세스의 시그널 마시크는 변하지 않고, how는 무시된다.

how

Description

SIG_BLOCK

add a collection of signals to those currently blocked

SIG_UNBLOCK

delete a collection of signals from those currently blocked

SIG_SETMASK

set the collection of signals being blocked to the specified set

  •  예제)

 

 

kill() and raise() system call

  • kill 함수는 프로세스들에게 시그널을 보낸다
  • raise함수는 프로세스가 자기 자신에게 시그널을 보내도록 한다.

pid >  0

The signal is sent to the process whose process ID is pid.

pid == 0

The signal is sent to all processes whose process group ID equals the process group ID of the sender

pid <  0

The signal is sent to all processes whose process group ID equals the absolute value of pid.

pid ==-1

The signal is sent to all processes on the system for which the sender has permission to send the signal.

 

  • Permission to send signals
    • super user는 어떤 프로세스에도 시그널을 보낼 수 있다.
    • 송신자의 real or effective user ID는 수신자의 real or effective user ID와 같아야한다.

 

  • 예제) kill에 의해 부모와 자식이 signal을 주고 받음 -> 종료되지 않음

 

 

pause() system call

  • pause() 함수는 시그널이 캐치될 때까지 호출된 함수는 일시 중단한다.
  • 만약 액션이 프로세스를 종료하는 경우 pause()는 리턴되지 않는다
  • 만약 액션이 시그널 캐치 함수를 실행시키는 거라면, pause()는 시그널 캐치 함수가 리턴된 후 리턴된다.

 

 

 

 

 

반응형
Comments