POSIX Times
- 시스템은 Epoch 이후로 경과한 시간을 초 단위로 유지해야
- Epoch은 세계 표준시 (Coordinated Universal Time, UTC, Greenwich Mean Time 또는 GMT로 불리기도 함) 기준으로 1970년 1월 1일 자정 00:00으로 정의됨
- POSIX는 구현이 시스템 시간을 실제 시간과 날짜에 어떻게 정렬해야 하는지 명시 X
#include <time.h>
time_t time(time_t *tloc);
시스템 시간에 접근한다 (Epoch 이후로 경과한 시간을 초 단위로 표현).
- tloc: NULL이 아닌 경우, 함수는 시간을 *tloc에 저장
- time_t: long 형식
- 성공한 경우, Epoch 이후로 경과한 초의 수 반환
- 실패한 경우, (time_t)-1 반환
- → 필수적인 오류는 정의되어 있지 않음
- 32비트 long 형식의 경우, 1970년 1월 1일부터 약 68년 후인 2038년에 시간이 오버플로될 예정
#include <time.h>
double difftime(time_t time1, time_t time0);
두 time_t 형식의 달력 시간 간의 차이를 계산
- 첫 번째 매개변수에서 두 번째 매개변수를 뺀 값을 담은 double 반환
- 정의된 오류 X
#include <time.h>
char* asctime(const struct tm *timeptr);
char* ctime(const time_t *clock);
struct tm *gmtime(const time_t *timer);
struct tm *localtime(const time_t *timer);
localtime()
- Epoch 이후로 경과한 초를 지정하는 매개변수를 사용
- 로컬 요구 사항에 맞게 조정된 시간 구성 요소를 포함하는 구조체를 반환
asctime()
- localtime에 의해 반환된 구조체를 문자열로 변환
ctime()
- asctime(localtime(clock))와 동일
- 개행 문자로 끝나는 26자 영문자열을 가리키는 포인터를 반환
- 시간 문자열을 보관하기 위해 정적 저장소를 사용
gmtime()
- UTC로 표현된 시간 구성 요소를 포함하는 구조체를 반환
asctime, ctime, localtime은 thread-safe X
- POSIX: TSF Thread Safe Extension은 스레드 안전한 대안을 지정하며, 추가 버퍼 매개변수를 가짐.
struct tm 구조체
- gmtime 및 localtime 함수에서 사용
- 구성원 • int tm_sec; /* 초 [0,59] / • int tm_min; / 분 [0,59] / • int tm_hour; / 시 [0,23] / • int tm_mday; / 월의 날짜 [1,31] / • int tm_mon; / 월 [0, 11] / • int tm_year; / 1900년 이후의 연도 / • int tm_wday; / 일요일부터의 일수 [0,6] / • int tm_yday; / 1월 1일부터의 일수 [0, 365] / • int tm_isdst; / 일광 절약 시간(Daylight Saving Time) 플래그 */
struct timeval
#include <sys/time.h>
int gettimeofday(struct timeval *restrict tp, void *restrict tzp);
- struct timeval
- POSIX:XSI 확장은 이 구조체를 사용하여 미세한 단위의 시간을 표현
- 구성원 • time_t tv_sec; /* Epoch 이후의 초 / • time_t tv_usec; / 마이크로초 */
gettimeofday()
- Epoch 이후의 시간을 초와 마이크로초로 검색
- tp: 검색한 시간을 받는다.
- tzp: 역사적인 이유로 반드시 NULL이어야 함
- 성공하면 0 반환
- 정의된 오류 없음 (일부 시스템은 고유의 오류를 구현함)
- long이 32비트인 경우, 최대 지속 시간은 2^31 - 1 마이크로초 또는 약 35분
Using real-time clocks
#include <time.h>
int clock_getres(clockid_t clock_id, struct timespec *res);
int clock_gettime(clockid_t clock_id, struct timespec *tp);
int clock_settime(clockid_t clock_id, const struct timespec *tp);
- Clock
- 일정 간격으로 증가하는 카운터인 클럭 해상도를 갖고 있는 것
- POSIX:TMR 타이머 확장에는 clockid_t 유형의 변수로 나타낼 수 있는 클럭이 포함되어 있음
- 모든 구현은 CLOCK_REALTIME 값에 해당하는 시스템 실시간 클럭을 갖도록 해야 함
- struct timespec 구조체 – time_t tv_sec; /* 초 / – long tv_nsec; / 나노초 */
- Clock 함수들 – 성공하면 0을 반환하고, 실패하면 -1을 반환하고 errno를 설정함
Sleep functions
#include <unistd.h>
unsigned sleep(unsigned seconds);
#include <time.h>
int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);
sleep()
- 호출된 스레드를 지정된 시간(초)이 경과하거나 호출된 스레드가 신호를 받을 때까지 중단시킴
- 요청한 시간이 경과하면 0을 반환
- 중단되었을 경우 남은 시간을 반환함
nanosleep()
- 호출된 스레드를 rqtp에 지정된 시간 간격이 경과하거나 스레드가 신호를 받을 때까지 실행을 중단시킴
- rmtp가 NULL이 아닌 경우 남은 시간이 포함됨
- 성공하면 0을 반환하고, 실패하면 -1을 반환하고 errno를 설정함
- SIGALRM을 포함한 어떤 신호의 사용에도 영향을 미치지 않음
POSIX:XSI Interval Timer
타이머
- 컴퓨터 시스템은 일반적으로 소수의 하드웨어 간격 타이머를 가지고 있음
Clock vs. timer ?
- 운영 체제는 이러한 하드웨어 타이머를 사용하여 여러 소프트웨어 타이머를 구현
- 타임 쉐어링 운영 체제는 프로세스 스케줄링을 위해 간격 타이머도 사용할 수 있음
POSIX:XSI 타이머
- struct itimerval 구조체를 사용함
- struct timeval it_value; /* 다음 만료까지의 시간 /
- struct timeval it_interval; / 타이머에 다시 로드할 값 */
#include <sys/time.h>
int getitimer(int which, struct itimerval *value);
int setitimer(int which, const struct itimerval *restrict value, struct itimerval *restrict ovalue);
getitimer()
- 타이머 'which'의 현재 값이 'value'에 저장됨
- ITIMER_REAL
- 실제 시간에서 감소하고 만료되면 SIGALRM 신호를 생성
- ITIMER_VIRTUAL
- 가상 시간(프로세스에서 사용하는 시간)에서 감소하고 만료되면 SIGVTALRM 신호를 생성
- ITIMER_PROF
- 가상 시간 및 프로세스의 시스템 시간에서 감소하고 만료되면 SIGPROF 신호를 생성
setitimer()
- 'which'로 지정된 타이머를 'value'로 설정함
- ovalue가 NULL이 아닌 경우 이전 값이 저장됨
- value->it_interval이 0이 아닌 경우, 타이머는 만료될 때 이 값으로 다시 시작함
- value->it_interval이 0이면 타이머는 만료될 때 다시 시작되지 않음
- value->it_value가 0이면 실행 중인 경우 타이머가 중지됨
POSIX:TMR interval timers
소수의 클럭, 예를 들면 CLOCK_REALTIME과 같은 것들이 있으며, 프로세스는 각 클럭에 대해 많은 독립적인 타이머를 생성 가능
struct itimerspec 구조체를 기반으로 함
- struct timespec it_interval; /* 타이머 주기 /
- struct timespec it_value; / 만료 시간 */
- struct timeval보다 더 높은 해상도를 가짐
#include <signal.h>
#include <time.h>
int timer_create(clockid_t clock_id, struct sigevent *restrict evp, timer_t *restrict timerid);
int timer_delete(timer_t timerid)
timer_create()
- 프로세스별 타이머를 생성
- fork에서 상속되지 않음
- clock_id: 타이머가 기반으로 하는 클럭
- timerid: 생성된 타이머의 ID를 보유
- evp
- 타이머 만료 시 발생할 비동기 알림을 지정함
- NULL이면 타이머는 기본 신호를 생성함 (CLOCK_REALTIME에 대한 경우 SIGALRM)
- evp->sigev_signo: 원하는 신호 번호
- evp->sigev_notify: 타이머 만료 시 취해질 작업을 지정함
- SIGEV_SIGNAL: 타이머 만료가 신호를 생성
- SIGEV_NONE: 타이머가 신호를 생성하지 않도록 함
#include <time.h>
int timer_getoverrun(timer_t timerid);
int timer_gettime(timer_t timerid, struct itimerspec *value);
int timer_settime(timer_t timerid, int flags, const struct itimerspec *value,
struct itimerspec *ovalue);
timer_settime()
- 타이머를 시작하거나 중지
- ‘flags'는 타이머가 상대 시간 또는 절대 시간을 사용하는지 여부를 지정함
- ovalue'가 NULL이 아닌 경우 이전 값을 저장함
timer_overrun()
- 타이머가 만료되는 동안 이전에 동일한 타이머의 만료에서 아직 처리되지 않은 신호가 대기 중인 경우, 생성된 신호 중 하나가 손실될 수 있음
- 오버런의 횟수를 반환
Timer drift
- 이유
- 타이머가 만료되어야 할 때와 타이머가 재설정된 때 사이의 지연 시간
- 2초 간격이 5 마이크로초의 지연으로 인해 사실은 2.000005초가 됨
예시
- 타이머의 resolution이 10 ms인 경우, 주기가 22 ms인 반복 타이머가 있다고 친다면?
- 오차는 각 만료마다 8 ms씩 증가함
절대 시간(absolute time)을 사용한 해결책
- 타이머가 실제로 만료되어야 하는 시간을 추적하고, 매번 타이머를 설정할 때 이 값을 조정함
- T = 현재 시간 + 22 ms 저장
- 타이머를 22 ms 후에 만료되도록 설정
- 신호 핸들러에서 타이머를 (T - 현재 시간 + 22ms) 후에 만료되도록 설정하고, T = T + 22 ms
POSIX:TMR 타이머를 사용한 해결책 (절대 시간)
- timer_settime의 flags 매개변수를 TIMER_ABSOLUTE로 설정할 수 있음
- *value 매개변수의 it_value 멤버에 지정된 시간은 시간 간격이 아닌 실제 시간을 나타냄
- clock_gettime를 사용하여 현재 시간을 결정하고 22 ms를 더해줌 → 이를 T로 저장
- 타이머를 TIMER_ABSOLUTE 플래그를 사용하여 T 시간에 만료되도록 설정
- 타이머 신호 핸들러에서 T에 22 ms를 더하고 타이머를 T 시간에 만료되도록 설정
'Study > 시스템프로그래밍' 카테고리의 다른 글
[시스템프로그래밍] Mutex에 관하여 (4) | 2023.12.05 |
---|---|
[시스템프로그래밍] 스레드 (0) | 2023.12.04 |
[시스템프로그래밍] Signals (1) | 2023.12.03 |
[시스템프로그래밍] pipe에 관하여 (0) | 2023.11.02 |
[시스템프로그래밍] 디렉토리 엔트리, hard/symbolic link, stat에 관하여 (0) | 2023.11.02 |