(문제 정의)
어떤 writer(쓰기 프로세스)와 reader(읽기 프로세스)가 동시에 하나의 공유 자원(변수, 파일 등)에 접근한다.
상호 배제(Mutual Exclusion)가 필요하다.
✅ Writer's lock (Exclusive lock, 배타적 잠금)
✅ Reader's lock (Shared lock, 공유 잠금)

#include <fcntl.h>
int fcntl(int filedes, int cmd, struct flock *lock);
filedes: 파일 디스크립터
cmd: 명령어 (lock 관련)
F_GETLK: 락을 걸 수 있는지 확인
이미 누군가 락을 걸었으면, 해당 락 정보가 채워진 구조체를 반환
락을 걸 수 있으면 F_UNLCK으로 설정된 구조체를 반환
F_SETLK: 락을 시도함 (비차단)
l_type을 F_RDLCK(읽기 락), F_WRLCK(쓰기 락)으로 설정해야 함
이미 락이 걸려 있으면 ➡︎ -1 반환
실패 시 errno는 ➡︎ EACCES 또는 EAGAIN
F_SETLKW: F_SETLK의 차단(blocking) 버전
F_SETLK + l_type == F_UNLCK

문제점➡︎ 두 프로세스가 동시에 작업하면서 데이터가 덮어써짐(overwrite)
해결책➡︎ 크리티컬 섹션 보호 (즉, 락 필요)
입금/출금 (update balance)
이 작업은 다른 입금/조회 작업과 겹치면 안 됨 (배타적이어야 함)
이를 writer's lock (exclusive lock)이라고 부름
조회 (read balance)
다른 조회 작업과는 동시에 가능, 쓰기 작업과는 동시에 불가능
이를 reader’s lock (shared lock)이라고 함

#include <fcntl.h>
int flock(int fd, int operation);
(매개변수 설명)
fd : 열린 파일 디스크립터
operation :
LOCK_SH
: 공유 잠금 (읽기용)
LOCK_EX
: 배타 잠금 (쓰기용)
LOCK_UN
: 현재 이 프로세스가 가진 잠금 해제
>LOCK_NB와 OR 연산자로 함께 사용하면 non-blocking(대기 없이 실패) 모드로 사용 가능
fork()로 자식 프로세스를 만들면 잠금은 상속되지 않음
exec()로 실행된 프로세스는 잠금을 상속받음
프로세스가 종료되면 ➡︎ 그 프로세스가 만든 모든 잠금은 자동으로 해제됨
파일 디스크립터(fd)를 close() 하면 ➡︎ 그 파일에 걸려있던 모든 잠금도 해제됨


커널 내부에서의 잠금 구조
(struct flock 구조)
시작 위치 (starting offset)
길이 (length)
프로세스 ID
잠금 타입 (flags)
커널이 강제하지 않음
읽기/쓰기가 잠금 규칙을 위반할 수 있음
프로세스가 자발적으로 잠금 규칙을 따라야 함
커널이 잠금 규칙을 강제로 적용
어떤 읽기/쓰기 작업도 잠금 규칙을 위반할 수 없음
모든 읽기/쓰기 호출마다 검사해야 하므로 커널 부하가 큼
(Mandatory 잠금 설정 방법)
set-group-ID 비트 ON, group-execute 비트 OFF
$ chmod 2644 lockfile (or $ chmod g+s,g-x lockfile)
파일 권한 예시: -rw-r--lr--