본문으로 이동
  • 현재 모든 문서는 작성 중입니다. 보시는 데 불편함이 있더라도 양해 부탁드립니다.

운영체제 기본/파일과 입출력: 두 판 사이의 차이

~ldmsys
총론 재작성: 이름·객체·열린 상태 분리와 버퍼링·비동기·영속성의 trade-off
BIOS GRUB 부팅 경로 보강과 각론 형식 통일
 
(같은 사용자의 중간 판 3개는 보이지 않습니다)
4번째 줄: 4번째 줄:


== 저장 위치 대신 이름과 바이트를 제공하기 ==
== 저장 위치 대신 이름과 바이트를 제공하기 ==
프로그램이 저장장치의 어느 구역에 데이터가 있는지 직접 관리한다면, 파일이 커지거나 다른 프로그램이 공간을 사용했을 때 배치를 함께 조정해야 한다. 파일 시스템은 저장 공간의 할당과 회수를 맡고, 프로그램에는 이름으로 찾을 수 있는 파일을 제공한다. 디렉터리는 이름을 파일 객체에 연결하며, 파일 객체는 내용과 크기, 접근 권한 같은 정보를 가진다. 이름을 찾는 문제와 내용을 어디에 놓을지는 분리된다.
프로그램이 저장장치의 어느 구역에 데이터가 있는지 직접 관리한다면, 파일이 커지거나 다른 프로그램이 공간을 사용했을 때 배치를 함께 조정해야 한다. 파일 시스템은 저장 공간의 할당과 회수를 맡고, 프로그램에는 이름으로 찾을 수 있는 파일을 제공한다. 일반 파일의 '''데이터'''는 본문의 바이트이고, '''메타데이터'''는 크기, 종류, 소유자와 접근 권한, 접근·수정 시각처럼 파일을 관리하는 정보다. 내용을 저장장치의 어디에서 찾을지 나타내는 블록 매핑도 메타데이터에 속한다. Unix 계열에서는 inode가 이러한 파일별 정보를 담거나 관련 구조를 가리킨다. 이름과 달리 inode 번호는 해당 파일시스템 안에서 파일을 식별한다.<ref name="fs-inode">[https://man7.org/linux/man-pages/man7/inode.7.html Linux man-pages: inode(7)].</ref><ref name="fs-block-map">[https://docs.kernel.org/filesystems/ext4/ifork.html Linux kernel documentation: The Contents of inode.i_block], Extent Tree.</ref>


일반 파일을 바이트의 나열로 제공하면 프로그램은 저장장치의 세부 구조에 맞춰 매번 코드를 바꾸지 않아도 된다. Unix는 파일뿐 아니라 파이프와 터미널에도 읽기와 쓰기라는 공통 인터페이스를 제공한다. 덕분에 출력 대상을 바꾸어 프로그램을 조합하기 쉽다. 다만 인터페이스가 비슷해도 모든 대상의 성질이 같지는 않다. 일반 파일은 위치를 바꾸어 읽을 수 있지만 파이프는 그렇게 할 수 없으며, 바이트 스트림의 데이터에 붙일 레코드 구조는 프로그램이 정해야 한다. <ref name="source-1">[https://man7.org/linux/man-pages/man2/read.2.html Linux man-pages: read(2)].</ref>
일반 파일을 바이트의 나열로 제공하면 프로그램은 저장장치의 세부 구조에 맞춰 매번 코드를 바꾸지 않아도 된다. Unix는 파일뿐 아니라 파이프와 터미널에도 읽기와 쓰기라는 공통 인터페이스를 제공한다. 덕분에 출력 대상을 바꾸어 프로그램을 조합하기 쉽다. 다만 인터페이스가 비슷해도 모든 대상의 성질이 같지는 않다. 일반 파일은 위치를 바꾸어 읽을 수 있지만 파이프는 그렇게 할 수 없으며, 바이트 스트림의 데이터에 붙일 레코드 구조는 프로그램이 정해야 한다. <ref name="source-1">[https://man7.org/linux/man-pages/man2/read.2.html Linux man-pages: read(2)].</ref>
== 디렉터리와 경로로 파일을 찾기 ==
디렉터리는 자식 파일의 본문을 모두 품은 저장 상자가 아니라, 이름을 파일 식별자에 연결하는 항목들의 모음이다. Unix 계열의 ext4에서는 각 항목이 이름과 inode 번호를 연결한다. 디렉터리 자체도 inode와 내용을 갖지만, 그 내용은 자식 파일의 본문이 아니라 이런 연결 정보다.<ref name="fs-directory">[https://docs.kernel.org/filesystems/ext4/directory.html Linux kernel documentation: Directory Entries].</ref>
<code>/home/ldmsys/a.txt</code>는 하나의 파일 식별자가 아니라 탐색할 이름의 순서다. 프로세스의 루트 디렉터리에서 <code>home</code>을 찾고, 그 안에서 <code>ldmsys</code>, 다시 그 안에서 <code>a.txt</code>를 찾아 대상 inode에 도달한다. 중간 디렉터리에 탐색 권한이 없으면 마지막 파일의 읽기 권한만으로 접근할 수 없다.<ref name="fs-path">[https://man7.org/linux/man-pages/man7/path_resolution.7.html Linux man-pages: path_resolution(7)].</ref> 같은 파일시스템에서 여러 디렉터리 항목이 같은 inode를 가리키는 관계가 하드 링크다. 이름이 늘어도 본문이 복제되는 것은 아니다.<ref name="fs-inode"/>


== 이름, 객체, 열린 상태의 수명을 나누기 ==
== 이름, 객체, 열린 상태의 수명을 나누기 ==
파일을 열 때마다 경로를 다시 검색하는 대신, 운영체제는 찾은 객체를 계속 참조할 수 있는 핸들을 돌려준다. Unix의 파일 디스크립터가 그 예다. 핸들이 가리키는 열린 상태에는 읽고 쓰는 현재 위치 등이 들어 있다. 같은 파일을 따로 열면 위치를 독립적으로 유지할 수 있고, 열린 상태를 공유하는 핸들은 위치도 함께 사용한다. 파일 내용의 공유와 읽기 위치의 공유는 다른 선택이다. <ref name="source-2">[https://man7.org/linux/man-pages/man2/open.2.html Linux man-pages: open(2)].</ref>
파일을 열 때 운영체제는 경로로 찾은 객체를 계속 참조할 수 있는 핸들을 돌려준다. 이후 입출력마다 처음 경로를 다시 검색할 필요는 없다. Unix의 파일 디스크립터가 그 예다. 핸들이 가리키는 열린 상태에는 읽고 쓰는 현재 위치 등이 들어 있다. 같은 파일을 따로 열면 위치를 독립적으로 유지할 수 있고, 열린 상태를 공유하는 핸들은 위치도 함께 사용한다. 파일 내용의 공유와 읽기 위치의 공유는 다른 선택이다. <ref name="source-2">[https://man7.org/linux/man-pages/man2/open.2.html Linux man-pages: open(2)].</ref>
 
이 분리는 이름이 바뀌어도 진행 중인 작업을 유지하게 한다. 이름 하나를 삭제하는 것은 해당 디렉터리 항목을 제거하는 일이며, 다른 하드 링크가 남아 있으면 그 이름으로 파일에 접근할 수 있다. Unix에서 로그 파일의 마지막 이름을 삭제해도 프로세스가 파일을 열어 두었다면 계속 쓸 수 있다. 같은 이름으로 새 파일을 만들어도 기존 핸들이 새 파일로 옮겨 가지는 않는다. 이름이 사라진 시점과 저장 공간을 회수할 수 있는 시점이 달라지는 대신, 경로의 변경 때문에 이미 연 대상이 갑자기 바뀌지 않는다. <ref name="source-3">[https://man7.org/linux/man-pages/man2/unlink.2.html Linux man-pages: unlink(2)].</ref>
 
== 파일 안의 위치와 저장장치의 위치를 나누기 ==
바이트 오프셋은 파일 시작에서 몇 바이트 떨어졌는지를 나타낸다. 블록 기반 파일시스템은 이를 파일 내부의 논리 블록 번호와 블록 안의 위치로 나누고, 블록 매핑으로 장치상의 물리 블록을 찾는다. 예를 들어 블록이 4 KiB라면 오프셋 5000은 논리 블록 1의 블록 내 오프셋 904다. 논리 블록 1이 물리 블록 900에 대응할 수 있으므로 두 번호를 같다고 볼 수 없다. 여기서 물리 블록은 장치에 요청하는 주소이지 SSD 내부의 실제 셀 위치를 뜻하지 않는다.
 
<pre>
/home/ldmsys/a.txt
  -> / 의 home 항목 -> home의 ldmsys 항목
  -> ldmsys의 a.txt 항목
  -> 파일 inode: 크기, 권한, 블록 매핑 등
  -> 읽을 오프셋 5000 -> 논리 블록 1 + 블록 내 위치 904
  -> 블록 매핑 -> 물리 블록 900의 해당 데이터
</pre>


이 분리는 이름이 바뀌어도 진행 중인 작업을 유지하게 한다. Unix에서 로그 파일의 마지막 이름을 삭제해도 프로세스가 파일을 열어 두었다면 계속 쓸 수 있다. 같은 이름으로 새 파일을 만들어도 기존 핸들이 새 파일로 옮겨 가지는 않는다. 이름이 사라진 시점과 저장 공간을 회수할 수 있는 시점이 달라지는 대신, 경로의 변경 때문에 이미 연 대상이 갑자기 바뀌지 않는다. <ref name="source-3">[https://man7.org/linux/man-pages/man2/unlink.2.html Linux man-pages: unlink(2)].</ref>
extent는 연속된 논리 블록 구간을 연속된 물리 블록 구간에 대응시키는 표현이다. 시작 위치와 길이로 여러 블록을 묶어 기록한다. 연속 구간이 길면 블록별 주소를 나열하는 것보다 매핑 정보를 줄일 수 있지만, 파일이 흩어질수록 필요한 구간 수가 늘어난다.<ref name="fs-block-map"/> 반대로 희소 파일의 hole은 파일 크기 안에 있지만 데이터 블록을 할당하지 않은 구간이며, 읽으면 값이 0인 바이트들로 채워진다. 따라서 파일의 크기와 실제 할당된 저장 공간은 다를 수 있다.<ref name="fs-hole">[https://man7.org/linux/man-pages/man2/lseek.2.html Linux man-pages: lseek(2)], DESCRIPTION.</ref> 위 흐름은 이름에서 데이터 위치로 이어지는 관계다. 필요한 내용이 캐시에 있다면 실제 장치 읽기는 생략할 수 있다.


== 버퍼링과 비동기로 기다리는 비용 줄이기 ==
== 버퍼링과 비동기로 기다리는 비용 줄이기 ==
CPU가 작은 입출력 요청마다 느린 장치의 완료를 기다리면 계산할 시간이 낭비된다. 버퍼는 속도가 다른 두 쪽 사이에서 데이터를 잠시 보관한다. 작은 쓰기를 모아 보내면 요청 횟수를 줄이고, 읽은 내용을 캐시에 남기면 같은 데이터를 다시 가져오는 비용을 줄인다. 대신 버퍼가 RAM을 차지하며, 모아서 보내기를 기다리는 동안 개별 데이터의 전달은 늦어질 수 있다. 처리량의 개선과 한 요청의 지연 시간 단축은 항상 같은 방향이 아니다.
프로그램이 작은 입출력 요청을 하나씩 보내고 완료를 기다리면 그 스레드의 다음 작업이 지연된다. 기다리는 동안 다른 스레드는 CPU를 사용할 수 있다. 버퍼는 속도가 다른 두 쪽 사이에서 데이터를 잠시 보관한다. 작은 쓰기를 모아 보내면 요청 횟수를 줄이고, 읽은 내용을 캐시에 남기면 같은 데이터를 다시 가져오는 비용을 줄인다. 대신 버퍼가 RAM을 차지하며, 모아서 보내기를 기다리는 동안 개별 데이터의 전달은 늦어질 수 있다. 처리량의 개선과 한 요청의 지연 시간 단축은 항상 같은 방향이 아니다.


비동기 입출력은 요청과 완료 확인을 분리해, 장치가 일하는 동안 프로그램이 다른 일을 하게 한다. 버퍼링과 함께 사용할 수 있지만 서로 같은 개념은 아니다. 비동기로 실행한 작업도 완료 결과를 확인해야 하며, 완료 전까지 사용 중인 버퍼의 수명을 보장해야 한다. 동시에 너무 많은 요청을 쌓으면 대기열과 메모리 사용량이 늘어난다. 읽기 캐시 역시 원본이 다른 경로로 바뀔 수 있다면 갱신을 어떻게 알아낼지 정해야 한다. 재사용의 이득에는 최신성 관리 비용이 따른다. <ref name="source-4">[https://docs.kernel.org/admin-guide/mm/concepts.html 커널 문서: 페이지 캐시].</ref><ref name="source-5">[https://man7.org/linux/man-pages/man7/aio.7.html Linux man-pages: 비동기 입출력].</ref>
비동기 입출력은 요청과 완료 확인을 분리해, 장치가 일하는 동안 프로그램이 다른 일을 하게 한다. 버퍼링과 함께 사용할 수 있지만 서로 같은 개념은 아니다. 비동기로 실행한 작업도 완료 결과를 확인해야 하며, 완료 전까지 사용 중인 버퍼의 수명을 보장해야 한다. 동시에 너무 많은 요청을 쌓으면 대기열과 메모리 사용량이 늘어난다. 읽기 캐시 역시 원본이 다른 경로로 바뀔 수 있다면 갱신을 어떻게 알아낼지 정해야 한다. 재사용의 이득에는 최신성 관리 비용이 따른다. <ref name="source-4">[https://docs.kernel.org/admin-guide/mm/concepts.html 커널 문서: 페이지 캐시].</ref><ref name="source-5">[https://man7.org/linux/man-pages/man7/aio.7.html Linux man-pages: 비동기 입출력].</ref>
29번째 줄: 48번째 줄:


== 관련 각론 ==
== 관련 각론 ==
* [[운영체제 기본/Linux의 파일시스템|Linux의 파일시스템]]: 파일시스템 구현 문서의 목차.
* [[운영체제 기본/Linux의 파일시스템/VFS와 파일 객체|VFS와 파일 객체]]: 디렉터리 탐색, inode, 열린 파일 상태를 Linux 객체와 연산으로 연결.
* [[운영체제 기본/Linux의 파일시스템/ext4의 파일 읽기|ext4의 파일 읽기]]: extent 매핑과 장치 읽기로 파일의 데이터를 공급하는 과정.
* [[운영체제 기본/Linux의 파일시스템/페이지 캐시|페이지 캐시]]: 파일 내용의 재사용과 데이터 준비 상태 관리.
* [[운영체제 기본/Linux의 파일 메모리 매핑|mmap을 통한 파일 읽기]]: 주소 영역 등록, 첫 메모리 접근, 파일 폴트와 PTE 설치.
* [[운영체제 기본/Linux의 파일 읽기]]: 파일 읽기 인터페이스가 Linux 내부 구현으로 이어지는 과정.
* [[운영체제 기본/Linux의 파일 읽기]]: 파일 읽기 인터페이스가 Linux 내부 구현으로 이어지는 과정.
== 참고 문헌 ==
== 참고 문헌 ==
<references />
<references />

2026년 10월 31일 (토) 09:26 기준 최신판

목차 · 이전: 가상 메모리

메모리의 데이터는 프로세스가 종료되거나 전원이 꺼진 뒤에도 필요한 경우가 많다. 파일 시스템은 데이터를 지속적으로 보관하고 다시 찾을 수 있게 하며, 입출력 계층은 속도와 동작 방식이 다른 장치 사이에서 데이터를 전달한다. 이때 프로그램이 사용하는 이름, 현재 접근 중인 대상, 실제 저장장치의 상태를 하나로 취급하면 동시 접근과 장애를 다루기 어렵다.

저장 위치 대신 이름과 바이트를 제공하기

프로그램이 저장장치의 어느 구역에 데이터가 있는지 직접 관리한다면, 파일이 커지거나 다른 프로그램이 공간을 사용했을 때 배치를 함께 조정해야 한다. 파일 시스템은 저장 공간의 할당과 회수를 맡고, 프로그램에는 이름으로 찾을 수 있는 파일을 제공한다. 일반 파일의 데이터는 본문의 바이트이고, 메타데이터는 크기, 종류, 소유자와 접근 권한, 접근·수정 시각처럼 파일을 관리하는 정보다. 내용을 저장장치의 어디에서 찾을지 나타내는 블록 매핑도 메타데이터에 속한다. Unix 계열에서는 inode가 이러한 파일별 정보를 담거나 관련 구조를 가리킨다. 이름과 달리 inode 번호는 해당 파일시스템 안에서 파일을 식별한다.[1][2]

일반 파일을 바이트의 나열로 제공하면 프로그램은 저장장치의 세부 구조에 맞춰 매번 코드를 바꾸지 않아도 된다. Unix는 파일뿐 아니라 파이프와 터미널에도 읽기와 쓰기라는 공통 인터페이스를 제공한다. 덕분에 출력 대상을 바꾸어 프로그램을 조합하기 쉽다. 다만 인터페이스가 비슷해도 모든 대상의 성질이 같지는 않다. 일반 파일은 위치를 바꾸어 읽을 수 있지만 파이프는 그렇게 할 수 없으며, 바이트 스트림의 데이터에 붙일 레코드 구조는 프로그램이 정해야 한다. [3]

디렉터리와 경로로 파일을 찾기

디렉터리는 자식 파일의 본문을 모두 품은 저장 상자가 아니라, 이름을 파일 식별자에 연결하는 항목들의 모음이다. Unix 계열의 ext4에서는 각 항목이 이름과 inode 번호를 연결한다. 디렉터리 자체도 inode와 내용을 갖지만, 그 내용은 자식 파일의 본문이 아니라 이런 연결 정보다.[4]

/home/ldmsys/a.txt는 하나의 파일 식별자가 아니라 탐색할 이름의 순서다. 프로세스의 루트 디렉터리에서 home을 찾고, 그 안에서 ldmsys, 다시 그 안에서 a.txt를 찾아 대상 inode에 도달한다. 중간 디렉터리에 탐색 권한이 없으면 마지막 파일의 읽기 권한만으로 접근할 수 없다.[5] 같은 파일시스템에서 여러 디렉터리 항목이 같은 inode를 가리키는 관계가 하드 링크다. 이름이 늘어도 본문이 복제되는 것은 아니다.[1]

이름, 객체, 열린 상태의 수명을 나누기

파일을 열 때 운영체제는 경로로 찾은 객체를 계속 참조할 수 있는 핸들을 돌려준다. 이후 입출력마다 처음 경로를 다시 검색할 필요는 없다. Unix의 파일 디스크립터가 그 예다. 핸들이 가리키는 열린 상태에는 읽고 쓰는 현재 위치 등이 들어 있다. 같은 파일을 따로 열면 위치를 독립적으로 유지할 수 있고, 열린 상태를 공유하는 핸들은 위치도 함께 사용한다. 파일 내용의 공유와 읽기 위치의 공유는 다른 선택이다. [6]

이 분리는 이름이 바뀌어도 진행 중인 작업을 유지하게 한다. 이름 하나를 삭제하는 것은 해당 디렉터리 항목을 제거하는 일이며, 다른 하드 링크가 남아 있으면 그 이름으로 파일에 접근할 수 있다. Unix에서 로그 파일의 마지막 이름을 삭제해도 프로세스가 파일을 열어 두었다면 계속 쓸 수 있다. 같은 이름으로 새 파일을 만들어도 기존 핸들이 새 파일로 옮겨 가지는 않는다. 이름이 사라진 시점과 저장 공간을 회수할 수 있는 시점이 달라지는 대신, 경로의 변경 때문에 이미 연 대상이 갑자기 바뀌지 않는다. [7]

파일 안의 위치와 저장장치의 위치를 나누기

바이트 오프셋은 파일 시작에서 몇 바이트 떨어졌는지를 나타낸다. 블록 기반 파일시스템은 이를 파일 내부의 논리 블록 번호와 블록 안의 위치로 나누고, 블록 매핑으로 장치상의 물리 블록을 찾는다. 예를 들어 블록이 4 KiB라면 오프셋 5000은 논리 블록 1의 블록 내 오프셋 904다. 논리 블록 1이 물리 블록 900에 대응할 수 있으므로 두 번호를 같다고 볼 수 없다. 여기서 물리 블록은 장치에 요청하는 주소이지 SSD 내부의 실제 셀 위치를 뜻하지 않는다.

/home/ldmsys/a.txt
  -> / 의 home 항목 -> home의 ldmsys 항목
  -> ldmsys의 a.txt 항목
  -> 파일 inode: 크기, 권한, 블록 매핑 등
  -> 읽을 오프셋 5000 -> 논리 블록 1 + 블록 내 위치 904
  -> 블록 매핑 -> 물리 블록 900의 해당 데이터

extent는 연속된 논리 블록 구간을 연속된 물리 블록 구간에 대응시키는 표현이다. 시작 위치와 길이로 여러 블록을 묶어 기록한다. 연속 구간이 길면 블록별 주소를 나열하는 것보다 매핑 정보를 줄일 수 있지만, 파일이 흩어질수록 필요한 구간 수가 늘어난다.[2] 반대로 희소 파일의 hole은 파일 크기 안에 있지만 데이터 블록을 할당하지 않은 구간이며, 읽으면 값이 0인 바이트들로 채워진다. 따라서 파일의 크기와 실제 할당된 저장 공간은 다를 수 있다.[8] 위 흐름은 이름에서 데이터 위치로 이어지는 관계다. 필요한 내용이 캐시에 있다면 실제 장치 읽기는 생략할 수 있다.

버퍼링과 비동기로 기다리는 비용 줄이기

프로그램이 작은 입출력 요청을 하나씩 보내고 완료를 기다리면 그 스레드의 다음 작업이 지연된다. 기다리는 동안 다른 스레드는 CPU를 사용할 수 있다. 버퍼는 속도가 다른 두 쪽 사이에서 데이터를 잠시 보관한다. 작은 쓰기를 모아 보내면 요청 횟수를 줄이고, 읽은 내용을 캐시에 남기면 같은 데이터를 다시 가져오는 비용을 줄인다. 대신 버퍼가 RAM을 차지하며, 모아서 보내기를 기다리는 동안 개별 데이터의 전달은 늦어질 수 있다. 처리량의 개선과 한 요청의 지연 시간 단축은 항상 같은 방향이 아니다.

비동기 입출력은 요청과 완료 확인을 분리해, 장치가 일하는 동안 프로그램이 다른 일을 하게 한다. 버퍼링과 함께 사용할 수 있지만 서로 같은 개념은 아니다. 비동기로 실행한 작업도 완료 결과를 확인해야 하며, 완료 전까지 사용 중인 버퍼의 수명을 보장해야 한다. 동시에 너무 많은 요청을 쌓으면 대기열과 메모리 사용량이 늘어난다. 읽기 캐시 역시 원본이 다른 경로로 바뀔 수 있다면 갱신을 어떻게 알아낼지 정해야 한다. 재사용의 이득에는 최신성 관리 비용이 따른다. [9][10]

요청한 양과 완료된 양을 구분하기

입출력은 요청 전체의 성공과 실패 두 상태만으로 표현하기 어렵다. 읽기는 파일 끝에 도달하거나 현재 도착한 데이터가 적으면 요청보다 적은 바이트를 돌려줄 수 있다. 쓰기도 공간 부족이나 실행 중단 등의 이유로 일부만 끝날 수 있다. 따라서 반환된 양은 실제로 어디까지 진행했는지를 나타내는 정보다. 남은 부분을 재시도하려면 이미 처리한 위치를 보존해야 한다. 처음부터 다시 보내면 중복된 데이터가 생길 수 있다.

양수를 반환한 부분 완료, 입력의 끝, 오류는 각각 다르게 처리해야 한다. 특히 바이트 스트림에서는 송신 측이 한 번에 쓴 범위와 수신 측이 한 번에 읽는 범위가 일치하지 않는다. 메시지의 길이나 구분자는 상위 프로토콜이 정해야 한다. 또한 버퍼에 쓰기를 받아들인 뒤 실제 저장 과정에서 오류가 드러날 수 있으므로, 최초 쓰기의 성공만으로 이후 실패 가능성까지 사라지지는 않는다. [11]

보이는 변경과 남는 변경을 구분하기

버퍼드 쓰기의 성공은 데이터가 운영체제의 캐시에 전달되었다는 뜻일 수 있다. 다른 읽기에서 변경을 관찰하는 것과 전원 장애 후에도 변경이 남는 것은 별개다. 내구성이 필요하면 데이터를 저장장치까지 반영하는 동기화 절차와 그 실패를 처리해야 한다. 매번 동기화하면 대기 비용이 커지고, 여러 변경을 묶으면 비용을 나누는 대신 마지막 묶음이 보장되기까지의 시간이 길어진다.

이름 변경의 원자성도 내구성과 구별한다. 같은 파일 시스템에서 완성한 새 파일로 기존 이름을 원자적으로 교체하면, 새로 여는 쪽이 교체 도중의 빈 이름을 보는 문제를 피할 수 있다. 이미 열린 참조는 이전 객체를 유지한다. 그러나 이것만으로 여러 파일의 변경이 하나의 트랜잭션이 되거나 장애 후 복구 결과가 보장되지는 않는다. 파일 내용과 그 이름을 담은 디렉터리는 각각 영속화가 필요한 상태다. Linux에서 파일에 대한 동기화만으로 디렉터리 항목까지 저장되었다고 볼 수 없는 이유도 여기에 있다. [12][13]

관련 각론

참고 문헌