Signals
Small message that notifies a process that an event of some type has occurred in the system
시스템 안에서 일어났던 어떤 종류의 event를 알려주는 작은 메시지를 signal이라고 합니다.
- Sent from the kernel (sometimes at the request of another process) to a process
kermel에서 signal을 process로 보냅니다. 가끔은 (* 주로 kill() system call) 프로세스에 의해 signal이 보내지기도 합니다. 이는 kernel에게 signal을 보내고싶은 process가 요청을 하고 실질적인 signal은 kernel에서 signal을 받는 process으로 보내집니다; process가 kernel한테 보내는 것은 signal이 아닙니다.
- Different signals are identified by small integer ID's
서로 다른 signal은 작은 숫자 ID로 구분됩니다.
- The only information in a signal is its ID and the fact that it arrived.
signal이 보내는 정보는 오직 그 signal의 ID와 그 signal이 도착했다는 사실입니다. 즉, signal은 많은 정보를 갖고있지 않습니다; 구조체로 구성되어있거나 .. 이런식이 아닙니다.
대표적인 signal들은 다음과 같습니다:
ID | Name | Default Action | Corresponding Event |
2 | SIGINT | Terminate | Interrupt from keyboard(ctrl-c) |
9 | SIGKILL | Terminate | Kill program(cannot override or ignore) |
11 | SIGSEGV | Terminate & Dump | Segmentation violation |
14 | SIGALRM | Terminate | Timer signal |
17 | SIGCHLD | Ignore | Child stopped or terminated |
Default Action은 어떤 signal을 받았을 때, 자동적으로 수행하게 되는 행동을 말합니다.
(* 이때 Dump는 오류에 대한 정보를 stack에 쌓아 debugging에 도움을 주는 행동입니다.)
Signal Concepts
Sending a signal
Kernel sends (delivers) a signal to a destination process by updating some state in the context of the desination process.
kernel은 목적지 프로세스의 kernel에 있는 PCB안에 vector값을 조정하여 signal을 보냅니다.
kernel은 다음의 이유들 중 하나로 인해 signal을 보냅니다:
- Kernel has detected a system event such as divided-by-zero(* SIGFPE) or the termination of a child process(* SIGCHLD)
kernel은 0으로 나누는 사건이나 자식 프로세스가 종료되는 사건을 감지하면 signal을 보냅니다.
- kill command sends the specified signal(* SIGKILL) to the specified process or process group.
kill command는 특정한 프로세스나 특정한 프로세스 그룹에 SIGKILL을 보냅니다.
- Another process has invoked the kill() system call to explicitly request the kernel to send a signal to the destination process
또 다른 프로세스가 kill() system call을 통해 kernel에게 목적지 프로세스에게 signal을 보내달라고 요청하는 경우 kernel은 목적지 프로세스에게 signal을 보냅니다.
(* 참고로 kill command와 kill() system call은 다릅니다. kill() system call은 다른 프로세스에게 signal을 보내는 명령어일 뿐, 프로세스를 정지시키거나 종료시키지 않습니다.)
Receiving a signal
A destination process receives a signal when it is forced by the kernel to react in some way to the delivery of the signal.
목적지 프로세스는 kernel에게 특정한 방식으로 행동하도록 합니다.
다음은 목적지 프로세스가 할 수 있는 3가지의 행동입니다:
- Ignore the signal (do nothing)
- Terminate the process
- Catch the signal by executing a user-level function called a signal handler.
signal을 무시하거나, 그 프로세스를 종료하거나, signal을 받아 signal handler에 의해 유저 단에서의 함수를 실행합니다. 이때 앞서 봤었던 atexit()을 통해 프로세스가 종료될 때, 실행시킬 함수로 지정해놓은 함수가 user-level function의 예시입니다. default action또한 signal handler가 호출하는 함수들 입니다.
A signal is pending if it has been sent but not yet received. There can be at most one pending signal of any particular type.
signal이 보내졌지만 아직 목적지 프로세스가 받지 않았다면 signal은 보류/저장될 수 있습니다; 이때 signal을 받았다는 것은 action을 아직 취하지 않았음을 의미한다. 하지만 한 종류의 signal에 대해 최대 하나까지만 보류/저장할 수 있습니다.
이때 중요한 점은 signal은 queued되지 않는다는 것입니다.
만약 프로세스가 k 타입의 signal을 가지고 pending중이라면, 다음 k 타입의 signal이 오면 그 프로세스는 해당 signal을 버립니다.
이는 각 프로세스가 가지고 있는 PCB의 pending vector를 생각하면 편합니다.
pending vector는 bit단위로 각종 타입의 signal을 저장합니다.
만약 2번 종류의 signal이 저장/보류되어야 한다면 2번째 bit의 값을 1로 바꿔줍니다. 그 후 2번 signal이 받아들여질 수 있다면 2번째 bit의 값을 0으로 바꾸고 그에 맞는 행동을 취합니다.
만약 2번 종류의 signal이 이미 저장/보류되어있어서 2번째 값이 이미 1일 때, 다시 2번 signal이 들어온다면, 2번째 bit의 값은 다시 1로 바뀔것입니다. 하지만 이미 값이 1이었기 때문에, 이는 나중에 들어온 signal이 버려진것으로 볼 수 있습니다.
initial pending vector:
Signal Type(bit) | 0 | 1 | 2 | 3 | 4... |
value | 0 | 0 | 0 | 0 | 0 |
type 2 signal을 받은 후 pending vector:
Signal Type(bit) | 0 | 1 | 2 | 3 | 4... |
value | 0 | 0 | 1 | 0 | 0 |
또 다시 type 2 signal을 받은 후 pending vector:
Signal Type(bit) | 0 | 1 | 2 | 3 | 4... |
value | 0 | 0 | 1 | 0 | 0 |
똑같습니다. 다시 말해, pending vector로는 어떤 type의 signal이 몇 번 들어왔는지 같은 것들은 알 수 없습니다.
A process can block the receipt of certain signals. Blocked signals can be delivered, but will not be received until the signal is unblocked
프로세스는 특정 signal을 막을 수 있습니다. 하지만 막힌 signal은 목적지 프로세스에 아에 도달하지 못하는 것이 아니고, 목적지 프로세스까지 도착하지만, 그 후의 행동으로까지는 그 signal의 block이 풀릴 때까지 이어지지 않습니다.
이때 blocked된 signal은 block vector에 저장/보류됩니다.
Kernel maintains pending and blocked bit vectors in the context of each process.
process의 kernel은 2개의 bit vectors를 관리하고 있습니다.
pending vector는 현재 저장/보류중인 signal들을 나타냅니다. kernel은 type k의 signal이 도착하면 k번째 비트를 1로 수정하고, type k의 signal이 받아지면 0으로 수정합니다.
blocked vector는 막혀있는 signal들을 나타냅니다. 이는 특정 application에 의해 지워지고 수정됩니다.
그 후 kernel은 다음의 연산을 통해 pnb를 계산합니다:
pnb = pending & ~blocked
(* 이때, &, ~은 논리연산자가 아닌 bitwise연산자입니다.)
blocked가 1로 켜져있으면 ~연산을 통해 0이 되고,
0과 어떤 값이 &연산 되어도 0이기 때문에,
pnb에는 결과적으로 blocked가 1로 켜져있는 bit칸은 0이 됩니다.
만약 pnb == 0 이면:
처리할 signal이 없다는 의미로, 프로그램 흐름상의 다음 명령의 실행을 위해 프로세스 p에게 통제권을 넘깁니다.
만약 pnb != 0 이면:
처리할 signal이 있다는 의미로, 가장 작은 0이 아닌 비트 k에 대해서 프로세스 p에게 행동을 취하도록 합니다. 이를 모든 0이 아닌 비트에 대해서 반복하고, 모두 끝나면 프로그램 흐름상의 다음 명령의 실행을 위해 프로세스 p에게 통제권을 넘깁니다.
각각의 signal들은 그에 맞는 default action을 사전에 정해두었습니다.
default action은 아래의 예시들 중 하나입니다:
- The process terminates
- The process terminates and dumps core
- The process stops until restarted by a SIGCONT signal(* suspended signal을 받았을 때, 다시 시작하라는 signal인 SIGCONT를 받을 때까지는 stoped된 상태로 계속된다.)
- The process ignores the signal
Process Groups
Every Process belongs to exactly one process group.
모든 프로세스들은 하나의 프로세스 그룹에 속합니다.
(* getpgrp()을 통해 process group id를 얻을 수 있고, setpgrp()을 통해 process group id를 변경할 수 있습니다.)
따라서 다음과 같은 명령어를 수행할 수 있습니다:
$ kill -9 24818
-> Send SIGKILL to process 24828
$ kill -9 -24817
-> Send SIGKILL to every process in process group 24817
(* kill() 은 system call로, signal을 보내달라는 요청입니다. -9는 9번째 signal으로, 9번째 signal은 SIGKILL을 의미합니다. 그냥 24818은 process id를 의미하고, 앞에 -가 붙으면 process group id를 의미합니다.)
'[학교 수업] > [학교 수업] 시스템 프로그래밍' 카테고리의 다른 글
[시스템 프로그래밍] Race Condition (0) | 2024.09.28 |
---|---|
[시스템 프로그래밍] Signal Programming (0) | 2024.09.24 |
[시스템 프로그래밍] Linux Commands (0) | 2024.09.20 |
[시스템 프로그래밍] Linux Installation (0) | 2024.09.13 |
[시스템 프로그래밍] Multi-process Programming (0) | 2024.09.11 |