본문 바로가기
리눅스

(번역) File locking

by 다움위키 2025. 1. 31.

원문 보기: https://dawoum.duckdns.org/wiki/File_locking

 

Original article: w:File locking

파일 잠금은 특정 시간에 단 한 명의 사용자나 프로세스만 파일을 수정하거나 삭제할 수 있도록 허용하고, 파일이 수정되거나 삭제되는 동안에는 파일을 읽는 것을 방지함으로써 컴퓨터 파일이나 파일의 특정 영역에 대한 접근을 제한하는 메커니즘입니다.

시스템은 업데이트 프로세스의 직렬화를 임의의 주어진 파일에 강제함으로써, 경쟁 조건의 한 예인 중재 업데이트(interceding update) 시나리오를 방지하기 위해 잠금을 구현합니다. 다음 예는 중재 업데이트 문제를 보여줍니다:

  1. 프로세스 A는 고객의 계좌 잔액과 전화번호를 포함한 계좌 정보를 포함하는 파일에서 고객 기록을 읽습니다.
  2. 프로세스 B는 이제 같은 파일에서 같은 레코드를 읽으므로, 그것은 자체 사본을 가집니다.
  3. 프로세스 A는 고객 기록 사본의 계좌 잔액을 변경하고 기록을 다시 파일에 씁니다.
  4. 고객 기록 사본에 계정 잔액에 대한 원래의 오래된 값이 여전히 남아 있는 프로세스 B는 계정 잔액을 업데이트하고 고객 기록을 파일에 다시 씁니다.
  5. 프로세스 B가 오래된 계좌 잔액 값을 파일에 기록했기 때문에 프로세스 A에서 변경한 내용이 손실되었습니다.

대부분의 운영 시스템기록 잠금의 개념을 지원하며, 이는 임의의 주어진 파일 내의 개별 기록은 잠길 수 있음을 의미하며, 이를 통해 동시 업데이트 프로세스의 수가 증가합니다. 데이터베이스 유지 관리에서는 파일 잠금을 사용하여, 데이터베이스의 기반이 되는 전체 물리적 파일에 대한 접근을 직렬화할 수 있습니다. 이렇게 하면 다른 프로세스가 파일에 접근하는 것을 방지할 수 있지만, 각 잠금을 획득하고 해제하는 오버헤드를 제거하여 파일의 여러 영역을 개별적으로 잠그는 것보다 효율적일 수 있습니다.

모든 컴퓨터 잠금과 마찬가지로 파일 잠금의 잘못된 사용은 성능 저하를 초래하거나 교착 상태를 발생시킬 수 있습니다. 파일 잠금은 Windows 보안, NTFS 권한을 사용하거나 타사 파일 잠금 소프트웨어를 설치함으로써 컴퓨터 사용자에 의해 적용되는 추가 보안을 의미할 수도 있습니다.

In mainframes

IBM은 OS/360을 사용하는 메인프레임 컴퓨터에서 사용하기 위해 1963년에 파일 잠금을 개척했으며, 이를 "독점 제어(exclusive control)"라고 불렀습니다.

In Microsoft Windows

Microsoft Windows는 공유 파일에 대한 접근을 관리하기 위해 세 가지 고유한 메커니즘을 사용합니다:

  1. 응용 프로그램에게 읽기, 쓰기, 또는 삭제를 위한 전체-파일 접근 공유를 지정하도록 허용하는 공유-접근 제어를 사용
  2. 단일 파일 내의 영역에 대한 읽기 및 쓰기 접근을 중재하기 위해 바이트-범위 잠금 사용
  3. Windows 파일 시스템에 의해 실행 중인 파일이 쓰기 또는 삭제 접근을 위해 열리는 것을 허용하지 않음

Windows는 MS-DOS 시스템에서 공유-접근 제어의 의미론을 상속받았으며, MS-DOS 3.3에서 공유가 도입되었습니다. 따라서, 응용 프로그램은 파일을 열 때 명시적으로 공유를 허용해야 합니다; 그렇지 않으면 닫힐 때까지 파일에 대한 독점적인 읽기, 쓰기, 및 삭제 접근 권한이 부여됩니다 (파일의 속성을 검색하는 것과 같은 다른 유형의 접근은 허용됩니다).

공유 접근으로 열린 파일에 대해, 응용 프로그램은 바이트-범위 잠금을 사용하여 파일의 특정 영역에 대한 접근을 제어할 수 있습니다. 그러한 바이트-범위 잠금은 파일의 영역 (오프셋 및 길이)과 잠금 유형 (공유 또는 배타)을 지정합니다. 잠기는 파일의 영역은 파일 내에 데이터가 있을 필요가 없으며, 응용 프로그램은 때때로 이 기능을 활용하여 기능을 구현함에 주목하십시오.

Windows에서 파일 읽기/쓰기 API를 사용하는 응용 프로그램에 대해, 바이트-범위 잠금은 Windows 내에서 실행되는 파일 시스템에 의해 강제됩니다 (필수 잠금(mandatory locks)이라고도 함). Windows에서 파일 매핑 API를 사용하는 응용 프로그램에 대해, 바이트-범위 잠금은 강제되지 않습니다 (권고 잠금(advisory locks)이라고도 함). 바이트-범위 잠금은 Windows 시스템에 다른 부작용을 일으킬 수도 있습니다. 예를 들어, Windows 파일-공유 메커니즘은 전형적으로 임의의 클라이언트에 의해 바이트-범위 잠금이 사용될 때 모든 클라이언트에 대해 파일의 클라이언트 측 캐싱을 비활성화할 것입니다. 읽기 및 쓰기 작업을 파일이 저장된 서버로 보내야 하기 때문에 클라이언트는 접근 속도가 느려질 것입니다.

응용 프로그램에서 부적절한 오류-처리는 파일이 잠기고 ("공유" 접근 사용 또는 바이트-범위 파일 잠금 사용) 다른 응용 프로그램에 의해 접근될 수 없는 시나리오로 이어질 수 있습니다. 그럴 경우, 사용자는 오작동하는 프로그램을 수동으로 종료함으로써 파일 접근을 복원할 수 있습니다. 이는 전형적으로 작업 관리자 유틸리티를 통해 수행됩니다.

CreateFile 함수 (파일을 여는 데 사용)의 공유 모드 (dwShareMode) 매개변수는 파일-공유를 결정합니다. 공유 모드는 읽기, 쓰기, 또는 삭제 접근, 또는 이들의 임의의 조합을 위해 파일을 공유를 허용하도록 지정될 수 있습니다. 파일을 열려는 후속 시도는 이전에 부여된 모든 파일 공유-접근과 호환되어야 합니다. 파일이 닫힐 때, 공유-접근 제한이 조정되어 해당 특정 파일 열기로 인해 부과된 제한이 제거됩니다.

바이트-범위 잠금 유형은 파일의 영역을 잠그기 위해 사용되는 LockFileEx 함수의 dwFlags 매개변수에 의해 결정됩니다. Windows API 함수 LockFile도 사용될 수 있고 파일 영역에 대한 배타적 잠금을 획득합니다.

현재 컴퓨터 시스템에서 프로그램으로 실행 중인 실행 가능한 프로그램 파일을 포함하는 임의의 파일 (예를 들어, EXE, COM, DLL, CPL 또는 기타 바이너리 프로그램 파일 형식)은 통상적으로 운영 시스템 자체에 의해 잠겨 있어, 임의의 응용 프로그램도 이를 수정하거나 삭제할 수 없습니다. 프로그램 파일이 임의의 응용 프로그램에 의해 열리지 않았다는 사실에도 불구하고 이를 행하려는 임의의 시도는 공유 위반 오류로 거부될 것입니다. 어쨌든, 일부 접근은 여전히 허용됩니다. 예를 들어, 실행 중인 응용 프로그램 파일은 실행 중일 때에도 이름을 바꾸거나 복사 (읽기)할 수 있습니다.

파일은 파일 핸들을 사용함으로써 Windows에서 응용 프로그램에 의해 접근됩니다. 이들 파일 핸들은 Process Explorer 유틸리티로 탐색될 수 있습니다. 이 유틸리티는 핸들을 보유한 응용 프로그램을 종료하지 않고도 핸들을 강제로 닫는 데 사용될 수도 있습니다. 강제로 닫힌 핸들을 사용할 때 프로그램이 예기치 않은 오류를 수신하고 핸들 번호가 재활용될 수 있으므로 예기치 않은 파일에서 작동할 수도 있으므로 정의되지 않은 동작이 발생할 수 있습니다.

Microsoft Windows XPServer 2003 에디션은 NTFS에 볼륨 스냅샷 (VSS) 기능을 도입하여, 임의의 배타적 잠금에도 불구하고 백업 소프트웨어에 의해 열린 파일에 접근할 수 있도록 했습니다. 어쨌든, 이 기능을 특별히 지원하도록 소프트웨어를 다시 작성하지 않는 한, 스냅샷은 충돌 시에만 일관성이 유지되고, 적절하게 지원되는 응용 프로그램은 운영 시스템이 "트랜잭션 일관성이 있는" 스냅샷을 만드는 데 도움을 줄 수 있습니다. Windows 아래에서 잠긴 파일에 접근하기 위한 다른 상용 소프트웨어로는 File Access ManagerOpen File Manager가 있습니다. 이들 소프트웨어는 커널 모드에서 파일에 접근하기 위해 자체 드라이버를 설치하여 작동합니다.

In Unix-like systems

유닉스-계열 운영 시스템 (Linux와 Apple의 macOS 포함)은 통상적으로 열린 파일을 자동으로 잠그지 않습니다. 여러 종류의 파일-잠금 메커니즘이 다양한 유닉스 변종에서 사용 가능하고, 많은 운영 시스템도 호환성을 위해 두 가지 이상을 지원합니다. 가장 공통적인 메커니즘은 fcntl입니다. 다른 두 가지 메커니즘은 flock(2)lockf(3)이며, 각각 fcntl 위에 구현되거나 fcntl과 별도로 구현될 수 있습니다. 비록 일부 유형의 잠금은 필수로 구성될 수 있을지라도, 유닉스 아래에서 파일 잠금은 기본적으로 권고 모드입니다. 이것은 협력하는 프로세스는 잠금을 사용하여 서로 파일에 대한 접근을 조정할 수 있지만, 비협력하는 프로세스도 잠금을 무시하고 그것들이 선택한 임의의 방식으로 파일에 접근할 수 있음을 의미합니다. 다시 말해, 파일 잠금은 다른 파일 로커만 잠그고 I/O는 잠그지 않습니다.

두 가지 종류의 잠금: 공유 잠금과 배타적 잠금이 제공됩니다. fcntl의 경우에서, 서로 다른 종류의 잠금은 파일의 서로 다른 섹션 (바이트 범위)에 적용되거나, 전체 파일에 적용될 수 있습니다. 공유 잠금은 여러 프로세스에 의해 동시에 보유될 수 있지만, 배타적 잠금은 오직 한 프로세스에 의해 보유될 수 있고, 공유 잠금과 공존할 수 없습니다. 공유 잠금을 획득하기 위해, 프로세스가 임의의 배타적 잠금을 보유하는 프로세스가 없을 때까지 기다려야 합니다. 배타적 잠금을 획득하기 위해, 프로세스가 두 종류의 잠금을 보유하는 프로세스가 없을 때까지 기다려야 합니다. fcntl로 생성된 잠금과 달리, flock으로 생성된 잠금은 fork에서 보존되며, 그것들을 포킹 서버에서 유용하게 만듭니다. 그러므로 이들 프로세스가 자식 관계를 공유하고 배타적 잠금이 fork에서 복제되기 전에 단일 프로세스에서 처음 생성된다는 조건으로 하여, 여러 프로세스가 같은 파일에 대한 배타적 잠금을 보유할 수 있습니다.

공유 잠금은 때때로 "읽기 잠금"이라고 불리고 배타적 잠금은 때때로 "쓰기 잠금"이라고 불립니다. 어쨌든, 유닉스에서 잠금은 권고 모드이므로, 이것이 강제되지 않습니다. 따라서 데이터베이스가 "공유 쓰기" 대 "배타적 쓰기"라는 개념을 가질 수 있습니다; 예를 들어, 공유 접근에서 필드를 변경하는 것은 허용되지만 가비지-수집 및 데이터베이스 다시 쓰기에는 배타적 접근이 필요할 수 있습니다.

파일 잠금은 파일 이름이 아닌 실제 파일에 적용됩니다. 이것은 유닉스에서 여러 이름이 같은 파일을 참조할 수 있기 때문에 중요합니다. 비-필수 잠금과 함께, 이것은 여러 프로세스에서 파일에 접근하는 데 큰 유연성으로 이어집니다. 다른 한편, 협력 잠금 방식은 프로세스가 다른 프로세스에 의해 설정한 파일 잠금을 따르지 않고 파일에 쓸 때 문제로 이어질 수 있습니다.

이러한 이유로, 일부 유닉스-계열 운영 시스템도 필수 잠금(mandatory locking)에 대한 제한적인 지원을 제공합니다. 그러한 시스템에서, 해당 파일을 열 때 setgid 비트가 켜져 있지만 그룹 실행 비트가 꺼진 파일은 놓여있는 본 파일 시스템이 그것을 지원하면 자동 필수 잠금의 대상이 됩니다. 어쨌든, 비-지역 NFS 파티션은 이 비트를 무시하는 경향이 있습니다. 만약 파일이 필수 잠금의 대상이면, 배타적 잠금으로 잠긴 영역에서 읽거나, 공유 또는 배타적 잠금으로 잠긴 영역에 쓰려는 시도는 잠금이 해제될 때까지 차단됩니다. 이 전략은 System V에서 처음 시작되었으고, 오늘날 Solaris, HP-UX, 및 리눅스 운영 시스템에서 볼 수 있습니다. 어쨌든, 그것은 POSIX의 일부가 아니고, FreeBSD, OpenBSD, NetBSD, 및 Apple의 macOS와 같은 BSD-파생 운영 시스템은 그것을 지원하지 않습니다. 리눅스도 파일 시스템 마운팅 (mount(8))을 위한 특수 -o mand 매개변수를 통해 필수 잠금을 지원하지만, 거의 사용되지 않습니다.

일부 유닉스-계열 운영 시스템은 실행 중인 프로그램의 실행 파일을 쓰기 목적으로 여는 시도를 차단합니다; 이것은 fcntl 및 flock에서 제공하는 것과는 별개의 세 번째 잠금 형태입니다.

Problems

둘 이상의 프로세스는 배타적 잠금이 이후 fork에서 복제되면 주어진 파일에 배타적 flock을 보유할 수 있습니다. 이것은 네트워크 서버에 대한 코딩을 간소화하고 경쟁 조건을 방지하는 데 도움이 되지만, 모르는 사람에게는 혼란스러울 수 있습니다.

필수 잠금은 unlink 시스템 호출에 영향을 미치지 않습니다. 결과적으로, 특정 프로그램은 필수 잠금을, 효과적으로, 우회할 수 있습니다. Stevens & Rago (2005)는 ed 편집기가 실제로 그렇게 했다고 관찰했습니다.

NFS와 같은 네트워크 파일 시스템에서 flock 잠금이 작동하는지 여부와 방법은 구현에 따라 다릅니다. BSD 시스템에서, NFS-마운트된 파티션의 파일에 열려 있는 파일 설명자에 대한 flock 호출은 성공적인 no-ops입니다. 2.6.12 이전의 Linux에서, NFS 파일에 대한 flock 호출은 지역에서만 작동합니다. 2.6.12 이상의 커널은 POSIX 바이트-범위 잠금을 사용하여 NFS 파일에 대한 flock 호출을 구현합니다. 이들 잠금은 fcntl -스타일 POSIX 잠금을 구현하는 다른 NFS 클라이언트에서는 볼 수 있지만, 그렇지 않은 클라이언트에서는 볼 수 없습니다.

잠금 업그레이드 및 다운그레이드는 새 잠금을 적용하기 전에 이전 잠금을 해제합니다. 또 다른 응용 프로그램이 배타적 잠금을 기다리며 차단된 동안 응용 프로그램이 배타적 잠금을 공유 잠금으로 다운그레이드하면, 후자의 응용 프로그램이 배타적 잠금을 얻고 첫 번째 응용 프로그램을 잠글 수 있습니다. 이것은 잠금 다운그레이드가 차단될 수 있음을 의미하며, 이는 반직관적일 수 있습니다.

주어진 프로세스에 대한 파일과 결합된 모든 fcntl 잠금은 해당 프로세스에 의해 해당 파일의 임의의 파일 설명자가 닫혀 있을 때 제거되며, 해당 파일 설명자에 대한 잠금이 요청되지 않았더라도 마찬가지입니다. 역시, fcntl 잠금은 자식 프로세스에 상속되지 않습니다. fcntl 닫기 의미론은 파일에 접근할 수 있는 서브루틴 라이브러리를 호출하는 응용 프로그램에 특히 문제가 됩니다. 이들 "버그"는 실제 flock -스타일 잠금을 사용하여 발생하지 않습니다.

유닉스 도메인 소켓을 사용하여 다른 프로세스에 전달된 열린 파일 기술자의 잠금 상태를 유지하는 것은 구현에 따라 달라집니다.

Buffered I/O problems

잠금 실패의 한 가지 원인은 버퍼링된 I/O가 운영 시스템 버퍼 풀에서가 아닌 사용자의 지역 작업 공간에 버퍼가 할당해 왔을 때 발생합니다. fread 및 fwrite는 공통적으로 버퍼링된 I/O를 수행하기 위해 사용되고, 한번 파일의 섹션을 읽으면, 같은 섹션을 읽으려는 다른 시도는 대부분 지역 버퍼에서 데이터를 가져옵니다. 문제는 같은 파일에 부착된 또 다른 사용자에게 자체 지역 버퍼가 있고, 같은 일이 해당 사용자에게도 발생한다는 것입니다. fread에 의해 버퍼에서 얻은 데이터의 fwrite는 파일 자체에서 데이터를 획득하지 않을 것이고, 어떤 다른 사용자가 이를 변경했을 수 있습니다. 둘 다 flock을 사용하여 배타적 접근을 보장하여 동시 쓰기를 방지할 수 있지만, 읽기는 파일 자체가 아닌 버퍼에서 읽기 때문에 사용자 #1이 변경한 임의의 데이터는 사용자 #2가 손실 (덮어-쓰기)할 수 있습니다. 이 문제에 대한 가장 좋은 해결책은 flock과 함께 버퍼링되지 않은 I/O (read 및 write)를 사용하는 것이며, 이는 fseek 및 ftell 대신 lseek를 사용함을 의미합니다. 물론, 함수 매개변수와 반환된 결과에 대한 조정을 해야 합니다. 일반적으로 말하자면, 버퍼링된 I/O는 공유 파일과 함께 사용할 때 안전하지 않습니다.

In AmigaOS

AmigaOS에서, 파일 (또는 디렉토리)에 대한 잠금은 Lock 함수 (dos.library에 있음)를 사용하여 획득될 수 있습니다. 잠금은 공유 (다른 프로세스가 파일/디렉토리를 읽을 수 있지만, 그것을 수정하거나 삭제할 수 없음), 또는 잠금을 성공적으로 획득한 프로세스만 대상에 접근하거나 수정할 수 있도록 배타적일 것입니다. 잠금은 대상 전체에 적용되고 대상의 일부는 아닙니다. 잠금은 UnLock 함수로 해제해야 합니다: Unix와 달리, 운영 시스템은 프로세스가 종료될 때 대상을 암묵적으로 잠금 해제하지 않습니다.

Lock files

쉘 스크립트와 다른 프로그램은 종종 파일 잠금의 사용과 유사한 전략을 사용합니다: 즉, 잠금 파일의 생성으로, 잠금 파일은 그 컨텐츠가 중요하지 않고 (종종 파일에서 잠금 보유자의 프로세스 식별자를 찾을 수 있지만), 유일한 목적은 어떤 자원이 잠겨 있음을 존재를 통해 알리는 파일입니다. 잠금 파일은 종종 제어될 자원이 전혀 정규 파일이 아니면 가장 좋은 방법이므로, 파일 잠금에 대한 방법을 사용하는 것은 적용하지 않습니다. 예를 들어, 잠금 파일은 여러 개의 다른 파일, 디렉토리, 디스크 파티션의 그룹, 또는 서버나 데이터베이스 연결과 같은 상위 수준 프로토콜에 대한 선택된 접근과 같은 관련 자원의 집합에 대한 접근을 제어할 수 있습니다.

잠금 파일을 사용할 때, 작업이 원자적이어야 한다는 점에 주의해야 합니다. 잠금을 얻기 위해, 프로세스가 잠금 파일이 존재하지 않는지 확인하고 그런-다음 그것을 만들어야 하며, 그 사이에 다른 프로세스가 잠금 파일을 만드는 것을 방지해야 합니다. 이를 위한 다양한 방법은 다음과 같습니다:

  • lockfile 명령을 사용합니다 (procmail 패키지에 포함된 조건부 세마포어-파일 생성기).
  • 파일을 생성하지만, 파일이 이미 존재하면 실패하는 시스템 호출. (시스템 호출은 C 또는 C++와 같은 언어에서 사용할 수 있고, 쉘 스크립트는 noclobber를 사용할 수 있음)
  • mkdir 명령을 사용하고 실패에 대한 종료 코드 확인

잠금 파일은 종종 그것들이 잠그는 파일의 이름 앞에 틸드 (~)를 접두사로 붙이거나, 전체 파일 이름의 복제본에 .LCK를 접미사로 붙인 이름을 붙입니다. 만약 그것들이 파일이 아닌 자원을 잠글 때, 그것들은 더 임의로 이름을 지정될 수 있습니다.

Unlocker software

언락커는 무슨 프로세스가 파일을 잠그고 있는지 결정하기 위해 사용되는 유틸리티고, 프로세스 목록과 프로세스에 대한 작업 선택 사항 (작업 종료, 잠금 해제 등)을 표시하고 삭제나 이름 바꾸기와 같은 파일 옵션 목록도 표시합니다. 언락커의 목적은 부적절하거나 오래된 파일 잠금을 제거하는 것입니다. 이는 종종 충돌이나 멈춘 프로세스와 같은 비정상적인 상황에서 발생하며, 소유 프로세스가 이미 종료되었음에도 불구하고 파일 잠금이 지속되는 결과를 초래합니다. 일부 유닉스-계열 시스템에서, fstat 및 lockf와 같은 유틸리티는 프로세스별, 파일 이름별, 또는 둘 다로 파일 잠금 상태를 검사하기 위해 사용될 수 있습니다.

Windows 시스템에서, 만약 파일이 잠겼으면, 다음 재부팅 시에 이동 또는 삭제를 수행하도록 예약할 수 있습니다. 이 방법은 전형적으로 설치 프로그램에 의해 잠긴 시스템 파일을 대체하기 위해 사용됩니다.

Version control systems

버전 제어 시스템에서, 파일 잠금은 두 사용자가 같은 파일 버전을 병렬로 변경하고 그런-다음 저장할 때 두 번째 사용자가 첫 번째 사용자가 변경한 내용을 덮어쓰는 것을 방지하기 위해 사용됩니다. 이것은 잠긴 파일을 파일 시스템에서 읽기-전용으로 표시함으로써 구현됩니다. 파일을 변경하려는 사용자는 잠금 해제 (체크아웃이라고도 함) 작업을 수행하고, 체크인 (저장) 작업이 수행되거나 잠금이 되돌려질 때까지 다른 사람은 파일의 잠금을 해제할 수 없습니다.