본문 바로가기
CS 지식/시스템 프로그래밍

[C언어] 프로세스 timer와 alarm개념과 관련 시스템 콜

by 코딩하는 동현 2025. 3. 2.

프로세스 타이머와 알람 시스템

Sleeping과 Timer 개념

  • Sleeping: 프로세스나 스레드가 실행을 대기하는 상태
  • Timer: 프로세스가 자기 자신에게 알림을 스케줄하는 메커니즘

Alarms

unsigned int alarm(unsigned int seconds)

호출하는 프로세스가 지정된 seconds 이후에 SIGALRM 시그널을 받도록 설정한다.

동작 방식

  • 기존 알람이 설정되어 있다면 새로운 알람으로 대체된다.
  • 기존 알람의 남은 시간이 반환된다.
  • seconds가 0이면 기존 알람을 취소한다.

시한 폭탄 예제

int left;
char *msg1 = "Beep\n";
char *msg2 = "Boom!\n";

void bomb(int sig) {
    left--;
    if(left) {
        write(1, msg1, strlen(msg1));
        alarm(1);
    } else {
        write(1, msg2, strlen(msg2));
        _exit(0);
    }
}

int main(int argc, char *argv[]) {
    left = atoi(argv[1]);
    signal(SIGALRM, bomb);
    write(1, msg1, strlen(msg1));
    alarm(1);
    
    while (1) {
        pause();
    }
    return 0;
}

Interval Timers (재귀적 알람)

alarm()의 일회성 문제를 해결하기 위한 기능이다.

Interval Timer 종류

  • ITIMER_REAL: 실시간 기반으로 SIGALRM 시그널을 보냄
  • ITIMER_VIRTUAL: 유저 코드 실행 시간을 기반으로 SIGVTALRM 시그널을 보냄
  • ITIMER_PROF: 유저 + 커널 코드 실행 시간을 기반으로 SIGPROF 시그널을 보냄

struct itimerval

struct itimerval {
    struct timeval it_interval; // 반복 알람 주기
    struct timeval it_value; // 최초 알람 설정 시간
};

Interval Timer 설정

int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue);

5초 이후 1초마다 실행되는 타이머 예제

void alarm_handler(int sig) {
    write(1, "Timer hit!\n", 12);
}

int main() {
    struct itimerval delay;
    signal(SIGALRM, alarm_handler);
    delay.it_value.tv_sec = 5;
    delay.it_interval.tv_sec = 1;
    setitimer(ITIMER_REAL, &delay, NULL);
    
    while(1) pause();
    return 0;
}

get Interval Timer

int getitimer(int which, struct itimerval *value)

  • 성공 시 0 반환
  • 실패 시 -1 반환
  • which에 지정된 interval 타이머의 남은 시간이랑 interval 값을 가져와서 value에 저장한다.
    • which가 다르면 다른 itimer에 등록된 타이머나 0초를 반환한다.
    • itimer마다 고유한 타이머가 존재한다.

Interactions with alarm()

리눅스에서는 alarm()과 setitimer()는 각 프로세스의 timer를 공유한다. 즉, 서로 대체가 된다. 실시간 타이머(ITIMER_REAL)는 setitimer()와 alarm() 둘 중 하나만 설정하는 것이 권장된다.


POSIX Clocks 기반 Timer

setitimer()의 한계를 극복하는 방법으로 POSIX 타이머를 사용한다.

고전적인 Interval Timer의 한계

  • 각 ITIMER_REAL, ITIMER_VIRTUAL, ITIMER_PROF 타입마다 1개씩만 타이머를 설정할 수 있다.
  • 타이머가 만료됐다는 알림은 오직 타입마다 정해진 종류의 '시그널'을 통해 받을 수 있다.
  • 해당 시그널이 block된 상태로 여러 번 울리면, pending되므로 block이 풀리고 시그널 핸들러가 한 번밖에 실행되지 않는다.

 

POSIX Timer 생성

int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid);
  • clock_id: 사용할 시스템 시간 지정 (CLOCK_REALTIME, CLOCK_MONOTONIC 등)
  • sigevent:
    • SIGEV_NONE: 알림 없음
    • SIGEV_SIGNAL: 특정 시그널을 보냄
    • SIGEV_THREAD: 특정 함수 실행
    • SIGEV_THREAD_ID: 특정 스레드에 시그널 전달

POSIX Timer 설정

int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspec *ovalue);
  • flags: TIMER_ABSTIME 또는 0
  • value: 타이머 설정 값

POSIX Timer 예제

#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>

void alarm_handler(int sig) {
    write(1, "Timer hit!\n", sizeof("Timer hit!\n"));
}

int main() {
    struct sigaction sa;
    struct sigevent ev;
    timer_t timer_id;
    struct itimerspec delay;

    sa.sa_handler = alarm_handler;
    sigaction(SIGUSR1, &sa, NULL);

    ev.sigev_notify = SIGEV_SIGNAL;
    ev.sigev_signo = SIGUSR1;
    timer_create(CLOCK_MONOTONIC, &ev, &timer_id);
    
    delay.it_value.tv_sec = 5;
    delay.it_interval.tv_sec = 1;
    delay.it_value.tv_nsec = 100;
    delay.it_interval.tv_nsec = 100;
    timer_settime(timer_id, 0, &delay, NULL);
    
    while (1) pause();
}

 

반응형

댓글