본문 바로가기

Job Notes/File System

파일 시스템의 원리와 리눅스에서의 제어구조 3

파일 시스템의 원리와 리눅스에서의 제어구조 3

(* 마이크로소프트웨어 2002년 11월호에 수록된 기사입니다.)

유닉스의 파일 시스템

가상파일시스템
  리눅스도 유닉스처럼 VFS를 이용해 매우 다양한 파일 시스템을 지원한다. 예를 들면, 기존 유닉스 파일 시스템인 UFS, 리눅스 기본 파일 시스템인 ext2와 ext3, 썬에서 개발한 네트워크 파일 시스템인 NFS, 작은 쓰기(small write)에 좋은 성능을 제공하는 LFS, CMU 대학에서 개발했으며 disconnected 연산 기능을 제공하는 CODA, CD를 위한 iso9660, MS의 도스 파일 시스템, 윈도우 NT 파일 시스템, 프로세스 파일 시스템(proc) 등이 있다. 리
눅스에서 각 파일 시스템의 위치와 커널 내의 다른 부분과의 상호 관계를 도시화하면 <그림 11>과 같다.

사용자 삽입 이미지

리눅스에서 지원하는 파일 시스템



파일 시스템의 제어 흐름
  이제부터는 VFS가 어떻게 여러 파일 시스템을 지원하고, 스위치 기능을 수행하고, 리눅스에서 파일 접근 요청이 어떻게 이뤄지는지 open과 read의 제어 흐름을 통해 살펴보자.

파일 열기
<그림 12>는 파일 열기에 대한 전 과정을 대략적으로 보여주는 것이다.

사용자 삽입 이미지

파일 열기


  프로세스의 task structure는 fs_sturuct와 files_struct에 대한 포인터를 가지고 있다. 이중 files_struct의 포인터를 따라가면 그 프로세스에 의해 열려 있는 모든 파일의 디스크립터가 저장된 struct file의 테이블을 참조할 수 있다. 이 file structure에는 여러 가지 파일에 관련된 정보가 저장되어 있는데, 대표적으로 이 파일을 담고 있는 파일 시스템 정보를 참조하는 vfsmount 포인터와 그 파일 시스템이 호출하는 file operation structure의 포인터를
갖고 있다. 다음으로 fs_struct 포인터가 가리키고 있는 테이블을 참조하면 커널에 등록된 파일 시스템의 여러 정보를 얻을 수 있다. 이러한 정보는 대략적으로 다음과 같은데 파일 시스템이 담고 있는 블럭 장치의 장치 번호, 이 파일 시스템이 마운트된 디렉토리, 이 파일 시스템이 마운트될 때 할당된 VFS 슈퍼 블럭에 대한 포인터 등이다. <그림 13>은 파일을 열기 위한 시스템 호출(system call)의 제어 흐름 중 일부분을 나타낸다.

사용자 삽입 이미지

파일을 열 때 제어 호출


  우리가 open()이라는 함수를 이용해 파일을 열고자 한다면 open() 함수는 우선 system_call_table에 등록된 sys_open()이라는 함수를 호출하게 된다. 이 함수는 열고자 하는 파일의 fd를 현재 사용되고 있지 않은 fd 중에서 얻는다. 이렇게 얻은 fd는 fd 변수에 저장되고, 이어서 filp_open() 함수를 호출해 새로운 파일 구조(file structure)를 만든 뒤 그 파일 구조를 fd 테이블에 추가한다.
  filp_open() 함수에 의해 파일 구조를 만들 때 단순히 파일 구조를 초기화하는 것만이 아니다. 파일 시스템은 각각의 특성에 맞는 파일 오퍼레이션(file operation)이 필요한데, 바로 이 함수가 자신이 열고자 하는 파일 시스템의 파일 오퍼레이션을 알맞게 정의해준다. 그리고 filp_open() 함수가 호출하는 open_namei는 열려는 파일의 아이노드를 알 수 있도록 정보를 제공하고, 현재 파일 포지션을 0으로 셋팅하고, f_inode->i_fop에 그 파일 시스템에 알맞은 오퍼레이션 포인터를 채워 넣는다. <그림 14>는 각각의 파일 시스템에 따라 file_operation이 어떻게 정의되어 있는지를 보여준다.

사용자 삽입 이미지

file_operation의 자료 구조


  <그림 14>를 보면 알겠지만 각각의 파일 시스템은 다른 파일 오퍼레이션 구조(file operation structure)를 가지고 있다. 이것은 각각의 파일 시스템마다 다른 특성을 가지고 있기 때문에 다르게 만들어진 것이다.
  이렇게 해서 파일을 열기 위한 일련의 작업이 모두 끝나면 파일 타입에 맞게 장치 open 함수가 호출된다. <그림 15>를 보면 마지막에 chrdev_open()이나 nfs_file_open() 등의 함수들이 보이는데, 이것이 바로 파일 타입에 따라 달라지는 장치 open 함수다. 이렇게 해서 열고자 하는 파일이 열리는 것이다.

사용자 삽입 이미지

파일울 열 때 제어 호출2


  즉, VFS는 <그림 15>처럼 실제 파일 시스템보다 한 단계 위에 존재하면서 응용에서 요청이 들어 왔을 때 그에 상응하는 파일 시스템의 파일 오퍼레이션을 호출하게 된다. 예를 들어 ext2 파일 시스템인 경우에는 open 시에는 generic_file_open()을 호출한다. 반면에 nfs 파일 시스템인 경우에는 open시에 nfs_open()을 호출한다. 이와 같은 방법으로 VFS는 스위치 기능을 수행하고, 이런 VFS의 기능 때문에 여러 파일 시스템을 지원할 수 있다.

파일 읽기
 파일 읽기 역시 파일 열기와 비슷한 과정을 거친다. 다만 파일을 읽기 전에 버퍼 캐시 공간을 검색해 실제 물리적 장치의 블럭을 읽기 전에 데이터가 있는지 확인하고, 없으면 장치 드라이버에 데이터 읽기를 요청하게 된다. 버퍼 캐시를 사용하는 이유는 파일을 좀더 효율적으로 관리하기 위함인데 파일을 참조할 때마다 실제 물리적 장치를 매번 검색하는 것이 아니라 자주 참조하는 데이터는 버퍼 캐시에 저장해 놓음으로써 빠른 검색을 가능하게 해주는 것이다. <그림 16>은 파일 읽기의 시스템 호출 제어 흐름의 일부분을 보여준다.

사용자 삽입 이미지

파일 읽을 때의 제어 흐름


  사용자 프로그램에 read()를 호출해 파일을 읽으려고 할 때 read()는 sys_read()라는 시스템 호출을 system_call_table에서 찾아 호출하게 된다. sys_read() 함수에서는 우선 fget()을 호출해 읽고자 하는 파일의 file structure 정보를 가져온다. 가져온 정보에서 읽기 모드를 검사해 읽기 허가권이 있다면 읽고자 하는 영역에 잠금을 건 뒤 그 파일 시스템에 알맞은 file->f_op->read를 호출한다.
  <그림 16>에서는 특별한 파일의 읽기 루틴을 호출하지 않고, 직접적으로 페이지 캐시(page cache)를 사용할 수 있는 파일 시스템이면 모두 호출할 수 있는 generic_file_read()를 호출했다. 파일의 타입이 특별하기 때문에 특별한 파일 읽기 루틴을 사용해야 한다면 그 파일 타입에 알맞은 file_read() 루틴을 호출하면 된다.
  generic_file_read()는 읽기 원하는 데이터가 페이지 캐시 안에 있으면(이것을 보통 페이지 캐시 히트라 한다) 그 곳에서 직접 데이터를 읽어 오고, 만일 페이지 캐시에 데이터가 없으면 페이지 캐시 내에 빈 공간을 확보한 후 페이지를 할당받는다. 페이지를 할당받으면 페이지 캐시에 페이지를 추가하고, mapping->a_ops->readpage()를 호출해 실제로 데이터를 읽기 시작한다(mapping은 struct address_space 형태를 가지며 inode->i_mapping으로 접근할 수 있다).
  파일 읽기를 예를 들어 설명하겠다. 사용자 수준 응용이 특정 파일의 내용을 읽기 위해 read()를 호출했다고 가정하면, read() 시스템 호출에 의해 커널로 진입하고 파일 시스템에 읽기 서비스를 요청한다. 파일 시스템은 디렉토리를 탐색해 요청한 파일을 찾고 아이노드를 이용해 요청한 데이터의 위치를 찾는다. 그리고 버퍼 캐시 공간에 요청한 데이터가 이미 존재하는지 검사해 있으면 그 데이터를 응용에 전달하고, 없으면 디스크 드라이버에 해당 디스크 블럭을 읽어 달라고 요청한다. 디스크 장치 드라이버는 요청된 논리적 디스크 블럭이 몇 번째 실린더, 몇 번째 트랙, 어느 섹터인지 계산해 해당 섹터를 읽어오게 한다. 디스크 장치 드라이버는 읽혀진 데이터를 파일 시스템에 전달하고, 파일 시스템은 사용자가 요청한 데이터를 사용자 수준 응용에 전달하게 된다.


다음 회에는 실제 파일 시스템 구조, 구현에 대해
지금까지 전반적인 파일 시스템의 개념 및 유닉스의 파일 시스템과 리눅스에서 제어 흐름에 대해 살펴봤다. 많은 분량의 내용을 요약해서 쓰려다 보니 많은 부분을 설명하지 못한 것 같아 아쉬움이 남는다. 관심 있는 독자라면 책을 통해 더 많은 정보를 얻길 바란다. 다음 회에서는 실제 파일 시스템의 구조 및 새로운 파일 시스템을 작성하는 방법을 다룰 것이다.


* 참고 자료
� Operating System Concept(Silberschatz 지음, Addison Wesley)
� Understanding the Linux Kernel(Daniel Pierre Bovet∙Marco Cesati 지음, O’REILLY)
� 리눅스 매니아를 위한 커널 프로그래밍(조유근∙최종무∙홍지만 지음, 교학사)
� Unix Internals(U.Vahalia 지음, Prentice Hall)