2010년 2월 2일 화요일

경고: 시계가 잘못되었음이 발견되었습니다.

리눅스에서 GCC로 작업하다가 다른 PC로 옮겨서 make를 실행해 보니 아래와 에러 메시지가 출력되네요.

make 경고make: 경고: 시계가 잘못되었음이 발견되었습니다. 빌드가 불완전할 수 있습니다.

시간이 없어서 급한 마음에 그냥 작업하려 했는데 계속 눈에 거슬립니다. 아마도 이전에 작업했던 PC와 시간 차이가 너서 그러는가 보다 했습니다. 해서 파일을 다른 곳에 옮겼다가 새로 복사해서 make를 실행했지만 똑 같은 경고 메시지가 나오네요.

저는 컴파일 에러 보다 경고(warning)메시지를 더 무서워하기 때문에 방법을 찾아 보았습니다. 그랬더니 이유는 작업 PC의 날짜가 아주 어뚱하게 설정되어 있네요. date 명령으로 날짜와 시간을 바로 잡아 주니 make 가 정상적으로 처리되어 있습니다.

그러나 date로 설정하는 날짜와 시간 입력 모습이 왜 이럴까요? 날짜와 시간을 아래와 같이 입력해 주어야 합니다.

]# date [월][일][시][분][년].[초]

예를 들어서 2007년 10월 16일 오후 10시 51분 19초라면

]# date 101622512007.19[엔터]입니다.

흠~ 좀 복잡해 보이죠. ^^;
date를 실행하시려면 su로 들어 가셔야 합니다.

]$ su - Password: ]# date 2000. 01. 01. (토) 01:01:01 KST ]# date 101622452007.30 2007. 10. 16. (화) 22:45:30 KST !!! 그러나 더 쉬운 방법이 있습니다. 바로 타임 서버를 이용하는 것입니다. !!! ]# rdate -s time.bora.net ]# date 2007. 10. 16. (화) 22:49:47 KST

매우 간단하고 간편합니다. ^^ rdate 도 기억해야 겠네요.

fcntl 레코드잠금


설명
fcntl() 함수는 파일을 전체나 파일의 일부를 다른 프로세스에서 사용하는 것을 제한할 수 있도록 해 주는 함수입니다. 물론 파일을 사용하기 위해 open() 함수나 fopen() 함수의 mode를 이용하여 다른 프로세스가 읽기나 쓰기를 제한할 수 있습니다. 그러나 이것은 파일 전체에 대해 적용되며 제한 내용을 변경하기 위해서는 파일을 닫았다가 다시 열기를 해야 합니다.
fcntl()은 열려진 파일에 대해서 필요에 따라 제한을 여러 번 변경하실 수 있으며, 파일 전체 뿐만 아니라 일부를 제한, 즉 "잠금" 상태를 만들 수 있기 때문에 fcntl() 함수를 "파일 잠금 함수"라기 보다는 "레코드 잠금 함수"로 불리워 집니다.
*** 주의하실 내용은 *** 파일에 대한 잠금은 프로세스별로 잠금 정보를 지정하는 것 뿐이지 실제로 다른 프로세스가 읽고 쓰는 것을 못하게 하는 것은 아닙니다. 즉, 쓰기를 제한했다고 해서 다른 프로세스에서 write() 가 실행이 안 되거나 에러가 발생하지 않습니다.
잠금 정보는 하나의 파일을 여러 프로세스가 동시에 사용하는 경우, 같은 시간에 쓰기를 하거나 아직 한쪽에서 변경 중이라면 다른 프로세스가 읽지를 못하게 하기 위한 정보를 주고 받기 위한 방법으로 이해햐셔야 합니다.
아래 예제에서도 쓰기를 하기 전에 쓰기 잠금이 가능한지를 확인한 후에 write() 를 실행했습니다. 그러나 확인없이 쓰기를 한다면 쓰기가 가능합니다. 그러므로 잠금 정보는 프로세스가 쓰기를 못하게 한다가 아니라 지금은 읽어 서는 안 된다 또는 쓰기를 해서는 안 된다라는 정보로 이용하셔야 합니다.
int fcntl(int fd, int cmd, struct flock * lock);
cmd에는 아래와 같이 상수로 정의되어 있습니다.
cmd의미
F_GETLK레코의 잠금 상태를 구해지며, 정보는 세번째 인수인 lock에 담겨져 옮니다.
F_SETLK레코드 잠금을 요청하며, 다른 프로세스가 먼저 선점해서 실패했다면 즉시 -1 로 복귀합니다.
F_SETLKW끝의 W는 wait의 약자로 레코드 잠금을 요청했는데, 다른 프로세스가 먼저 선점해서 실패했다면 그 프로세스가 해제할 때까지 대기합니다.
이번에는 struct flock * lock의 내용을 알아 봐야 겠지요. ^^
struct flock {
        short   l_type;
        short   l_whence;
        off_t   l_start;
        off_t   l_len;
        pid_t   l_pid;
        __ARCH_FLOCK_PAD
};
l_type 은 어떻게 잠금을 할지, 해제할지를 정합니다. 즉, 아래와 같은 상수가 정의되어 있습니다.
l_type의미
F_RDLCK다른 프로세스가 읽기 잠금만 가능하게하고 쓰기 잠금은 못하게 합니다.
F_WRLCK다른 프로세스는 읽기 잠금과 쓰기 잠금 모두 불가능하도록 합니다.
F_UNLCK잠금을 해제합니다.
l_whence 는 블록할 영역을 지정하는 기준 위치를 지정합니다. 즉, 파일 첫 부분부터 따질지, 아니면 현제 읽기/쓰기 포인터를 기준으로 따질지를 정합니다.
l_whence의미
SEEK_SET파일의 시작 위치
SEEK_CUR현재 읽기/쓰기 포인터를 기준
SEEK_END파일의 끝을 기준
l_start와 l_len은 l_whence가 가르키는 위치에서 블록을 지정합니다. 또한 l_len이 0 의 값이 되면 l_len값은 l_start부터 파일 끝 사이의 크기가 됩니다.
즉,
  • l_whence가 SEEK_CUR이고
  • l_start 가 0이면서
  • l_len 이 0 이면
파일 전체를 가르키게 됩니다. 아래의 그림을 보십시오.
l_pid 는 F_GETLK 를 실행하여 레코드에 대한 잠금 상태 정보를 구할 때, 이미 잠금을 실행하고 있는 프로세스의 ID 입니다.
헤더unistd.h, fcntl.h
형태int fcntl(int fd, int cmd, struct flock * lock);
인수
int fd제어 대상 파일 디스크립터
int cmd제어 동작 명령
struct flock * lock잠금을 위한 옵션
반환
-1실패
-1 !=cmd에 따라 달라짐
예제 1
예제에서는 같은 파일을 부모 프로세스가 열기를 하고 7번째 문자부터 7개의 문자 영역을 읽기만 가능할 뿐 쓰기를 해서는 안된다는 잠금 정보를 설정합니다. 차일드 프로세스는 쓰기를 할 때, 변경하려는 영역이 과연 쓰기가 가능한 것인지를 확인한 후에 쓰기를 합니다.
예제에서 차일드는 2 곳에 따로따로 쓰기를 하는데, 한 번은 쓰기가 가능한 곳이지만 다른 한 곳은 부모 프로세스에 의해 읽기 잠금한 영역입니다. 이 때 쓰기가 어떻게 되는지 보겠습니다.
예제에서 사용하는 test.txt의 내용은 "FORUM.FALINUX.COM HAPPY NEW YEAR!!" 입니다.
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main()
{
   char    *filename = "./test.txt";
   int      fd;
   pid_t    pid;
   struct   flock filelock;

   pid   = fork();
   switch( pid)
   {
      case -1  :
      {
         printf( "자식 프로세스 생성 실패\n");
         return -1;
      }
      case 0   :
      {
         printf( "자식: 부모 프로세스를 위해 잠시 대기하겠습니다.\n");
         sleep( 1);

         printf( "자식: 파일의 내용을 수정합니다.\n");
         fd = open( filename, O_RDWR ¦ O_CREAT, 0666);

         filelock.l_type   = F_WRLCK;
         filelock.l_whence = SEEK_SET;
         filelock.l_start  = 0;
         filelock.l_len    = 6;

         if ( -1 == fcntl( fd, F_SETLK, &filelock))
         {
            printf( "자식:레코드 잠금에 실패해서 forum을 쓰지를 못했습니다.\n");
         }
         else
         {
            write( fd, "forum", 6);
         }

         filelock.l_type   = F_WRLCK;
         filelock.l_whence = SEEK_SET;
         filelock.l_start  = 6;
         filelock.l_len    = 7;

         if ( -1 == fcntl( fd, F_SETLK, &filelock))
         {
            // 여기서는 이 If 절을 만족하게 됩니다.
            printf( "자식:레코드 잠금에 실패해서 falinux를 쓰지 못했습니다.\n");
         }
         else
         {
            write( fd, "falinux", 7);
         }

         close( fd);
         break;
      }
      default  :
      {
         printf( "부모: 파일을 열고 레코드 잠금하겠습니다.\n");
         fd = open( filename, O_RDWR, 0666);

         filelock.l_type   = F_RDLCK;
         filelock.l_whence = SEEK_SET;
         filelock.l_start  = 7;
         filelock.l_len    = 7;

         if ( -1 != fcntl( fd, F_SETLK, &filelock))
         {
            printf( "부모: 잠금에 성공했으며 3초간 대기합니다.\n");
            sleep( 3);
         }
         close( fd);
      }
   }
}
]$ ./a.out
]$ cat test.txt
FORUM.FALINUX.COM HAPPY NEW YEAR!!          // 모두 대문자
]$ ./a.out
자식: 부모 프로세스를 위해 잠시 대기하겠습니다.
부모: 파일을 열고 레코드 잠금하겠습니다.
부모: 잠금에 성공했으며 3초간 대기합니다.
자식: 파일의 내용을 수정합니다.
자식: 레코드 잠금에 실패해서 falinux를 쓰지 못했습니다.
]$ cat test.txt
forumFALINUX.COM HAPPY NEW YEAR!!          // 역시 첫번째 쓰기만 적용되었습니다.
]$
예제 2
예제 1은 cmd를 F_SETLK를 사용했지만 이번에는 잠금 상태가 해제될 때까지 기다리는 F_SETLKW를 사용해 보겠습니다.
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main()
{
   char    *filename = "./test.txt";
   int      fd;
   pid_t    pid;
   struct   flock filelock;

   pid   = fork();
   switch( pid)
   {
      case -1  :
      {
         printf( "자식 프로세스 생성 실패\n");
         return -1;
      }
      case 0   :
      {
         printf( "자식: 부모 프로세스를 위해 잠시 대기하겠습니다.\n");
         sleep( 1);

         printf( "자식: 파일의 내용을 수정합니다.\n");
         fd = open( filename, O_RDWR ¦ O_CREAT, 0666);

         filelock.l_type   = F_WRLCK;
         filelock.l_whence = SEEK_SET;
         filelock.l_start  = 0;
         filelock.l_len    = 6;

         if ( -1 == fcntl( fd, F_SETLKW, &filelock))
         {
            printf( "레코드 잠금에 실패해서 forum을 쓰지를 못했습니다.\n");
         }
         else
         {
            write( fd, "forum", 6);
         }

         filelock.l_type   = F_WRLCK;
         filelock.l_whence = SEEK_SET;
         filelock.l_start  = 6;
         filelock.l_len    = 7;

         if ( -1 == fcntl( fd, F_SETLKW, &filelock))
         {
            printf( "레코드 잠금에 실패해서 falinux를 쓰지 못했습니다.\n");
         }
         else
         {
            write( fd, "falinux", 7);
         }

         close( fd);
         printf( "자식: 프로그램을 종료합니다.\n");
         break;
      }
      default  :
      {
         printf( "부모: 파일을 열고 레코드 잠금하겠습니다.\n");
         fd = open( filename, O_RDWR, 0666);

         filelock.l_type   = F_RDLCK;
         filelock.l_whence = SEEK_SET;
         filelock.l_start  = 7;
         filelock.l_len    = 7;

         if ( -1 != fcntl( fd, F_SETLK, &filelock))
         {
            printf( "부모: 잠금에 성공했으며 3초간 대기합니다.\n");
            sleep( 3);
         }
         // 파일이 닫히면 자동으로 잠금이 해제
         close( fd);
      }
   }
}
]$ ./a.out
자식: 부모 프로세스를 위해 잠시 대기하겠습니다.
부모: 파일을 열고 레코드 잠금하겠습니다.
부모: 잠금에 성공했으며 3초간 대기합니다.
자식: 파일의 내용을 수정합니다.
]$ 자식: 프로그램을 종료합니다.

]$ cat test.txt
forumfalinux.COM HAPPY NEW YEAR!!
]$