검색결과 리스트
글
설명
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); | |||||||
인수 |
|
|||||||
반환 |
|
예제 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!! ]$
'Linux > C/C++' 카테고리의 다른 글
dup() 파일 디스크립터 복사본 만들기 (0) | 2012.07.11 |
---|---|
strdup() 문자열의 clone 만들기 (0) | 2012.07.11 |
ftruncate() 파일을 지정한 크기로 변경 (0) | 2012.07.11 |
How to truncate a file in C? (0) | 2012.07.11 |
파일 유무 확인 (0) | 2012.07.10 |
RECENT COMMENT