Signals
What's a signal?
signal은 커널에 의해 제공되는 구조입니다. signal은 하나의 프로세스가 다른 프로세스에게 비동기적으로 신호를 보내는 것을 도와줍니다. 만약 그 프로세스가 signal을 받길 원한다면, 대부분의 경우 그 signal로 무엇을 할지 정할 수 있습니다. 다음은 signal의 리스트입니다.
Name | Default Action | Usual Use Case |
---|---|---|
SIGINT | Terminate Process (Can be caught) | Tell the process to stop nicely |
SIGQUIT | Terminate Process (Can be caught) | Tells the process to stop harshly |
SIGSTOP | Stop Process (Cannot be caught) | Stops the process to be continued |
SIGCONT | Continues a Process | Continues to run the process |
SIGKILL | Terminate Process (Cannot be caught) | You want your process gone |
When are signals generated?
- User가 signal을 보낼 때 발생합니다. 예를 들어, 터미널에서 CRTL-C를 누르는 것과 같은 경우입니다.
- System event가 발생할 때입니다. 예를 들어, forking 후에 child process중 하나가 종료된다면 SIGCHILD를 받을 수 있습니다.
- 다른 프로그램이 보내는 경우입니다. 예를 들어, shell에서 kill -9 PID를 보내면 PID에 SIGKILL이 보내집니다.
- 적절한 Hardware interrupt가 발생했을 때입니다. 예를 들어 의도하지 않은 page에 접근했을 때 하드웨어는 segfault interrupt를 발생시켜 kernel에 의해 인식됩니다. 커널은 이러한 일이 발생한 프로세스를 찾아 software interrypt signal인 SIFSEGV를 보냅니다.
Can I pause my child?
물론 가능합니다. SIGSTOP signal을 보내 동작하는 프로세스를 일시적으로 정지하는게 가능합니다. 만약 그 프로세스를 정지하는데 성공한다면 그 프로세스는 더이상 CPU time을 할당받지 못합니다.
이러한 프로세스를 다시 동작하게 하려면 SIGCONT signal을 보내주어야 합니다.
다음 프로그램은 매 초마다 점을 1개씩 찍어 59개를 찍는 프로그램입니다.
#include <unistd.h> #include <stdio.h> int main() { printf("My pid is %d\n", getpid()); int i = 60; while (--i) { write(1, ".", 1); sleep(1); } write(1, "Done!", 5); return 0; }
위와 같은 프로그램을 background에서 실행하고 (끝에 &사용) kill command를 통해 SIGSTOP이나 SIGKILL을 이용해 정지하거나 종료할 수 있습니다.
How do I kill /stop/suspend my child from C?
C에서 kill POSIX call을 이용해 child에게 signal을 보낼 수 있습니다.
kill(child, SIGUSR1); // Send a user-defined signal kill(child, SIGSTOP); // Stop the child process (the child cannot prevent this) kill(child, SIGTERM); // Terminate the child process (the child can prevent this) kill(child, SIGINT); // Equivalent to CTRL-C (by default closes the process)
위에서 보는 C코드는 아래의 shell에서 사용하는 kill 명령어처럼 동작합니다.
ps
kill -l
kill -9 45
kill -s TERM 46
How can I detect "CTRL-C" and clean up gracefully?
나중에 signal을 좀더 자세히 다루겠습니다. 지금은 단지 signal에 대한 간단한 소개를 했을 뿐입니다. 좀더 알아보고 싶으시다면 리눅스 시스템에서 man -s7 signal을 보십시오.
Signal handler 안에 실행 가능한 코드에는 엄격한 제한이 존재합니다. 대부분의 library call과 system call은 비동기 신호에 안전하지 않습니다. 이들은 re-entrant safe하지 않으므로 signal handler 안에서 사용될 수 없습니다.(?)자세한 설명이 필요합니다(?)
단일 쓰레드 프로그램에서 signal handling은 일시적으로 프로그램 실행을 interrupt시켜 signal handler code를 대신 실행시킵니다. 당신의 원래 프로그램이 library call인 malloc을 실행하는 중에 interrupt가 걸렸다고 가정해봅시다. malloc에 의해 사용되는 메모리 구조체는 일관된 상태가 아니게 될 것입니다.
signal handler에서 (malloc을 사용하는)printf를 호출하는 것도 예상치 못한 행동을 할 것입니다.
사실 프로그램이 signal handler code를 실행하기 위해 interrupt 되었을 때 정확히 어떤 프로그램을 실행하고 있는지에 따라 결과가 달라진다면 프로그램이 충돌하거나 잘못된 값을 만들거나 dead lock에 걸릴 수 있습니다.
Signal handler의 일반적인 사용은 boolean flag를 set하여 일반적인 프로그램이 동작할때 쓸 수 있습니다. (????)다음은 이렇게 쓰이는 예입니다.
int pleaseStop; // See notes on why "volatile sig_atomic_t" is better void handle_sigint(int signal) { pleaseStop = 1; } int main() { signal(SIGINT, handle_sigint); pleaseStop = 0; while (! pleaseStop) { /* application logic here */ } /* cleanup code here */ }
위의 코드는 그냥 봤을 때 정확한 코드처럼 보입니다. 그러나 컴파일러와 CPU core에게 main loop를 실행할 것이라는 힌트를 주어야 합니다.
일단 컴파일러 최적화를 방지해줄 필요가 있습니다. 위의 !pleaseStop은 항상 참이되어 계속 동작하는 것처럼 보여 true로 단순화될 수 있습니다.
둘째로는 pleasedStop의 값이 CPU register가 사용하는 cache가 아니라 항상 main memory에서 읽고 쓰여져야 합니다. sig_atomic_t type은 변수의 모든 비트가 atomic operation 안에서 읽거나 수정되는 것을 의미합니다. atomic operation은 interrupte가 걸리지 않는 하나의 operation을 의미합니다. 이렇게 함으로써 예전의 비트값과 새로운 비트값이 합쳐진 값을 읽는 것이 불가능하게 됩니다.
pleaseStop을 volatile sig_atomic_t로 지정하면 signal handler가 return된 후에 main loop가 종료될 곳에 포터블한 코드를 쓸 수 있습니다. sig_atomic_t는 최근 대부분의 플랫폼에서 int와 크기가 같지만 임베디드 시스템에서는 char와 같은 크기를 가져 -127에서 127 사이의 값만 가질수도 있습니다.
volatile sig_atomic_t pleaseStop;
'Angrave System Programming > Processes' 카테고리의 다른 글
Processes Review Questions (0) | 2019.01.18 |
---|---|
Process Control : Wait macros (0) | 2019.01.11 |
Forking: Fork, Exec, Wait(2) (0) | 2019.01.11 |
Forking: Fork, Exec, Wait(1) (0) | 2019.01.11 |
Forking Introduction (0) | 2019.01.10 |