6. 파일 및 레코드 잠금

김동웅·2021년 9월 16일
0

UNIX

목록 보기
7/14

• 한 레코드 혹은 파일에 대한 읽기 잠금은 여러 프로세스가 공유할 수 있지만, 쓰기 잠금은 공유할 수 없으며 한 프로세스만 가질 수 있다.
fcntl() 시스템 호출을 이용하여 지정된 영역에 대해 잠금 검사, 잠금 설정 혹은 잠금 해제를 할 수 있다.
lockf() 함수를 이용하여 지정된 영역에 대해 잠금 검사, 잠금 설정 혹은 잠금 해제를 할 수 있다.

6.1 파일 및 레코드 잠금

파일 및 레코드 잠금의 원리

  • 어떻게 프로세스 사이에 데이터를 주고받을 수 있을까?

프로세스A가 파일에 쓴 내용을 B가 읽음

  • 문제가 발생하는 경우
    : 한 프로세스가 파일 내용을 수정하는 동안에 다른 프로세스가 그 파일을 읽는 경우
    : 두 개의 프로세스가 하나의 파일에 동시에 접근하여 데이터를 쓰는 경우

  • 문제해결법 : 잠금

잠금(LOCK)

  • 한 프로세스가 그 영역을 읽거나 쓸때 다른 프로세스의 접근을 제한
  • 잠금된 영역에 하나의 프로세스만 접근 가능

❗ 파일잠금이 아닌 레코드 잠금 필요성

  • 파일 전체를 잠궈도 되지만, 여러 사용자가 한 파일을 동시에 접근할 경우 비효율적이다. ( 사용자가 파일접근 할때마다 파일 전체를 잠궈야 해서 속도 등 성능에 영향이 간다)

  • 특정 영역만 잠금하여 나머지 부분은 다른 프로세서에서 접근 가능하도록 하는것이 효율적이다

잠금 구현

  • fcntl() 함수 : 파일 및 레코드 잠금을 구현할 수 있다.

  • 잠금의 종류 : 읽기잠금(F_RDLCK), 쓰기잠금(F_WRLCK)

잠금함수 : fcntl()

#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>

int fcntl(int fd, int cmd, struct flock *lock);

cmd에 따라 잠금 검사 혹은 잠금 설정을 한다. 성공하면 0 실패하면 -1을 리턴
  • fd는 대상이 되는 파일 디스크립터

  • cmd

    • F_GETLK : 잠금을 걸수 있는지 검사
      잠금을 걸수 없게하는 잠금이 있으면 이미 걸려있는 잠금 정보가 l_type 채워짐(F_RDLCK, F_WRLCK, F_UNLCK)

    • F_SETLK : 잠금 설정 or 해제
      // l_type이 F_RDLCK(읽기잠금) or F_WRLCK(쓰기잠금)
      // 잠금설정 불가시 전역변수인 errno를 EAGAIN으로 설정

    • F_SETLKW : 잠금 설정 or 해제 ( 블로킹 버전 )

  • flock 구조체
    : 잠금 종류, 프로세스 ID, 잠금위치 등등..

  • 파일 잠금과 레코드 잠금은 동일 원리
    l_start를 파일의 시작위치 (SEEK_SET), l_len을 0으로 설정하면 된다.

struct flock 
{
short l_type; // 잠금종류
off_t l_start; // 잠금 시작 위치
short l_whence; // 기준 위치 : SEEK_SET,SEEK_CUR,SEEK_END
off_t l_len; // 잠금 길이 : 바이트 수 (0이면 파일 끝까지)
pid_t l_lid; // 프로세스 번호
};

잠금예제

  • 학생 레코드를 질의하는 프로그램 : rdlock.c
  • 학생 레코드를 수정하는 프로그램 : wrlock.c
  • 수정프로그램에서 어떤 레코드를 수정하는 중(wrlock.c)에는 프로그램 질의하는 프로그램인 rdlock.c을 할수 없ㅎ도록 레코드 잠금을 이용하여 제한한다.
  • rdlock.c
#include <stdio.h>
#include <fcntl.h>
#include "student.h"
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char* argv[])
{
	int fd,id;

	struct student record;
	struct flock lock;

	if(argc<2)
	{
		fprintf(stderr,"use : %s file\n",argv[0]);
		exit(1);
	}

	if( (fd=open(argv[1],O_RDONLY))==-1)
	{
		perror(argv[1]);
		exit(2);
	}
	
    // 검색하고자 하는 학생의 학번 입력;
	printf("\nEnter the student's number to search for : " );
	
	while(scanf("%d",&id)==1)
	{
    	// 해당 레코드 부분 읽기 잠금
		lock.l_type = F_RDLCK;
		lock.l_whence = SEEK_SET; 
		lock.l_start = (id-START_ID)*sizeof(record);
		lock.l_len = sizeof(record);
		if(fcntl(fd,F_SETLKW,&lock) ==-1 ) 
		{
			perror(argv[1]);
			exit(3);
		}
	
		// 파일 포인터를 해당 레코드부분 설정
		lseek(fd,(id-START_ID)*sizeof(record),SEEK_SET);
		// 검색결과 프린트
		if( (read(fd,&record,sizeof(record)) > 0) && (record.id!=0)  )
		{
			printf("NAME: %s \t NUM: %d \t SCR: %d \n",record.name,record.id,record.score);
		}
		else
		{
			printf("no record \n");
		}
		
      	        // 읽기 잠금 해제
		lock.l_type = F_UNLCK;
		fcntl(fd,F_SETLK,&lock);
		printf("Enter the student's number to search for :");
		
	}


	close(fd);
	exit(0);
	
}
  • wrlock.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include "student.h"

int main(int argc, char* argv[])
{
	int fd,id;
	struct student record;
	struct flock lock;

	if(argc <2)
	{
		fprintf(stderr,"use : %sfile\n",argv[0]);
		exit(1);
	}

	if( (fd=open(argv[1],O_RDWR))==-1)
	{
		perror(argv[1]);
		exit(2);
	}

	printf("\nENTER THE NUMBER YOU WANNA UPDATE : ");
	while(scanf("%d",&id)==1)
	{	
    		// 해당레코드 쓰기잠금
		lock.l_type = F_WRLCK;
		lock.l_whence = SEEK_SET;
		lock.l_start = (id-START_ID)*sizeof(record);
		lock.l_len = sizeof(record);
		if(fcntl(fd,F_SETLKW,&lock) == -1)
		{
			perror(argv[1]);
			exit(3);
		}
		
     	       // 파일포인터 해당레코드로 이동
		lseek(fd, (long)(id-START_ID)*sizeof(record), SEEK_SET);
		
    	        // 해당레코드 정보 출력
		if((read(fd,&record,sizeof(record))>0)&& (record.id!=0))
		{
			printf("이름:%s\t 학번:%d\t 점수:%d\n",record.name,record.id,record.score);
		}
		else
		{
			printf("no record\n");
			
			printf("\nENTER THE NUMBER YOU WANNA UPDATE : ");
			continue;
		}
		
        	// 수정할 점수 입력
		printf("new scored : ");
		scanf("%d",&record.score);
		
        	// 파일포인터 해당레코드 시작으로 재이동 ( 출력과정에서 파일포인터가 해당레코드의 끝부분으로 이동하였으므로) 
		lseek(fd,(long)-sizeof(record),SEEK_CUR);
        
        	// 수정
		write(fd,&record,sizeof(record));


		// 잠금해제
		lock.l_type = F_UNLCK;
		fcntl(fd,F_SETLK,&lock);

		printf("\nENTER THE NUMBER YOU WANNA UPDATE : ");
	}
	close(fd);
	exit(0);
}

0개의 댓글

관련 채용 정보