시간처리

Mokwon Univ 2008. 8. 16. 17:07
APK009 시간 처리
==============================================

1. 개요

   이 문서는 ESP-NS에서 동작하는 응용 프로그램에서
   시간과 관련된 처리를 해야 하는 경우에 대한 소개입니다.
  
  
   작성자 : 유영창 frog@falinux.com
   작성일 : 2004년 9월 17일
   수정일 : 2005년 3월 10일
  
   관련된 ADK( Application Developer Kit ) 디렉토리
  
   adk/sample/time
  

2. 달력 시간

   임베디드 시스템의 경우 시간 관리는 거의 필수적인 사항이라고 봅니다.
   그 만큼 시간에 관련된 처리는 자주 발생하는 프로그램 사항입니다.
  
   그런데 시간의 의미는 여러가지가 있읍니다.
  
   가장 먼저 생각 나는 것은 달력 시간입니다. 달력 시간이라는 것은
   우리가 흔히 알고 있는 날짜와 관련된 시간을 말합니다.
  
   "2004년 11월 12일 12시 30분 29 초"
  
   이렇게 표시되는 것이죠...
  
   이런 정보를 얻기 위해서는 어떻게 해야 할까요?
   리눅스 응용프로그램에서는 어떤 함수를 사용해야 할까요?

2.1 time 함수

   우선 가장 오래되고 전통적인 방법인 time 이라는 함수에 대하여 알아 봅시다.

   이 함수는 다음과 같은 형태를 갖고 있읍니다.

    time_t time (time_t *result) ;

   이 함수를 사용하려면 #include <time.h> 를 포함해야 합니다.

   이 함수는 협정 세계시간 1970년 1월 1일 00: 00: 00 시간 이후 경과된 초의 수를 result 에 지정된 변수에
   넘겨 줍니다.
  
   함수 반환값은 대부분의 경우 무시해도 됩니다. 이 함수가 실패할 가능성은 거의 없기 때문에
   고민해서 저처럼 머리털 빠지지 않았으면 합니다.

   이 함수의 사용은 다음과 같이 하면 됩니다.

    time_t     cur_time;

    time( &cur_time );

    printf( "time value %ldn", cur_time );

   이 예는 현재 시간을 받아서 초로 표시해 주는 예 입니다.

   자..

   여기서 한가지 이 함수와 관련된 문제를 밝혀 드리겠읍니다.

   time_t 란 변수는 long int 형입니다.

   그래서 이 값은 134217727 에서 -134217728 사이의 값을 가집니다.

   이 값으로 표현할수 있는 최대 연도는 2038년으로 제한 됩니다.

   그렇다면 이후의 날짜는 어떻게 설정할까요?

   현재 glibc 를 사용하는 한 불가능합니다.

   다른 유닉스 시스템 중 일부에서는 time64 라는 함수와 time64_t 라는 변수형을 선언해서
   이 문제를 해결 하고 있지만 리눅스에서는 그 해결 방한을 아직까지 안 내놓은 것으로
   압니다. ( 물론 제가 해결책을 인터넷에서 아직 발견하지 못했기 때문일 수도 있읍니다. )

   이 시간의 문제는 단순히 함수만의 문제는 아닙니다.
   리눅스 운영체제에서 사용하는 각종 시간 관련 함수들을 사용하는 유틸리티나 응용 프로그램에서
   동시에 발생하는 문제 입니다.

   이 문제를 해결하고 싶다면 시간과 관련된 함수를 자신이 직접 작성해서 해결하는 방법 이외에는
   현재 대안은 없읍니다. 아니면 glibc 에서 해결해 주기를 기다려야 겠지요....


2.2 gmtime, localtime 함수

    time 함수로 반환된 것은 초 단위로 연산하기 때문에 시간에 대한 연산을 하기에 편리합니다.
    하지만 LCD 에 시간을 표시하거나 시간과 관련된 스케쥴링 처리를 다룰 경우에는
    우리가 보통 표현하는 방법인 년 월 일 시 분 초 로 구별되어 표현되는 함수가 필요합니다.

    이런 시간 형식으로 시간을 구하는 함수에는 gmtime 과 localtime이라는 두 함수가 있읍니다.

    이 두 함수의 차이는 무엇일까요?

    여러분이 국내에다 팔고 말 물건을 만든다면 그냥 gmtime 과 localtime 은 무시하고 쓰셔도 무방합니다.
    이런 경우에는 그냥  localtime을 사용하실 것을 권장합니다.
    이때 환경 변수에 설정할 것이 있는데 이것에 대한 것은  이 글을 계속 읽고 나시면 아실겁니다.
   
    여러분이 시스템을 만든다고 합시다.

    문제는 두 장비가 하나는 우리나라에 또 하나는 미국에 있다고 합시다.
    여기서 생기는 문제는 우리나라와 미국은 시간대가 틀리다는 겁니다 .
   
    우리나라보다 미국은 12시간 정도의 차이가 납니다.

    이런 경우 두 장비간에 10:00 시간에 상호 동기를 맞추기 위한 통신을 시작 하자고 하거나
    어떤 데이터의 기록 시간을 정한다고 한다면 문제가 생깁니다. 우리나라의 10:00와
    미국의 10:00는 다른 시간 시점이 되기 때문입니다.

    이럴 경우에는 세계 기준 시간을 사용하는 것이 정확한 것입니다.
   
    즉 세계 기준 시간인 10:00 에 서로 통신하기로 한다면
    우리나라와 미국이 서로 시간대가 틀리더라도 아무 문제가 없다는 것입니다.

    왜냐하면 세계 기준 시간은 서로 다른 위치에 있어도 동일한 시간을 가리키기 때문입니다.

    리눅스 시스템은 원칙적으로 커널에서 관리하는 내부 시간은 세계 기준 시간이라고 봅니다.

    이 세계 기준 시간을 가져오는 함수가 gmtime 이라는 함수 입니다.

    반면에 세계 기준 시간에 해당하는 우리나라의 시간을 구하는 함수는 localtime 입니다.

    그래서 gmtime으로 구한 함수는 시스템에 내부적으로 설정된 시간이고 localtime은
    그 시간을 각 지역에 맞도록 변환한 시간입니다.

    이 localtime 은 환경 변수 TZ 라는 것에 영향을 받습니다.

    이 TZ 값을 보려면 다음과 같은 명령을 사용하면 보입니다.

    
    # echo $TZ

    아마도 ESP-NS 에서는 이 값 이 아무런 값으로도 설정 되어 있지 않을 겁니다.

    이런 경우에는 gmtime 에서 구한 시간과 localtime에서 구한 시간은 동일한 시간값으로
    구해집니다.

    이때의 시스템 시간을 보려면 다음 명령을 치면 됩니다.

    # date
    Thu Jan  1 01:14:30 Universal 1970

    이렇게 나온 것은 ESP-NS 가 초기 생산시에는 시간을 설정하지 않아서 입니다.
    이 부분은 이후에 RTC에 대한 설명을 할때 다시 설명할 것입니다.

    일단 임시로 시간을 설정하려면 다음과 같이 설정해야 합니다.
    설정하려는 시간이 2005년 3월 8일 23시 21분 00 초 라면
      
    # date -s 030823212005.00
    Tue Mar  8 23:21:00 Universal 2005

    와 같이 해 주어야 합니다.

    보시는 바와 같이 date -s 명령의 형식은 다음과 같습니다.

    YYYY년 MM 월 DD 일 HH 시 NN 분 SS 초 라면

    date -s MMDDHHNNYYYY.SS

    이런식으로 들어 갑니다.

    자 이 상태에서 TZ 값을 우리나라 시간대로 맞추어 봅시다.
    다음과 같이 하면 됩니다.

    # export env TZ=GMT-09:00:00

    이렇게 설정한 상태에서 date 명령을 사용하여 시간을 보면 다음과 같이
    나옵니다.

    [root@ez-x5 /acu]$ date
    Wed Mar  9 08:25:13 GMT 2005

    표출된 시간을 보면 알겠지만 시스템에 설정된 시간보다 9시간이 지난 시간이
    표출됩니다.

    여기서 TZ 환경 변수에 대하여 조금 더 자세하게(?) 알아 보겠읍니다.

    이 TZ 환경 변수는 보통 부팅시에 초기화 스크립트에 의해서 설정됩니다.

    리눅스 시스템이 설치된 지역을 나타내는 지명을 쓰는 경우가 일반적입니다.

    예를 들어 TZ=Seoul 과 같은 형식입니다.

    하지만 이렇게 설정하는 것은 timezone 파일이 필요하고 복잡하므로

    저는 GMT-09:00:00 에 대한 것만 설명하려 합니다.
    제 경험으로는 이정도면 충분하고 설정도 간단하기 때문입니다.

    TZ 환경 변수에 GMT형식으로 설정하는 것은
    localtime 함수에 얼마의 시간을 더하거나 빼는가를 지정하는 것입니다.

    형식은 다음과 같습니다.
    GMT[+/-][HH:MM:SS]

    GMT 란 문자열은 GMT 형식을 나타내고
    + 또는 - 는 그리니치 천문대를 중심으로 서쪽에 있으면 + 동쪽에 있으면 - 입니다.

    우리나라는 동쪽에 위치하고 9시간의 차이가 있으므로

    TZ=GMT-09:00:00 으로 설정하는 것입니다.

    이 값은 ESP-NS 의 경우에는

    /etc/rc.d/rc.local 파일에 마지막에

    export env TZ=GMT-09:00:00

    란 항목을 넣으시면 됩니다.

    이제 gmtime 의 함수와 localtime 함수의 형식을 알아 봅시다.

    이 함수의 사용법은 다음과 같이 같습니다.

    
    struct tm *localtime (const time_t *time)
    struct tm *gmtime(const time_t *time)

    이 함수에서 사용하는 time 은 time 함수에서 구한 값의 변수 주소를 지정하면 되고
    그 결과로 tm 구조체의 주소를 반환합니다.

    사용 예는 다음과 같습니다.

    time_t     cur_time;
    struct tm *tm_data;

    time( &cur_time );

    tm_data = gmtime( &cur_time );
    printf( "GMT   TIME %04d-%02d-%02d %02d:%02d:%02dn",
                       tm_data->tm_year + 1900,
                       tm_data->tm_mon +1,
                       tm_data->tm_mday,
                       tm_data->tm_hour,
                       tm_data->tm_min,
                       tm_data->tm_sec );

    tm_data = localtime( &cur_time );
    printf( "LOCAL TIME %04d-%02d-%02d %02d:%02d:%02dn",
                       tm_data->tm_year + 1900,
                       tm_data->tm_mon +1,
                       tm_data->tm_mday,
                       tm_data->tm_hour,
                       tm_data->tm_min,
                       tm_data->tm_sec );
    

   tm 구조체에서 우리가 주로 쓰고 알아야 하는 필드 변수는 다음과 같습니다.


     int tm_sec          : 0 에서 59 까지의 범위를 갖는 초(second)
     int tm_min          : 0 에서 59 까지의 범위를 갖는 분(minute)
     int tm_hour         : 0 에서 23까지의 범위를 갖는 시(hours)
     int tm_mday         : 1 에서 31까지의 범위를 갖는 달
     int tm_mon          : 0 에서 11까지의 범위를 갖는 달
     int tm_year         : 1900년 이후의 년(year)
     int tm_wday         : 0 에서 6의 범위를 갖는, 일요일을 기점으로 해서 요일에 해당하는 수

   조심해야 하는 것은 구해진 년도는 1900을 더해 주어야 우리가 사용하는 년도가 된다는 것과
   월은 1 을 더해 주어야 우리가 사용하는 월이 된다는 점입니다.

   또 한가지 요일을 표현하는 tm_wday는 일=0 월=1 화=2 수=3 목=4 금=5 토=6 라는 점입니다.
   우리나라 사람들은 일주일의 시작을 월요일 부터라고 생각하지만 
   미국 사람들은 일요일 부터라고 생각합니다.

   시간을 나타내는 문자열의 함수를 glibc 에 있는 것을 사용하면
   우리나라 실정에는 조금 불편합니다. 그래서 저는 위와 같은 형식으로 직접 문자열로
   변환해 사용합니다.


2.3 mktime 함수

  
   지금까지 초를 시간으로 변환하는 함수를 알아 보았는데
   당연히 반대 함수도 있겠죠?

   예를 들어 2005년 9월 12일 18시 30분 40 초를 초로 바꾸려면

   mktime 함수를 사용해야 합니다.

   이 함수의 형식은 다음과 같습니다.

   time_t mktime(struct tm *brokentime)

   tm 구조체는 앞에서 설명한 것과 같습니다.

   위 시간을 초로 바꾸는 예제는 다음과 같습니다.

    time_t     timev;
    struct tm  tm_src;
    struct tm *tm_data;

    tm_src.tm_year = 2005 - 1900;
    tm_src.tm_mon  =    9 - 1;
    tm_src.tm_mday =   12;
    tm_src.tm_hour =   18;
    tm_src.tm_min  =   30;
    tm_src.tm_sec  =   40;

    timev = mktime( &tm_src  );

    tm_data = gmtime( &timev );
    printf( "GMT   TIME %04d-%02d-%02d %02d:%02d:%02dn",
                       tm_data->tm_year + 1900,
                       tm_data->tm_mon +1,
                       tm_data->tm_mday,
                       tm_data->tm_hour,
                       tm_data->tm_min,
                       tm_data->tm_sec );

    tm_data = localtime( &timev );
    printf( "LOCAL TIME %04d-%02d-%02d %02d:%02d:%02dn",
                       tm_data->tm_year + 1900,
                       tm_data->tm_mon +1,
                       tm_data->tm_mday,
                       tm_data->tm_hour,
                       tm_data->tm_min,
                       tm_data->tm_sec );

3. gettimeofday 함수 ( 정밀 시간 )

   임베디드 시스템에서는 초 단위의 시간만으로는 I/O 제어를 하거나 통신 재 전송 시간 관리
   같은 매우 짧은 시간 간격의 제어가 불가능합니다.

   그래서 매우 정밀한 시간 측정이 필요한데 이럴때 사용하는 것이 gettimeofday 라는 함수
   입니다.

   이 함수는 다음과 같은 형식을 가지고 있읍니다.

   
   int gettimeofday (struct timeval *tp, struct timezone *tzp)

   여기서 tzp 매개변수는 무조건 NULL 이라고 생각하시면 됩니다.
  
   timeval 구조체는 다음과 필드 변수를 가지고 있읍니다.

   long int tv_sec : time 함수에서 구한 값과 같은 의미로
                     협정 세계시간 1970년 1월 1일 00: 00: 00 시간 이후 경과된 초입니다.

   long int tv_usec : 마이크로초(microsecond)로 표현된 시간값인데 부가적으로 흐른 시간 마이크로 초입니다.


   이 함수를 사용하는 예는 다음과 같습니다.

    struct timeval tp;
    int lp;

    for( lp = 0; lp < 1000; lp++ )
    {
        gettimeofday ( &tp, NULL );
        printf( "Sec %ld uSec %ldn", tp.tv_sec, tp.tv_usec );
    }

    이 함수를 저는 주로 지나간 시간을 측정하는 용도로 사용합니다.
    또한 대부분의 경우 미리 초 단위로 측정하면 되기 때문에
    다음과 같은 함수를 만들어서 사용합니다.


    static struct timeval  __mclock_start;   // 기준 시작 시간
   
    void mclock_init( void )
    {
        gettimeofday( &__mclock_start, NULL );
    }
   
    long int mclock( void )
    {
        struct timeval timecurrent;
        struct timeval timeresult;
       
        gettimeofday( &timecurrent, NULL );
       
        timeresult.tv_sec  = timecurrent.tv_sec  - __mclock_start.tv_sec;
        timeresult.tv_usec = timecurrent.tv_usec - __mclock_start.tv_usec;
   
        if( timeresult.tv_usec < 0 )
        {
            timeresult.tv_sec--;
            timeresult.tv_usec += 1000000;
        }
       
        return timeresult.tv_sec * 1000 + ( timeresult.tv_usec / 1000 );
    }
   
    void mclock_test( void )
    {
        long int start_mclock;
        int lp;
   
        mclock_init();  // 프로그램 전체에서 한번만 설정해 주면 된다.
   
        for( lp = 0; lp < 10000; lp++ )
        {
            printf( "mclokc : %ld m Sec n", mclock() );
        }
       
    }

*** clock 함수의 문제

   10mSec 이상의 정밀한 측정시에 리눅스 커널에서 제공하는 CPU 의 경과 시간을 측정함수 중에는
   clock 이라는 함수가 있읍니다. 이 함수가 정상적으로 동작만 한다면야 50mSec 이상의 처리
   시간 간격을 허용하는 경우라면 무척 편리한 함수가 될 겁니다.

   그러나 실제로 제가 시험해 보았을때는 이 함수는 제대로 동작하지 않았읍니다.
   원인을 찾아 보고 싶었지만 저의 귀차니즘으로 인하여 관뒀읍니다.

   getimeofday라는 막강한 함수가 있어서지요
   시간이 나시는 분들이 계시면 한번 쯤 도전해 보시기 바랍니다.
   왜 동작하지 않는지 ....

4. sleep,usleep 함수

    프로그램을 작성하다보면 일정 시간을 멈출 필요가 있을때가 있읍니다.
    이럴때 사용할수 있는 함수는 두가지가 있읍니다.

    하나는 sleep 이고 또 하나는 usleep 입니다.

    두 함수다 모두 프로세스를 잠들게 합니다.
    sleep 는 초 단위 간격으로 잠들게 하고 usleep 는 마이크로초 즉 0.000001 단위로 잠들게 제어할수있습니다.

    그...러...나

    usleep 이 정말 0.000001 초 단위로 잠들게 할수 있는냐?

    아니죠.. 왜냐?

    프로세스가 잠들기 때문이죠....

    제 실험 결과는 19 미리 초 이하 즉 0.019 초 이하로는 잠들게 할수 없읍니다.
    무척 슬픈 일이죠...

    이말은 usleep 함수에서 문제가 될수 있읍니다.

    그래서 정확하게 잠들게 하고 싶다면
    ( 물론 하나의 프로세스만 수행되고 있어서 프로세스 스케쥴이 전환되지 않는다는 가정하에서 )

    gettimeofday 함수를 이용하여 반복문을 사용하는 것이 더 정확합니다.
   
    sleep 함수와  usleep의 형식은 다음과 같습니다.

       #include <unistd.h>

       unsigned int sleep (unsigned int seconds);
       void         usleep(unsigned long usec);

    sleep 함수를 사용하는 예제는 다음과 같습니다 .

    printf( "sleep startn" );
    sleep(2);
    printf( "sleep endn" );


    usleep 함수를 사용하는 예제는 다음과 같습니다 .

    long int old,cur;

    mclock_init();
    printf( "usleep startn" );
   
    old = mclock();
    usleep(1);
    cur =  mclock();
    printf( "mclock : %ld m Sec n", cur - old );
    printf( "usleep endn" );

5. RTC 와 시간 설정 및 관리

   이제 시간과 관련한 마지막 문제인 RTC 라고 불리는 시계칩과
   리눅스 시간설정과 관리에 대한 문제에 대하여 이야기 해야 겠습니다.

   임베디드 시스템에서는 시간 설정은 반드시라고 할 만큼 일어나는 문제 입니다.

   그런데

   임베디드 리눅스 응용 프로그램을 작성하는 프로그래머가 부딪히는 문제 중 하나는
  
   RTC라고 하는 시계 칩과 리눅스 시스템의 시간과의 동기화 문제와
   시간이 변경될때 뜻하지 않는 여러가지 문제점에 봉착한다는 것입니다.

  이 문제를 하나씩 따져 봅시다.

5.1 RTC 와 시스템 시간 관리

   보통 임베디드 시스템에는 RTC 라는 시계 칩을 가지게 됩니다. CPU에
   포함되었든 외부장치에 붙었든 이 칩은 시스템에 전원이 나가도
   동작합니다.

   ESP-NS 보드에는 DS1307 이라는 RTC 를 사용합니다.

   자 여기서 질문이 있읍니다.

   리눅스 커널에서 사용하는 시간은 어디에 기준을 맞출까요?
  
   1) RTC와 연동되어 시간을 관리한다.
   2) OS 타이머를 이용하여 관리한다.
   3) 내부 타이머를 이용하여 관리한다.

   예...

   정답은 2 번이 되겠읍니다.

   이말은 여러분이 아무리 date 명령을 사용하여 리눅스 시간을 변경해도
   보드 전원을 내렸다 재 부팅하면 말짱 꽝이라는 말입니다 ㅜㅜ

   물론 RTC를 커널 소스에서 연결한다면 180 초 단위로
   ( 정확히 맞는지 오래 전에 기억했던 것이라 자신없음 ^^ ) 로 동기를 시킬 겁니다.

   하지만 ESP-NS 나 기타 대부분의 임베디드 보드의 커널에는 동기 처리가 빠진것으로 알고 있습니다.

   리눅스 시스템은 자주 껐다가 켜졌다가 하는 시스템으로 사용되기 보다는 계속 켜져 있는 시스템으로
   적합한 시스템입니다.

   또한 RTC는 생각보다 오차가 큰 시계입니다.
   전원에도 영향을 받고 온도에도 영향을 받습니다. (물론 미미합니다만 ㅜㅜ )

   그에 반해서 대부분의 시스템에서 사용하는 OS 타이머는 분해능이 높기 때문에
   무척 높은 정확도를 가집니다.

   그래서 리눅스 시스템은 RTC를 믿지 않고 자신이 계산하는 시간을 신봉합니다.

   즉.....

   리눅스 시스템은 주기적으로 자신의 시간으로 RTC를 설정할 필요가 있는 겁니다 .

   RTC의 시간을 가져와서 자신의 시간과 맞추는 경우는 부팅할때 딱! 한번 뿐입니다.

   그 외에는 RTC를 못 믿기 때문에 리눅스 커널 시간을 RTC에 주기적으로 설정하는 것이
   맞는 방법이라는 것입니다.

   물론 RTC의 오차라는 것이 몇년에 몇초 정도의 오차이기 때문에 아주 크리틸컬한
   시스템이 아니라면 굳이 동기화 시킬 필요는 없읍니다
   그래서 저는 동기화 처리 프로세스 프로그램을 따로 만들지는 않는 매우 게으른
   프로그래머입니다. ( 아.. 이 성격 고쳐야 하는데 쩝 )

   그...러...나

   외부에서 또는 사용자의 조작으로 인하여 시간을 수정한다면
   당연히 시간을 설정하는 함수 또는 유틸리티를 알고 있어야 합니다.

   자...

   그런데 앞에서 이야기 했지만 이럴 경우 date 명령이나  settimeofday 함수를 이용해 봤자
   RTC의 시간은 수정되지 않습니다. 그래서 전원을 내렸다가 올려 버리면 말짱 꽝입니다.

   그렇다면 이것을 어떻게 해결해야 할까요?

   우선 보드가 부팅 되었을때 RTC 에서 시간을 가져와서 시스템 시간을 설정하도록 해야 겠지요?

   가장 쉬운것은 보드에 hwclock 라는 프로그램이 있을 경우입니다. busybox에서 옵션으로
   이 프로그램을 선택할수 있을 겁니다.

   그런데 제가 게으른 관계로 ESP-NS 에 있는 파일 시스템에는 hwclock 이라는 유틸리티가
   탑제 되어 있지 않습니다.

   그래서 저희는 아주 무식하게 ds1307 디바이스 드라이버를 수정해서 다음과 같은 형식을
   이용하면 RTC에서 시간을 가져 오도록 하고 있읍니다. 이 것은 ESP-NS 에서만 사용할수
   있는 방법입니다. 다른 시스템에서는 사용할수 없음에 주의 하세요...

    /etc/rc.d/rc.local 에 다음 명령이 추가되도록 수정하면 됩니다.

    if [ -f /proc/driver/ds1307 ]; then
        echo "sync" > /proc/driver/ds1307
    fi

    이러면 알아서 동기화 시켜 줍니다.

    그렇다면 이런 비표준적인 방법 말고 리눅스에서 사용 가능한 방법은 무엇일까요?

    다음 소스 예처럼 함수를 하나 만들어서 부팅시에 실행 시켜 주면 됩니다.

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <sys/stat.h>
    #include <sys/ioctl.h>
    #include <time.h>
    #include <sys/time.h>
    #include <linux/rtc.h>

    void check_rtc_devicefile( void )
    {
        if( access( "/dev/rtc" , F_OK ) == 0 ) return;
   
        mknod( "/dev/rtc" , S_IRWXU|S_IRWXG|S_IFCHR,(10<<8|(135)));
    }
   
    void rtc2systemtime( void )
    {
        int              rtc;
        struct rtc_time  rtc_time_data;
        struct tm        tm_src;
        struct timeval   tv = { 0, 0 };
   
        check_rtc_devicefile();
   
        rtc = open ( "/dev/rtc", O_RDONLY );
        if( rtc < 0 )
        {
            perror( "/dev/rtc open error" );
            return;
        }
   
        if ( ioctl ( rtc, RTC_RD_TIME, &rtc_time_data ) < 0 )
        {
            perror( "/dev/rtc rtc read time error" );
            close( rtc );
            return;
        }
   
        tm_src.tm_year = rtc_time_data.tm_year;
        tm_src.tm_mon  = rtc_time_data.tm_mon;
        tm_src.tm_mday = rtc_time_data.tm_mday;
        tm_src.tm_hour = rtc_time_data.tm_hour;
        tm_src.tm_min  = rtc_time_data.tm_min;
        tm_src.tm_sec  = rtc_time_data.tm_sec;
   
        tv.tv_sec = mktime( &tm_src );
   
        settimeofday ( &tv, NULL );
    
        close( rtc );
   
    }

    check_rtc_devicefile 함수는 /dev/rtc 라는 장치파일을 체크해서 없으면 만들어 주는 함수입니다.
    rtc2systemtime 함수가 실제로 RTC에서 시간을 읽어서 시스템 시간으로 설정하는 함수입니다

    소스를 보시면 아시겠지만 ioctl 함수를 이용하여 /dev/rtc에서 RTC 시간을 얻어 온 후
    mktime 함수를 이용하여 초로 환산한후  settimeofday 함수를 이용하여 시스템 시간을 설정합니다.

    자....

    이제 반대로 설정하는 함수를 알아 봅시다.

    나중에 사용하기 편리하게 시스템 시간 설정과 RTC를 한꺼번에 설정하도록 만들면
    다음과 같이 됩니다.

    int set_systemtime( int year, int mon, int day, int hour, int min, int sec )
    {
   
        int              rtc;
        struct rtc_time  rtc_time_data;
        struct tm        tm_src;
        struct timeval   tv = { 0, 0 };
   
        tm_src.tm_year = rtc_time_data.tm_year = year - 1900;
        tm_src.tm_mon  = rtc_time_data.tm_mon  = mon  - 1;
        tm_src.tm_mday = rtc_time_data.tm_mday = day;
        tm_src.tm_hour = rtc_time_data.tm_hour = hour;
        tm_src.tm_min  = rtc_time_data.tm_min  = min;
        tm_src.tm_sec  = rtc_time_data.tm_sec  = sec;
   
        tv.tv_sec = mktime( &tm_src );
   
        settimeofday ( &tv, NULL );
       
        check_rtc_devicefile();
   
        rtc = open ( "/dev/rtc", O_WRONLY );
        if( rtc < 0 )
        {
            perror( "/dev/rtc open error" );
            return -1;
        }
   
        if ( ioctl ( rtc, RTC_SET_TIME, &rtc_time_data ) < 0 )
        {
            perror( "/dev/rtc rtc write time error" );
            close( rtc );
            return -1;
        }
   
        close( rtc );
   
       return 0;
   
    }

    이렇게 함수를 이용하여 시간 설정 프로그램을 짜기 싫다면 당근 hwclock 유틸리티를 이용하여
    설정하면 되는데 설명하기 귀찮으므로 여러분이 알아서 하십시오 ( 메롱... )

    이젠 마지막으로 아주 중요한 문제를 제기하고 여러 고수분들에게 해답을 구하면서
    마치도록하겠읍니다.

    여러분이 여러장치를 다루기 위해서 다중 입출력 프로그램을 이용하거나 이벤트 구조형태의
    프로그램을 작성하는 경우 select 나 poll 함수를 사용하게 됩니다.

    그런데 이 함수에는 타임 아웃을 설정하도록 하고 있는데
    시스템 시간 설정을 하는 프로세스가 따로 있다면 잘못하면 타임 아웃이 무한대로 걸리지
    않을 경우가 있습니다.

   이 버그를 만나면 아주 심각한데 이런 경우의 한 예로 다음과 같은 시스템을 구성할 경우에
   해당합니다.

   여러분이 아주 까다로운 고객이나 시간에 매우 크리티컬한 장치를 만들어야 하는 경우에
   타임서버나 GPS 를 이용하여 1초 간격으로 시간을 받아서 시스템 시간과 틀릴 경우에는
   자동으로 시간을 수정하도록 하는 요구를 받았다고 합시다.

   이런 경우 1초마다 시간을 설정하면 아주 치명적인 문제가 발생하는데 아까 이야기 했던
   select나 poll 기타 시간을 기다리는 부분에서 무한 대기가 발생하는 경우가 있다는 겁니다

   이런 경우 저는 전체 시스템에 시간을 처리하는 변수를 두고 현재 시스템 시간과 외부에서 강제로
   정해지는 정확한 시간간에 차를 구한 후 이를 사용하는 방법을 사용하여 가급적이면
   시스템 시간을 바꾸지 않도록 프로그램을 작성합니다. 이 방법도 완전한 처리 방법은 아닌것으로
   알고 있읍니다.

   이런 방법이외에 adjtime 이라는 함수를 사용하는 방법이 있는데 이 함수는 시간을 아주 천천히
   맞추어 주지요....

   하지만 이 함수는 제가 쓴 적이 없으므로 이글을 읽는 분중에서 고수분이 계시면 저에게
   사용 방법을 알려 주셨으면 하네요...

   그외 위에서 제기한 문제를 어떻게 효율적으로 수정하는지 알려 주셨으면 고맙겠읍니다.

   그럼 .... 시간과 관련하여 임베디드 리눅스에서 어떻게 다루는가에 대한 글은 이것으로 마치려 합니다.
Posted by 용학도리
,