1. File and File System
(1) File
파일이라고 하면, 흔히 하드디스크에다 저장하는 단위를 말합니다. 메모리 시스템에 대해 메모리는 주소를 통해 접근했다면, 파일은 이름을 통해 접근하는 단위입니다.
그래서 파일의 정의를 보면, A named collection of related information으로 쉽게 말해 관련된 정보를 이름을 가지고 저장하는 것을 말합니다.
(운영체제에서는 특히 리눅스에서는 장치들도 관리하기 위해서 파일이라는 이름을 사용해 관리하는데 일반적으로 생각하는 데이터를 저장하는 목적이 아닌, 다양한 저장장치들을 운영체제는 다른 파일로 관리합니다. 이런 것은 Device Special File이라고 부르는데 일반적으로 생각하는 파일과는 다른 개념입니다.)
File 연산
파일과 관련된 연산에는 create(생성), read(읽기), write(쓰기), reposition(lseek, 위치 이동), delete(삭제), open(열기), close(닫기) 등이 있습니다.
읽기, 쓰기, 생성, 삭제 등은 쉽게 역할이 파악되지만, 생소한 reposition이라는 연산이 있는데 이것은 포인터와 관련된 연산입니다. 파일의 크기는 크기 때문에 읽거나 쓸 때 파일의 어느 위치를 가리키는 포인터가 존재합니다. 사용자가 필요에 의해 현재 포인터가 가리키는 위치가 아닌 다른 부분을 읽거나 쓰고 싶은 경우에, 현재 접근하는 위치를 수정하는 연산이 reposition 연산입니다.
그리고 파일의 open과 close 연산이 있습니다. 파일을 읽고 쓰려면 일단 open 해야 하고 작업이 완료되면 close를 해야 합니다.
open 연산은 디스크에서 메모리로 파일을 직접 올리는 것이 아닌 파일의 메타 데이터를 메모리에 올려놓습니다. close 연산은 사용이 끝난 파일과 관련된 자원을 해제하는 역할을 합니다. 즉, open과 close는 파일을 조작하기 위한 전처리와 후처리 작업을 담당합니다.
여기서 메타 데이터(파일 속성, File Attribute)란 파일 자체의 내용이 아닌 파일을 관리하기 위한 각종 정보들을 말합니다. 파일 이름과 유형, 저장된 위치, 파일 사이즈 등이 속합니다. 메타 데이터를 통해 파일 시스템에서 파일을 식별하고 관리할 수 있습니다.
(2) File system
파일 시스템은 운영체제에서 파일을 관리하는 소프트웨어 부분으로, 파일 자체의 데이터뿐 아니라 파일의 메타 데이터도 관리합니다. 이름을 입력받아 해당 데이터를 반환해 주는 소프트웨어가 파일 시스템입니다.
그리고 파일 시스템은 디렉터리 정보를 관리하여 파일의 계층 구조를 구성합니다. 디렉터리는 파일의 그룹화를 통해 파일을 쉽게 찾고 조직할 수 있도록 합니다.
2. Directory and Logical Disk
(1) Directory
디렉터리도 결국에는 하나의 파일입니다. 그 파일의 내용은 해당 디렉터리 밑에 어떤 파일이 존재하는지와 그 디렉터리 밑에 있는 파일의 메타데이터를 포함합니다. 그래서 파일 시스템은 파일과 디렉터리를 동일한 방식으로 다룰 수 있습니다. 그래서 파일을 검색하거나 생성, 삭제하는 것과 같은 파일 관리 작업을 디렉터리에서 수행할 수 있습니다. (search for a file, create a file, delete a file list a directory, rename a file, traverse the file system)
또한 디렉터리 목록을 표시하거나 파일 이름을 변경하는 등의 디렉터리 관리 작업을 수행할 수 있습니다.
(2) Logical Disk
이러한 파일 시스템은 결국에는 하드 디스크에 저장됩니다. 하드 디스크는 논리적 디스크(=파티션, Partition)와 물리적 디스크로 구분됩니다. 하나의 물리적 디스크 안에 여러 파티션을 두는 것이 일반적입니다.
운영체제는 논리적 디스크를 인식하고 각각을 관리합니다. 예를 들어, 하나의 하드 디스크를 C 드라이브와 D 드라이브로 나누면 각각의 드라이브가 논리적 디스크가 됩니다. 이러한 파티션에 파일 시스템을 설치하거나 Swap 영역으로 사용할 수 있습니다.
3. open()
파일의 메타데이터를 메모리로 가져오기 위해서는 해당 파일의 디렉터리 경로를 검색해야 합니다. 일반적으로 파일 시스템은 경로의 시작점인 루트 디렉터리부터 출발하여 해당 파일의 위치를 찾습니다.
예를 들어 /a/b/c라는 메타 데이터가 메모리로 올라온다고 하면,
- "/" (루트 디렉터리)를 open 하여 디렉터리 정보를 읽습니다.
- "a" 디렉터리 내에서 파일 "b"의 위치를 찾기 위해 "a" 디렉터리를 open 하고 디렉터리 정보를 읽습니다.
- "b" 디렉터리 내에서 파일 "c"의 위치를 찾기 위해 "b" 디렉터리를 open하고 디렉터리 정보를 읽습니다.
- "c" 파일을 open 하여 해당 파일의 메타데이터를 메모리로 가져옵니다.
이러한 방식으로 디렉터리 경로를 따라가며 파일의 위치를 찾기 때문에 디렉터리 경로가 깊을수록 메타 데이터를 가져오는 데 걸리는 시간이 더 많이 소요될 수 있습니다.
하지만 한 번 파일을 open 한 후에는 해당 파일의 메타데이터가 이미 메모리에 적재되어 있기 때문에 추가적인 디렉터리 경로 검색을 하지 않습니다. 따라서 한 번 open 한 파일은 read/write 작업에 별다른 디렉터리 경로 검색을 하지 않습니다.
이렇게 open을 read/write와 별도로 두는 이유는 디렉터리 경로 검색으로 인한 시간 소요를 줄여 파일 접근 속도를 빠르게 하기 위함입니다.
이것을 그림을 통해 살펴보겠습니다.
왼쪽은 물리적 메모리이고 오른쪽은 논리적 디스크입니다. 운영체제 안에는 각 프로세스 별로 관리하기 위한 PCB가 있고, 전체 프로그램에서 open 한 파일들이 어떤 것인지 관리하는 Open file table이 전역적으로 유지되고 있습니다.
사용자 프로그램이 System call을 통해 /a/b라는 파일을 open 하려고 하면, CPU 제어권이 커널로 넘어갑니다. open을 하면 먼저 root directory의 메타 데이터를 메모리에 올립니다. root directory를 먼저 오픈하고 메타데이터를 열어보면 root content가 있습니다. 그리고 그 안에는 a라는 파일의 메타데이터가 있습니다. 그럼 이 a 파일의 메타데이터를 메모리에 올립니다. 그리고 이 메타데이터 안에는 b 파일의 메타데이터가 있습니다. 이것을 또 메모리에 올려놓습니다.
결론적으로 open은 그 파일의 메타 데이터를 메모리에 올려놓는 작업입니다.
4. Open File Table & File Descriptor(file handle, file control block)
시스템 콜을 통해 open 했기 때문에 결괏값을 반환하게 됩니다. 어떤 결과 값을 리턴하는지에 대해 살펴보겠습니다.
각 프로세스마다 그 프로세스가 오픈한 파일들에 대한 메타데이터 포인터를 갖고 있는 일종의 배열이 정의되어 있습니다. 현재 오픈한 b의 메타데이터의 위치를 가리키는 포인터가 배열 어딘가에 만들어지면, 그 배열에서 몇 번째 인덱스에 그 포인터가 위치하는지를 사용자에게 반환해 주게 됩니다.
b라는 파일에 대해 read/write를 하면 디스크의 어디에 위치하는지 root directory부터 찾을 필요가 없습니다. b의 메타데이터는 이미 메모리에 올라와있고, 그것에 대한 위치는 File Descriptor(fd)가 가지고 있습니다. 그래서 사용자 프로세스는 File Descriptor 인덱스 (정수값)을 통해 read/write 요청을 할 수 있습니다.
b의 내용을 읽어서 사용자 프로그램에게 직접 주는 것이 아니라 운영체제가 자신의 메모리 공간 일부에 먼저 그 내용을 저장해 놓습니다. 그리고 사용자 프로그램에게는 그 내용을 복사해서 전달해 줍니다. 다른 프로그램이 동일한 파일의 동일한 위치를 요청하면, 운영체제는 이미 한 번 읽어 놓는 것을 전달합니다. 이 방법을 Buffer Caching이라고 합니다.
파일 시스템의 Buffer Caching은 요청한 내용이 Buffer Cache안에 있든 없든 간에 CPU 제어권이 운영체제로 넘어가게 됩니다. 그래서 Buffer Caching 환경에서는 운영 체제가 모든 정보를 다 알기 때문에 LRU나 LFU 알고리즘을 사용할 수 있습니다. Paging 시스템에서 LRU 알고리즘을 사용하지 못하고 clock 알고리즘을 썼던 것과는 대조되는 부분입니다.
커널에 유지하는 File Descriptor 테이블은 프로세스마다 갖고 있어서 Per-process File Descriptor Table이라고 부르고, Open File Table은 시스템 전체에 하나 존재하기 때문에 System-Wide Open File Table이라고 부릅니다.
5. File Protection
이전 포스팅에서 살펴본 메모리에 대한 Protection은 접근 권한에 대한 Protection입니다. 왜냐하면 메모리는 프로세스마다 별도로 가지고 있기 때문에 자기 자신만 볼 수 있기 때문입니다.
반면 File에 대한 Protection은 접근 권한이 누구한테 있는지와 어떤 연산이 가능한지에 대한 정보를 가지고 있어야 합니다.
크게 세 가지의 방법이 있습니다.
(1) Access control Matrix
사용자와 파일을 행과 열에 나열해 두고 표시를 하는 것입니다. 특정 사용자가 특정 파일에 접근했을 때 권한이 있을 때에만 접근을 허용합니다. 현실적으로는 파일이 매우 많아서 행렬의 칸을 다 만들면 메모리 낭비가 큽니다. 그래서 이런 방법보다는 Linked List로 구현을 하고 주체를 누구로 할 것인가에 따라 두 가지로 나눌 수 있습니다.
- Access Control List
: 파일을 주체로, 그 파일에 대한 접근 권한이 있는 사용자들을 묶는 방법입니다. 접근 권한이 없는 사용자에 대해서는 List에 연결하지 않습니다. - Capability
: 사용자를 중심으로 접근 권한이 있는 파일들을 Link 해주는 방식입니다.
(2) Grouping
일반적인 운영체제에서 사용하는 방법입니다. 각각의 파일에 대해 사용자 그룹을 세 가지(owner, group, public)로 나눕니다. 그리고 각 파일에 대해 세 그룹의 접근 권한(rwx)을 3비트씩 표시합니다.
위는 유닉스 계열에서의 예시입니다. 파일 하나에 대해 접근 권한을 나타내기 위해서 총 9개의 Bit를 필요로 합니다.
(3) Password
파일마다 password를 두는 것입니다. 디렉토리 파일에 두는 것도 가능합니다. 그래서 접근 권한 별로 password를 따로 두어야 하기 때문에 암기나 관리의 문제가 생깁니다.
6. Mounting
하나의 물리적인 디스크를 파티션을 통해 여러 개의 논리적 디스크로 나눌 수 있습니다. 각각의 논리적 디스크에서 파일 시스템을 설치해 사용할 수 있습니다. 만약 다른 파티션에 설치되어 있는 파일 시스템에 접근하려고 하면 Mounting 과정을 거쳐야 합니다.
Root File System의 특정 디렉토리 이름에 또 다른 파티션의 파일 시스템을 Mount 해줍니다. 그러면 그 Mount 된 디렉토리에 접근한다면 또 다른 파일 시스템의 Root 디렉토리에 접근하는 것이 됩니다.
7. Access Methods
시스템이 제공하는 파일 정보의 접근 방식은 두 가지가 있습니다.
1. 순차 접근 (Sequential Access)
순차 접근은 파일을 처음부터 끝까지 순서대로 읽거나 쓰는 방식입니다. 카세트테이프를 듣거나 녹음할 때의 방식과 비슷합니다. 읽거나 쓸 때마다 오프셋(offset)이 자동으로 증가합니다.
2. 직접 접근 (Direct Access 또는 Random Access)
직접 접근은 LP 레코드판과 같이 파일을 임의의 순서로 접근할 수 있는 방식입니다. 이 방식을 통해 파일 내의 특정 위치로 직접 이동하여 읽거나 쓸 수 있습니다. 파일을 구성하는 레코드(record)를 임의의 순서로 접근할 수 있어서, 원하는 레코드를 읽고 쓸 수 있습니다. 하지만 직접 접근은 순차 접근보다 조금 더 복잡하게 구현될 수 있습니다.
참고자료
[KOCW 이화여대 반효경 교수님 - File Systems]
https://core.ewha.ac.kr/publicview/C0101020140516150939191200?vmode=f
[ Operating System Concepts - Abraham Silberschatz ]
https://www.yes24.com/Product/Goods/89496122