- alarm함수
- #include <unistd.h>
- unsigned int alarm(unsigned int seconds);
- -> 0 또는 SIGALRM 시그널이 발생하기까지 남아있는 시간을 초 단위로 반환
- 위 함수를 호출하면서 양의 정수를 인자로 전달하면 전달된 수에 해당하는 시간(초단위)이 지나고 SIGALRM시그널이 발생한다 그리고 0을 인자로 전달하면 이전에 설정된 SIGALRM시그널 발생의 예약이 취소된다.
- 그런데 위의 함수호출을 통해서 시그널의 발생을 예약만 해놓고, 이 시그널이 발생했을 때 호출되어야 할 함수를 지정하지 않으면(signal함수호출을 통해서) 프로세스가 그냥 종료되어 버리니 이를 주의해야 한다.
- signal.c 예제
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
// 시그널이 발생했을 때 호출되어야 할 함수가 각각 정의되어 있다.
// 이러한 유형의 함수를 가리켜 시그널 핸들러(handler)라고 한다.
void timeout(int sig)
{
if(sig == SIGALRM)
puts("Time out!");
// 2초 간격으로 SIGALRM 시그널을 반복 발생시키기 위해 시그널 핸들러 내에서
// alarm함수를 호출하고 있다.
alarm(2);
}
// 시그널이 발생했을 때 호출되어야 할 함수가 각각 정의되어 있다.
// 이러한 유형의 함수를 가리켜 시그널 핸들러(handler)라고 한다.
void keycontrol(int sig)
{
if(sig == SIGINT)
puts("CTRL+C pressed");
}
int main(int argc, char *argv[])
{
int i;
// 시그널 SIGALRM, SIGINT에 대한 시그널 핸들러를 등록하고 있다.
signal(SIGALRM, timeout);
signal(SIGINT, keycontrol);
// 시그널 SIGALRM의 발생을 2초뒤로 예약하였다.
alarm(2);
for(i = 0; i<3; i++)
{
puts("wait ...");
// 시그널의 발생과 시그널 핸들러의 실행을 확인하기 위해서 100초간
// 총 3회의 대기시간을 갖도록 반복문 내에서 sleep함수를 호출하고있다.
// 그렇다면 총 300초, 대략 5분정도가 지나야 프로그램이 종료된다는 계산이 나오는데
// 이는 상당히 긴 시간이다. 하지만 실제 실행시간을 보면 채 10초가 걸리지 않는다.
// 이유는 잠시후에 설명한다.
sleep(100);
}
return 0;
}
- 위 프로그램을 실행하면 예상과 달리 프로그램종료가 채 10초도 걸리지 않는다. 그 이유는
- 시그널이 발생하면 sleep함수의 호출로 블로킹 상태에 있던 프로세스가 깨어나기 때문이다.
- 함수의 호출을 유도하는 것은 운영체제이지만 그래도 프로세스가 잠들어 있는 상태에서 함수가 호출될 수는 없다. 따라서 시그널이 발생하면 시그널에 해당하는 시그널 핸들러의 호출을 위해서 sleep함수의 호출로 블로킹 상태에 있던 프로세스는 깨어나게 된다. 그리고 한번 깨어나면 다시 잠들지 않는다 비록 sleep함수의 호출문에서 요구하는 시간이 채 지나지 않아도 말이다. 그래서 위 예제의 실행에 걸리는 시간은 채 10초가 되지 않는다. 만약에 CTRL+C를 연속해서 입력한다면 1초도 채 걸리지 않을 수 있다.
- sigaction 함수를 이용한 시그널 핸들링
- 지금까지 설명한 내용으로도 좀비프로세스의 생성을 막는 코드는 충분히 만들수있지만 sigaction함수를 통해서 signal함수와 유사하지만 대체가능하고 훨씬 안정적으로 동작하도록 만들수 있다.
- 그이유는 아래와 같다.
- signal 함수는 유닉스 계열의 운영체제 별로 동작방식에 있어서 약간의 차이를 보일 수 있지만 sigaction함수는 차이를 보이지 않는다.
- 실제로 요즘은 signal함수를 사용해서 프로그램을작성하지 않는다. 이 함수는 과거 프로그램과의 호환성을 위해서 유지만 되고 있을 뿐이다. 그래서 sigaction함수에 대해 소개하고자 하는데 앞서 설명한 signal함수의 기능을 대신할 수 있는 수준으로만 설명하고자 한다. 그이상을 설명하는 것은 네트워크프로그래밍을 공부하는여러분에게 불필요하게 부담될수 있기 때문이다.
- #include<signal.h>
- int sigaction(int signo, const struct sigaction * act, struct sigaction* oldact);
- -> 성공시 0, 실패시 -1 반환
- signo: signal함수와 마찬가지로 시그널의 정보를 인자로 전달.
- act : 첫 번째 인자로 전달된 상수에 해당하는 시그널 발생시 호출될 함수(시그널 핸들러)의 정보전달.
- oldact : 이전에 등록되었던 시그널 핸들러의 함수 포인터를 얻는데 사용되는 인자. 필요없다면 0 전달
- 위 함수의 ᅟ호출을 위해서는 sigaction이라는 이름의 구조체 변수를 선언 및 초기화 해야하는데 이구조체는 다음과 같이 정의되어 있다.
- struct sigaction
- {
- void (*sa_handler)(int);
- sigset_t sa_mask;
- int sa_flags;
- }
- 위의 구조체 멤버 중에서 sa_handler에 시그널 핸들러의 함수 포인터값(주소값)을 저장하면된다. 그리고 sa_mask는 모든 비트를 0으로 , sa_flags는 0으로 초기화한다. 이 두 멤버는 시그널 관련 옵션 및 특성의 지정에 사용되는데 우리의 목적은 좀비 프로세스의 생성을 막는데 있으므로 이 두 멤버에 대한 설명은 생략한다.
- sigaction.c 예제
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void timeout(int sig)
{
if(sig == SIGALRM)
puts("Time out!");
alarm(2);
}
int main(int argc, char *argv[])
{
int i;
// 시그널 발생시 호출될 함수의 등록을 위해서는 이렇듯
// sigaction구조체변수를 선언해서 멤버sa_handler에 함수 포인터 값을 저장해야한다.
struct sigaction act;
act.sa_handler = timeout;
// 앞서 sigaction구조체를 설명하면서 멤버 sa_mask의 모든 비트를 0으로 초기화해야
//한다고 하였다. sigemptyset함수는 바로 이러한 목적으로 호출되는 함수이다.
sigemptyset(&act.sa_mask);
// sa_flags역시 signal함수를 대신하기 위해서 필요한 멤버가 아니므로 0으로 초기화한다.
act.sa_flags = 0;
// 시그널 SIGALRM에 대한 핸들러를 지정하고 있다. 그리고 이어서 alarm함수호출을
// 통해서 2초뒤에 시그널 SIGALRM의 발생을 예약해 놓았다.
sigaction(SIGALRM, &act, 0);
alarm(2);
for(i = 0 ;i<3; i++)
{
puts("wait . . .");
sleep(100);
}
return 0;
}
'책 > 윤성우 TCPIP' 카테고리의 다른 글
10-04 멀티태스킹 기반의 다중접속 서버(2) (0) | 2020.12.21 |
---|---|
10-03 : 시그널 핸들링 (3) ~ 10-04 멀티태스킹 기반의 다중접속 서버(1) (0) | 2020.12.20 |
10-01 프로세스의 이해와 활용 (4) ~ 10-03 : 시그널 핸들링 (1) (0) | 2020.12.18 |
10-1 프로세스의 이해와 활용 (0) | 2020.12.15 |
09-2 : SO_REUSEADDR (3) (0) | 2020.12.14 |