본문 바로가기

Common Gateway Interface/Perl

[옛 강좌] 29. Perlprog - BBS 8

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

 이 게시물은 지금은 폐쇄되어 접속되지 않는 Kim Young Soo(http://hours.interpia98.net/~unisoo/)님의 웹 사이트에 2001년경 게시된 내용을 바탕으로 오늘날 웹 환경에 맞게 내용을 덧붙였습니다.

Perlprog - BBS 8

Description

게시판 만들기 8 - 파일 잠금


BBS 만들기 8

 게시판 만들기 여덟번째 입니다. 이제 남은 건 검색과 관리자모드 입니다. 그 전에.... 지금까지 해왔던, 파일저장에 관해서 잠시 얘기할까 합니다. 처음에 이해가 안 된다고 하시더라도... 끝까지 읽다 보시면 이해하시게 될 겁니다.

파일 잠금에 대하여(About)

open(FILE, ">file");
print FILE ......;
close(FILE);
					

 우리가 어떤 파일에 쓰기위해선 위와 같이 했었죠. 근데 여기서 한가지 생각해 봐여.. 지금 만드는 게시판은 여러사람이 읽고 쓰기 위해서 만듭니다. 그러니 파일이 열리고 닫히는 수는 무수히 많겠죠.

 이 때 동시에 읽기 위해 열리는 경우는 크게 문제가 되지 않습니다. 파일에 어떤 영향을 주는건 아니니깐여.... 근데 저장하기 위해, 위처럼 여는 경우를 생각해 봅시다. 한 사람씩 차례대로 쓰기 위해 열었다면, 당연히 쓰고-닫히고, 쓰고-닫히고 하겠죠.

 근데... 아주 간발의 차이로.... 누군가 쓰기위해서 파일을 열었습니다. 지금 막 파일에 어떤 내용을 저장하는 순간인데.. 그 때 누군가 또 쓰기 위해서 파일을 열었습니다.

 기존에 데이터가 10개였고, 처음 쓰기위해 열었던 사람이 5번째 데이터까지 쓰고 있는데, 다른 사람이 또 쓰기 위해서 파일을 엽니다. 그러면 그 사람의 데이터까지 겹치게 되죠.

 처음 사람이 11번째 데이터가 되야 하고, 다음 사람이 12번째 데이터가 돼야 합니다. (참고로... 우리가 지금까지 한 프로그램은 파일의 내용을 항상 덮어쓰기로 열어서, 최근의 것이 제일 위로 올라가게끔 다시 쓰는 과정을 택했습니다.) 더욱이 우리가 구현한 답변기능은 더하죠. 바로 아래에 저장이 되어야 합니다.

 근데 위의 경우처럼, 간발의 차이로 들어오면, 데이터가 저장되어야 하는 위치에 저장이 안 되고 이상한 위치에 가서 저장이 됩니다(실제로 이같은 일을 겪었습니다.) 그리고 또 아래와 같은 경우가 생길수도 있습니다.

 텍스트로 되어있는 카운터를 예로보죠. 지금까지의 방문자수가 190명으로 기록되어 있었습니다. 이 때..... 누군가 들어왔고, 쓰기 위해 파일을 열었습니다. 이 때 파일 안에는 아무 내용도 없죠. 바로 이때..... 또 누군가 들어와서 파일을 열었습니다. 하지만.....밧뜨.... 파일에 내용이 없기 때문에 카운터가 0이되고, 여기에 1을 더해서 총채적인 카운터수가 1이 됩니다.

 전에 들어온 사람이 191을 기록했지만, 그 뒤에 간발의 차이로 온 사람의 데이터가 최종적으로 기록되므로, 결국은 1이라는 말도 안되는 수가 기록되게 됩니다. 비단 카운터뿐만이 아니죠. 게시판이나 방명록의 인덱스 파일이 이런 경우를 당할수도 있습니다. 파일을 쓰기위해 여는 거라면, 예기치 않은.... 데이터의 손실이 있을수 있습니다. 이걸 막기위해서 파일을 읽기위해, 또는 쓰기위해 여는경우, 한사람이 열어서 닫기까지는 다른 사람이 열고 닫는 것을 막아야 합니다. 그렇기에 파일 잠금이 필요합니다.

 하지만 읽기위해 여는 경우라면, 공유해도 상관없죠. 엄청 많은 사람이 읽기 위해 열었다 하더라도 데이터의 손실은 없을테니깐여.... 그렇기 때문에 파일 잠금 기능을 제공하는 flock()함수가 있습니다. 하지만... 모든 운영체제가 이 함수를 지원하는건 아닙니다.

flock()

 그럼 flock() 함수를 먼저 소개하고, 이 함수가 지원이 안되는 운영체제에선 어떻게 하는지 살펴보겠습니다.

flock(filehanle, operation)

-> 이런식으로 호출하게 됩니다.

open(FILE, ">test.db");
flock(FILE, 2);
print FILE "어쩌구 저쩌구...";
flock(FILE, 8);
close(FILE);
						

 그럼 하나씩 보겠습니다.

 filehandle은 말 그대로 열고자 하는 파일핸들을 말합니다. 위에서 test.db를 FILE이라는 파일핸들로 쓰기 위해 열었습니다. 이제 열어서 쓰고 닫힐 때까지는 다른 사람이 쓰기 위해서 열지 못하게 해야겠죠. 그래서 flock()으로 잠가둡니다.

내용을 쓰고.... 잠금을 해제 - flock() - 합니다. 그리고 파일을 닫고.... 그렇다면, 누군가 쓰기 위해 파일을 열었고, 파일을 잠근 상태에서 다른 사람이 접근하면, 어떻게 될까요. 그 사람은 먼저 파일을 열었던 사람이 닫을 때까지 대기 상태가 됩니다. 브라우져 상태표시줄에는 '웹 사이트를 찾았습니다. 응답을 기다리는 중...' 이라며, 대기하겠죠. 먼저 연 사람의 작업이 끝나면 그 다음 사람이 파일을 다시 열게 됩니다. 이제 파일 잠금의 작동에 대해서 이해를 하시겠죠.

flock() - 인수(Operation)

 그럼.. flock()의 두 번째 인수인 operation에 대해서 보겠습니다. 이 옵션에는 3개의 옵션이 있습니다.

share lock(LOCK_SH - 1)
파일을 읽기 위해 열 때 사용합니다. 공유의 목적입니다.
exclusive lock(LOCK_EX - 2)
파일을 쓰기 위해 열 때 사용합니다. 배타적인 공유죠. 한사람이 쓰면, 다른사람은 대기합니다.
unlock(LOCK_UN - 8)
잠갔던 파일을 해제하는 목적입니다.

 위처럼 있습니다. flock()에 사용하실 때는 옆의 숫자 - 1,2,8 - 로 쓰시면 됩니다.

flock(file, 1); flock(file, 8)

 사용하는건 쉽죠? ^^ 대부분의 소스에서 보면...

$LOCK_SH = 1;
$LOCK_EX = 2;
$LOCK_UN = 8;
						

 이런식으로 사용을 합니다. 보기 편하게 하기 위해서.... 이런 경우도 있었습니다. 파일을 읽으려고 열었는데.... 아무런 내용이 없더군요. 그럴리가..... 다시 열었더니 파일의 내용이 추가되어 있었습니다.

 이런 경우는... 누군가 파일을 쓸려고 열었져... 이 떄는 파일의 내용이 없습니다. 이때 제가 읽으려고 연 겁니다. 그러니 아무내용도 보이지 않았고... 누군가 파일을 저장하고 나갔고, 이 때 제가 다시 여니깐... 추가된 데이터와 함께 보이더군여...

지원되지 않는 운영체제(O/S)

 이제 윈도처럼 flock() 함수를 제대로 지원하지 않는 곳에선 어떻게 구현하는지 보겠습니다.

 우선.... 어떤 식으로 되는지 생각 먼저 하구여.... 첫 번째로 누군가 파일을 열었을 때(아님 열기 전에), 그 파일에 대해 .lock라는 파일을 임시로 만듭니다. 파일 이름이 guest.db라면... guest.db.lock(또는 guest.lock)

 그런다음, 이 사람이 다 쓰고 나면 이 임시파일을 지웁니다. 지우기 전에.... 누군가 쓰고 있는데, 또 다른 누군가가 들어 왔다면, 그 해당하는 파일에 대해서 .lock라는 파일이 있는지 검사를 합니다. 있다면 어느 시간만큼 대기 시켰다가, 다시 그 파일을 검사합니다. 없다면 그 파일에 대한 .lock파일을 다시 생성하고.... 다 했으면 .lock파일을 지웁니다. 이런식으로 되겠죠.

lock()

sub lock
{
	local ($file) = @_;
	$LockFile = "$file.lock";
	$EndTime = time + 45;
	while (-e $LockFile && time < $EndTime)
	{
	}
	open (LOCK,">$LockFile") || die $!;
	chmod 0666,$LockFile;
	$locked = 'Y';
}
						

unlock()

sub unlock
{
	# 이건 아래서....
}
						

예제(ex)

$db = "guest.db";
&lock($db);
open (FILE, ">$db");
	print FILE "....";
close(FILE);
&unlock($db);
						

위의 함수가 lock()을 구현한 함수 입니다.(flock(file, 2)가 되겠죠)

 우선 잠글 파일이름을 받고, 그 파일에 대해서 lock 파일을 설정하고, 잠시 쉴 시간을 정하고, 미리 만들어진(그러니깐, 누군가가 파일에 쓰고 있는지) lock파일이 있는지, 대기시간 만큼 대기했는지를 검사하고, 해당 lock파일을 생성하고, $locked 변수를 'Y'로 합니다. 파일은 잠그지 않습니다. 열어둔 상태로 있어야죠. 작업이 끝나면 닫고 지웁니다.

 time함수는 1970년 1월 1일 이후부터 현재까지의 시간을 초 단위로 리턴하는 함수입니다. 이 시간에 45초를 더하고, 다음 while루프에서 time함수를 계속 호출하면서 우리가 정한 45초의 시간만큼 대기하게 됩니다. 이렇게 해놓고 파일을 쓴다면 다음 사람은 대기 상태에 들어 가겠죠... 그런데.... 누군가.. 쓸려고 했다가 취소를 했어요. 그렇다면 lock파일이 남아 있을 수도 있습니다. 그렇담, 다음에 쓸려고 하는 사람을 마냥 대기시킬 수만은 없죠.

 위에서 정한 시간만큼(45초)만 대기하게 됩니다. 이 시간 동안 대기해도 파일이 없어지지 않았다면 그냥 씁니다. 그리고 작업이 끝나면 lock파일을 지웁니다. 서버로 쓰는 컴이 아무리 느려터져도, 45초 안에는 무슨 작업이든 하지 않겠습니까... 위의 while 루프는 and 연산을 썼죠. 하나라도 거짓이 된다면(파일이 없거나, 시간을 초과 했거나) 전체가 거짓이 되어서 루프를 빠져 나오게 됩니다. 이 시간이 길다면 조금 줄이면 됩니다.

 그렇담 여기서 숙제~~~~ ^^; (제 생각인데... 앞으로 이 시간이 공포의 시간이 되지 않을까.. ^^;)

 unlock() !!!! 이것의 구현이 바로 여러분의 숙제랍니당~~~ ^^;

힌트! : 위의 lock함수에서 파일을 열기만 했지 닫지는 않았죠. 그렇담....

  1. lock으로 설정된 파일 명을 먼저 받구,(당연 위의 파일 명과 같아야죠)
  2. $locked 변수가 "Y"로 되었는지 확인하구요,
  3. "Y"로 되었있다면, 열었던 lock파일을 잠그고, 지웁니다.
  4. 이 때... "Y"로 되었다 하더라도, 시스템 이상으로 파일이 없을수 도 있으니깐 파일이 있는지 없는지 검사합니다.
  5. $locked를 "N"로 다시 바꿉니다.

헐~~~ 이건 힌트가 아니라 답이네여....^^;

 여기서 다하면 좋지만, 하나라도 더 해보는 것이 프로그램을 이해하는데 훠~얼~씬 좋죠?


NOTES

 이로서 파일잠금에 대해서 알아보았습니다. 지금까지 프로그램할때는 이 과정을 생략했습니다. 포함시켜야 하겠죠... ^^; 그럼 다음엔 이제 얼마 남지 않은 게시판 만들기를 계속해서 하겠습니다.


이 문서는 Perl 패키지내의 pod2html를 이용하여 만들었습니다. - Kim Young Soo