• 한 레코드 혹은 파일에 대한 읽기 잠금은 여러 프로세스가 공유할 수 있지만, 쓰기 잠금은 공유할 수 없으며 한 프로세스만 가질 수 있다.
• fcntl() 시스템 호출을 이용하여 지정된 영역에 대해 잠금 검사, 잠금 설정 혹은 잠금 해제를 할 수 있다.
• lockf() 함수를 이용하여 지정된 영역에 대해 잠금 검사, 잠금 설정 혹은 잠금 해제를 할 수 있다.
프로세스A가 파일에 쓴 내용을 B가 읽음
문제가 발생하는 경우
: 한 프로세스가 파일 내용을 수정하는 동안에 다른 프로세스가 그 파일을 읽는 경우
: 두 개의 프로세스가 하나의 파일에 동시에 접근하여 데이터를 쓰는 경우
문제해결법 : 잠금
파일 전체를 잠궈도 되지만, 여러 사용자가 한 파일을 동시에 접근할 경우 비효율적이다. ( 사용자가 파일접근 할때마다 파일 전체를 잠궈야 해서 속도 등 성능에 영향이 간다)
특정 영역만 잠금하여 나머지 부분은 다른 프로세서에서 접근 가능하도록 하는것이 효율적이다
fcntl() 함수 : 파일 및 레코드 잠금을 구현할 수 있다.
잠금의 종류 : 읽기잠금(F_RDLCK), 쓰기잠금(F_WRLCK)
#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; // 프로세스 번호
};
#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);
}
#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);
}