[회고] 신입 iOS 개발자가 되기까지 feat. 카카오 자세히보기

💻 CS/시스템프로그래밍

[시스템프로그래밍] Times and Timers

inu 2019. 12. 7. 14:52
반응형

이것은 한낱 대학생이 교수의 수업을 듣고 작성한 개인저장용 복습 문서입니다.

 

그렇지만, 물론 지적과 수정은 환영합니다.


POSIX Times

 

- 시스템의 시간은 Epoch(컴퓨터의 시간이 시작되는 시점) 이후로 초 단위로 유지됩니다.

- Epoch는 1970 년 1 월 1 일 00:00 (자정)으로서, UTC 시간을 기준으로 합니다.

- POSIX는 시스템 시간이 실제 시간 및 날짜와 일치되는 방법을 지정하지 않았습니다.


Time in seconds

 

#include <time.h>
time_t time(time_t *tloc);

- epoch time으로부터 얼마나 시간이 지났는지

- tloc : output파라미터, NULL이 아니라면, tloc에 저장 (return값과 동일, long타입의 일종)

- 성공시 epoch로부터 지난 초를 리턴, 실패시 (time_t) -1 리턴

- 32비트 시스템에서는 2038년에 이 값이 overflow된다.

 

#include <time.h>

double difftime(time_t time1, time_t time0);

- time1 - time2 의 시간값 리턴


Displaying date and time

 

#include <time.h>

struct tm *gmtime(const time_t *timer);
struct tm *localtime(const time_t *timer);
char* asctime(const struct tm *timeptr);
char* ctime(const time_t *clock);

- gmtime() : 파라미터를 주면 UTC 시간에 맞추어 정보를 변환하여 tm구조로 리턴

- localtime() : 파라미터를 주면 현재 지역에 맞도록 해당 정보를 변환하여 tm구조로 리턴

- asctime() : tm구조체를 받아 해당 정보를 string으로 변환해준다.

- ctime() : asctime()과 하는 일을 동일하나, clock 파라미터를 받는다.

- 참고로 asctime, ctime, localtime는 thread-safe하지 못한 함수이다.

- struct tm : 시간에 관한 구조체로서 여기서 연도,월, 등등의 정보를 가져올 수 있다.
• int tm_sec; /* seconds [0,59]*/
• int tm_min; /* minutes [0,59] */
• int tm_hour; /* hours [0,23] */
• int tm_mday; /* day of the months [1,31] */
• int tm_mon; /* months [0, 11] */
• int tm_year; /* years since 1900 */
• int tm_wday; /* days since Sunday [0,6] */
• int tm_yday; /* days since January 1 [0, 365] */
• int tm_isdst; /* flag for daylight-saving time */


struct timeval

 

#include <sys/time.h>
int gettimeofday(struct timeval *restrict tp, void *restrict tzp);

- epoch로 부터 지난 second와 microsecond를 얻어오는 함수

- struct timeval :  tv_sec(second from epoch)과 tv_usec(microsecond from epoch)을 가지는 구조체

- tp : 일종의 output 파라미터.

- tzp : 무조건 NULL값을 가져야함 (역사적인 이유로)

- 성공시 0리턴. 

PROGRAM 9.11

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
#include <sys/time.h>
#define MILLION 1000000L
 
void function_to_time(void);
 
int main(void) {
   long timedif;
   struct timeval tpend;
   struct timeval tpstart;
 
   if (gettimeofday(&tpstart, NULL)) {
      fprintf(stderr, "Failed to get start time\n");
      return 1;
   }
   function_to_time();                              /* timed code goes here */
   if (gettimeofday(&tpend, NULL)) {
      fprintf(stderr, "Failed to get end time\n");
      return 1;
   }
   timedif = MILLION*(tpend.tv_sec - tpstart.tv_sec) +
                      tpend.tv_usec - tpstart.tv_usec;
   printf("The function_to_time took %ld microseconds\n", timedif);
   return 0;
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
 

- function_to_time() 라는 함수가 돌아가는 데에 걸리는 시간을 알 수 있다.


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 : 'clock resolution'이라고 불리는 일정간 간적으로 증가하는 카운터.

- clock_id에는 보통 CLOCK_REALTIME을 넣는다. (실제 시간)

- struct timespec : time_t tv_sec(seconds)와 long tv_nsec(nanoseconds)를 가진다. 

- 성공 시 0을 리턴하고, 실패시 errno을 지정하고 -1을 리턴한다.

Example using real-time clocks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <time.h>
#define MILLION 1000000L
 
void function_to_time(void);
 
int main (void) {
    long timedif;
    struct timespec tpend, tpstart;
    if (clock_gettime(CLOCK_REALTIME, &tpstart) == -1) {
        perror("Failed to get starting time");
        return 1;
    }
    function_to_time(); /* timed code goes here */
    if (clock_gettime(CLOCK_REALTIME, &tpend) == -1) {
        perror("Failed to get ending time");
        return 1;
    }
    timedif = MILLION*(tpend.tv_sec - tpstart.tv_sec) + (tpend.tv_nsec - tpstart.tv_nsec)/1000;
    printf("The function_to_time took %ld microseconds\n", timedif);
    return 0;
}
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
 

Sleep functions

 

#include <unistd.h>
unsigned sleep(unsigned seconds);

- 프로세스를 일정 시간동안 잠재우는 함수. 인터럽트되어도 깨질 수 있다.

- 보통 0 리턴. 인터럽트되면 보통 남아있는 시간값 리턴.

- 초 단위 수행


#include <time.h>

int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);

- rmtp : 남아있는 시간을 저장하는 아웃풋 파라미터

- 성공시 0을 리턴하고, 실패하면 errno을 재정한 다음 -1을 리턴한다.

- nanos단위 수행


Interval Timer

 

Timers

- OS에서 타이머를 여러가지 방법으로 제공한다.

- XSI Timer, TMR Timer 등


XSI Interval Timers

- struct itimerval 구조체를 사용.

- timeval it_value; : 한번 돌릴 때 사용할 값

- 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 : timer의 현재 설정값을 value에 넣는다. (value가 일종의 output 파라미터)

- setitimer : 설정된 timer에 value값을 넣는다. 

- cf. timer가 끝나면 알람시그널(SIGALRM) 발생 

 

- value : 설정할 타이머값

- value의 it_interval값이 0이 아니면 해당 타이머 종료 후에도 계속 돈다.

- 0이면 기준값 종료후엔 그냥 종료

- value의 it_value까지 0이면 해당 타이머가 그냥 종료된다. (만약 실행중이었다면)

- which : 어떤 종류의 시간으로 넣을 것이냐?

- realtime으로 흘러가길 원하면 ITIMER_REAL

- 프로세스가 running 중인 경우에만 흘러가길 원하면 ITIMER_VIRTUAL 

- running 혹은 해당 프로세스를 위한 작업 시에 흘러가길 원하면 ITIMER_PROF 

 

Example of timer

 

static int setupitimer(void) {
  struct itimerval value;
  value.it_interval.tv_sec = 2;
  value.it_interval.tv_usec = 0;
  value.it_value = value.it_interval;
  return (setitimer(ITIMER_PROF, &value, NULL));
}

- itimerval 구조체로 구성된 value의 내부 값들을 수정하여 사용한다.

- 그 후 setitimer를 사용하여 timer를 세팅한다.

 

struct itimerval ovalue, value;
ovalue.it_interval.tv_sec = 0;
ovalue.it_interval.tv_usec = 0;
ovalue.it_value.tv_sec = MILLION;
ovalue.it_value.tv_usec = 0;
setitimer(ITIMER_VIRTUAL, &ovalue, NULL);
function_to_time();
getitimer(ITIMER_VIRTUAL, &value);

- itimerval 구조체인 ovalue와 value를 선언한다.

- ovlaue의 값을 각각 지정하고

- ITIMER_VIRTUAL에 해당 정보를 준다.

- 그리고 function_to_time();함수 실행 후

- getitimer로 해당 타이머의 시간이 얼마나 흘렀는지 value로 받아준다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h> 
#include <sys/time.h>
 
/* ARGSUSED */
static void myhandler(int s) {
   char aster = '*';
   int errsave;
   errsave = errno;
   write(STDERR_FILENO, &aster, 1);
   errno = errsave;
}
 
static int setupinterrupt(void) {          /* set up myhandler for  SIGPROF */
   struct sigaction act;
   act.sa_handler = myhandler;
   act.sa_flags = 0;
   return (sigemptyset(&act.sa_mask) || sigaction(SIGPROF, &act, NULL));
}
 
static int setupitimer(void) {    /* set ITIMER_PROF for 2-second intervals */
   struct itimerval value;
   value.it_interval.tv_sec = 2;
   value.it_interval.tv_usec = 0;
   value.it_value = value.it_interval;
   return (setitimer(ITIMER_PROF, &value, NULL));
}
 
int main(void) {
   if (setupinterrupt()) {
      perror("Failed to set up handler for SIGPROF");
      return 1;
   }
   if (setupitimer() == -1) {
      perror("Failed to set up the ITIMER_PROF interval timer"); 
      return 1;
   }
   for ( ; ; );                        /* execute rest of main program here */
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
 

 

- 우선 setupinterrupt로 signal_handler를 조정해준다.

- handler는 화면에 *을 출력시켜준다.

- 그 후 setupitimer로 itimer를 생성하여 2초마다 signal_handler가 *를 출력하도록 한다.


TMR interval timers

 

- CLOCK_REALTIME과 같은 작은 클록 및 프로세스는 각 클록에 대해 많은 독립 타이머를 작성할 수 있다.

- itimerspec를 활용하면 된다.

- struct timespec it_interval; (timer의 interval 시간)
- struct timespec it_value; (초기 시간)

- nsec까지 표현이 가능해서 resolution이 좋다고 할 수 있다.

 

#include <signal.h>
#include <time.h>
int timer_create(clockid_t clock_id, struct sigevent *restrict evp, timer_t *restrict timerid);

- timer 하나를 만든다.

- fork로 상속되지 않는다.

- clock_id : timer가 기반을 둘 clock 선택

- timerid : output 파라미터, timer 생성으로 생성된 timer의 id가 넘어온다.

- evp : 발생시킬 signal을 선택할 수 있도록 하는 구조체. 

- evp->sigev_signo : signal num 선택가능 (SIGALARM이 기본값)

- evp->sigev_notify : SIGEV_SIGNAL로 signal이 생기도록 하거나, SIGEV_NONE으로 signal이 안 생기도록 할 수도있다.

- evp에 null을 줄 경우 기본값으로 셋팅된다.

 

int timer_delete(timer_t timerid)

- timerid를 주어 해당 timer를 삭제한다.

 

#include 
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);

- 이전의 gettime과 settime과 같이 활용할 수 있다.

- 해당 timerid에 해당하는 timer에 value값을 주거나, 얻어올 수 있다.

- getoverrun은 timer에서 몇개나 signal을 overrun했는가를 알 수 있는데, 현재 단계에서는 몰라도 된다.

- flag는 옵션정보. 0은 절대시간, 1은 상대시간. 보통 그냥 0 사용.

 

PROGRAM 9.12

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <stdio.h>
#include <time.h> 
#define MILLION 1000000L
#define THOUSAND 1000
 
void function_to_time(void);
 
int main(void) {
   long diftime;
   struct itimerspec nvalue, ovalue;
   timer_t timeid;
 
   if (timer_create(CLOCK_REALTIME, NULL&timeid) == -1) {
      perror("Failed to create a timer based on CLOCK_REALTIME");
      return 1;
   }
   ovalue.it_interval.tv_sec = 0;
   ovalue.it_interval.tv_nsec = 0;
   ovalue.it_value.tv_sec = MILLION;               /* a large number */
   ovalue.it_value.tv_nsec = 0;
   if (timer_settime(timeid, 0&ovalue, NULL== -1) {
      perror("Failed to set interval timer"); 
      return 1;
   }
   function_to_time();                       /* timed code goes here */
   if (timer_gettime(timeid, &nvalue) == -1) {
      perror("Failed to get interval timer value");
      return 1;
   }
   diftime = MILLION*(ovalue.it_value.tv_sec - nvalue.it_value.tv_sec) +
      (ovalue.it_value.tv_nsec - nvalue.it_value.tv_nsec)/THOUSAND;
   printf("The function_to_time took %ld microseconds or %f seconds.\n",
           diftime, diftime/(double)MILLION);
   return 0;
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
 

Timer drift

 

- 타이머가 정기적으로 울리지만, '타이머가 종료' <-> '다시 시작' 사이에 딜레이가 발생할 수 있다.

- 이 딜레이가 커지면서 누적되면, 문제가 발생할 수 있다.

- 따라서 22초마다 울리게 하고 싶으면, 알람시간 T에 대해 [T = current time + 22]보단, [T = T + 22]를 활용하는 것이 좋다.

- 절대시간을 활용하면 이런 문제를 좀 더 깔끔하게 해결할 수 있다. 

반응형