본문 바로가기

기술/Linux

리눅스 커널 심층 분석 개정 3판 : 13장. 가상 파일시스템 Linux Kernel Development Third Edition : Chapter 13. The virtual filesystem

리눅스 커널 심층 분석 개정 3판 : 13장. 가상 파일시스템 

Linux Kernel Development Third Edition : Chapter 13. The virtual filesystem

 

리눅스 커널 심층분석

이 책은 리눅스 커널의 핵심을 간결하면서도 심도있게 다루고 있다. 일반적인 운영체제에 대한 이해를 넘어, 여타 유닉스 시스템과 다른 리눅스만의 특징적인 부분에 대한 설계, 구현, 인터페이

book.naver.com

Notion에서 보기 


virtual filesystem = virtual switch = VFS

  • 유저 스페이스 프로그램에 제공되는 파일시스템 인터페이스
  • 커널의 서브시스템
  • VFS는 파일시스템이 공존하고 상호운용될 수 있도록 지원한다.
  • 프로그램이 다른 파일 시스템, 다른 미디어에 read/write하기 위해서 unix system call을 사용할 수 있게 한다.

1. Common Filesystem Interface

  • VFS는 파일시스템, 물리적 매체와 상관없이 open, read, write 를 위한 system call을 할 수 있도록 한다.
  • 시스템 콜은 서로 다른 파일시스템. 미디어 사이에서 운용된다.
  • 파일 시스템간 파일 복사, 이동이 가능한 시스템 콜 사용할 수 있따.
  • 가상 인터페이스를 통한 파일 시스템으로의 추상적 접근이 가능하다.open(), read(), write()와 같은 파일 시스템 접근 시스템 콜이 어떤 파일 시스템, 물리적 매체에서도 가능하도록 함** 추상화: 복잡한 자료, 모듈, 시스템의 핵심적인 개념 및 기능만 간추려 내는 것
  • 오늘날 운영체제에서 가상 인터페이스를 통해 파일 시스템에 대한 추상적 접근으로 interoperation, generic access가 가능해졌다.
  • → 특별한 도구 없이도 서로 다른 파일 시스템간 파일 이동, 복사, 접근 가능
  • 리눅스에 새로운 파일시스템, 스토리지 매체 적용
  • → rewritten, recompile X
  • VFS, block I/O layer - abstraction, interface, glue 제공
  • → 유저 스페이스 프로그램이 uniform naming policy를 통해 any 파일 시스템에 있는 파일에 접근할 수 있는 일반적인 시스템콜을 발생시키는 것들을 제공한다.

2. Filesystem Abstraction Layer

  • 커널이 abstraction layer를 구현하여 어느 type의 파일 시스템에도 접근할 수 있는 low-level filesystem interface를 제공한다.
  • VFS가 파일시스템의 일반적인 특징과 행위를 대표할 수 있는 common file model을 제공하기 때문에 모든 종류의 파일시스템에 대한 generic한(일반적인) 인터페이스 실현이 가능하다.
  • 추상화 계층에서 모든 파일 시스템이 지원하는 기본적인 개념적 인터페이스와 자료 구조를 선언한다.
  • 파일시스템은 어떻게 해당 파일을 열것인지, 디렉토리를 어떻게 인지하는지와 같은 개념을 형성하고 이것은 vfs의 예상과 매치한다.
  • 파일시스템은 vfs가 예상하는 추상화된 인터페이스와 자료구조를 제공하도록 프로그래밍되는데 이것의 구체적인 구현 디테일이 숨겨져 있다. (추상화)→ 커널은 어떤 파일 시스템이든 작업 가능 / 파일 시스템의 디테일을 알 필요 없다.
  • → VFS와 커널에게 모든 파일 시스템은 같은 것으로 보인다.
  • 파일시스템 접근 연산 수행:→ 파일시스템의 구체적인 구현 디테일을 다루는 백엔드 제공
  • user spce에서 front end를 제공하는 vfs는 일반적인 파일시스템 접근을 위한 system call을 받음
  • user-space에서 system call을 호출하면 VFS에서 일반적인 시스템 콜을 특정 파일 시스템에 적절한 방식으로 바꾸어 파일 시스템에 접근하고, 파일 시스템이 물리적 매체에서 system call을 처리한다.

3. Unix Filesystem

  • 파일 시스템 관련 기본적인 추상화 제공

Filesystem

  • 특정 구조를 적용한 계층적 데이터 저장소
  • 파일, 디렉토리, 파일과 디렉토리 관련 제어 정보
  • operation: creation, deletion, mounting
  • 파일 시스템은 namespace = gobal hierarchy의 특정 mount point에 마운트된다.** mnt namespace : 시스템에 마운트된 장치에 대해 어떤 장치가 어떤 권한으로 마운트되었는지에 대한 정보를 구조체 형태로 저장한다. 리눅스는 시스템의 모든 마운트포인트를 자료 구조에 저장하고 파일시스템의 어떤 부분을 어디에 어떤 권한으로 마운트할지에 대한 정보도 포함시킨다.독립된 프로세스들이 각각 전체시스템의 마운트포인트 구조에 대한 view를 독립적으로 가질 수 있음** mount point : 마운트 포인트: 장치와 연결되는 디렉토리⇒ 마운트된 모든 파일시스템이 단일 트리에 엔트리로 표현된다.→ 커널은 / (= HDD 파티션) 으로 파일시스템을 마운팅
  •  
  • 마운트 포인트에 파일시스템을 마운트하여 파일 시스템을 하나의 트리 구조를 이루게 함.
  • → 다른 프로세스에 영향 미치지 않고 해당 마운트된 장치를 사용할 수 있음
  • ** mount : 물리적 장치를 특정 위치, 디렉터리에 연결시키는 것
  • → namespace가 있으니까 이 자료 구조를 통해서 어떤 파일 시스템의 마운트 포인트를 다른 것에 영향 미치지 않고 변경 가능
  • ** namespace : 시스템에서 사용하는 일부 자원들을 분할하고 격리하여 사용하는 기능으로 시스템의 다른 부분으로부터 완전히 격리시킴

files

  • ordered string of bytes
  • 첫번째 byte = 파일의 시작, 마지막 byte = 파일을 끝을 표시한다.
  • operations: read, write, create, delete
  • 디렉토리에 정렬된다

directory entries

  • directory: = folder
  • 주로 관련 파일을 담고 있다.
  • subdirectory
  • 디렉토리 내부에 디렉토리 포함 가능 → nested path 형성
  • path의 컴포넌트
  • unix에서 디렉토리는 일반 파일의 단순 리스트로 간주됨.
  • ⇒ VFS에게 디렉토리 = 파일 (같은 연산 수행 가능)

inodes

  • inode = index node:
  • unix는 파일 메타데이터를 별도의 자료 구조 inode에 저장
  • 파일의 정보를 저장하는 자료 구조
  • (파일과 별도로 존재한다.)
  • 파일의 데이터를 담고 있는 디스크 블록 포인터 포함
    • 파일 타입 정보, 소유자, 파일 사이즈, 수정 시간 등
  • 파일의 이름 정보는 없음

superblock

  • 파일 시스템의 전체적인 정보를 저장하는 자료 구조
  • (개별적 파일에 대한 정보 + 전체 파일 시스템에 대한 정보)
  • 파일시스템의 메타데이터 = 파일시스템의 제어정보 저장
  • unix 파일 시스템은 이러한 개념들을 physical disk layout에 구현한다파일 시스템에게 디렉토리 = 파일
  • → 파일 정보는 disk의 분리된 block에 inode로 저장된다.
  • 위의 개념들은 물리적 저장 매체에 physically mapped
  • 위의 개념들로 리눅스 VFS가 구현됨.files ~ disk의 directory
  • superblock ~
  • ⇒ inode ~ disk의 file control block = 파일 정보 저장

4. VFS objects

  • vfs는 객체지향user-space에서 open, write, read 등 파일 시스템 접근 함수 호출→ 시스템 콜 실행
  • → vfs에서 가상 파일 시스템 계층 접근하여 표준 저수준 함수 + 추상화 인터페이스 제공하여 파일시스템의 구현 디테일을 몰라도 접근 가능
  • → 특정 모듈 (파일시스템)의 상세한 기능을 숨기고 인터페이스로 선언된 특정 함수로만 다른 계층 (user-space)와 인터페이싱하는 것을 돕는 추상화 계층을 제공하기 때문이다.
  • objects = struct (object의 인스턴스, 관련 데이터, 연산 등)
  • 등록된 파일 시스템들은 개별적인 file_system_type으로 각 기능 등이 기술됨.
  • mount point = vfsmount struct로 표현됨. (mount의 위치, flag 정보 포함)** ( )_operation 은 ( ) 에 대해 동작하는 함수 포인터들을 가지고 있는 구조체
  • 각 object 내부에 커널이 해당 object를 대상으로 하는 함수가 들어 있는 ( )_ operation 을 가지고 있음.

superblock object

  • file system 하나가 superblock object로 구현됨
  • 마운트된 특정 파일 시스템 정보 저장
  • = 디스크의 섹터에 저장되는 filesystem control block은 물리적 버전 of superblock object
  • <linux/fs.h>에 구조체 정의

  • 파일 시스템이 마운트 되면 alloc_super()를 통해 superblock object (마운트된 파일시스템) 가 생성되고 초기화 (실시간 생성)
  • → 디스크로부터 파일 시스템에 해당하는 superblock을 읽고, 정보를 채움.
  • 파일 시스템의 사이즈, block의 수, empty block의 목록, inode list, inode관련 정보, mount 관련 정보 등
  • operations**저수준 파일 입출력 : open, close, read, write / 커널 시스템 호출을 통해 수행 → 빠른 접근 가능, 바이트 단위로 파일 내용 다룸 → 일반 + 특수 파일 모두 읽고 쓰기 가능, open 파일 참조시 파일 디스크립터 사용
  • 파일시스템과 파일시스템 내부의 inode에 대하여 low-level operation을 수행한다.
  • s_op = superblock operation table 포인터
  • superblock operation table = struct super_operations로 정의됨.

  • **inode개수대로 superblock 동적 할당파일시스템이 해당 superblock object에 operation을 수행해야 하는 경우 해당하는 연산에 대한 포인터를 따라감
  • ex) sb (superblock object 포인터) → s_op → (superblock opertaion table 내부) 수행하고자 하는 함수 (sb) ; ⇒ 함수 수행
  • 구조체의 각 요소들은 superblock object에 수행되는 함수에 대한 포인터
  •  

<superblock operations>

  • struct inode * alloc_inode(struct super_block *sb)

: 해당 superblock에 새로운 inode 생성 및 초기화

  • void destroy_inode(struct inode *inode)

: *inode가 가리키는 inode 할당 해제

  • *void dirty_inode(struct inode inode) - block 할 없는 함수

: *inode가 가리키는 inode가 수정되었을때 VFS에 의해 실행됨

  • void write_inode(struct inode *inode, int wait)

: 해당 inode를 디스크에 write하게 하는 함수

wait 파라미터는 동기화 여부를 표현한다.

  • void drop_inode(struct inode *inode)

: inode에 대한 마지막 참조가 제거될때 vfs에 의해 실행

(일반적인 유닉스 시스템에서는 정의하지 않는 함수)

  • void delete_inode(struct inode *inode)

: 디스크에서 해당 inode 제거

  • void put_super(struct super_block *sb)

: 해당 superblock object를 해제, 파일 시스템은 unmount 할때 vfs에 의해 호출 / caller는 반드시 s_lock을 가지고 있어야 한다.

  • void write_super(struct super_block *sb)

: 특정 superblock과 함께 on-disk superblock을 갱신하는 함수 / vfs는 메모리에서 수정된 superblock을 디스크와 동기화 시키기 위해 해당 함수 사용 / caller는 반드시 s_lock을 가지고 있어야 한다.

  • int sync_fs(struct super_block *sb, int wait)

: 디스크의 파일 시스템과 파일시스템의 메타데이터를 동기화하는 함수 / wait파라미터는 동기화/비동기화 방식을 전달

  • void write_super_lockfs(struct super_block *sb)

: 파일 시스템이 바뀌는 것을 방지, 특정 superblock에 대하여 on-disk superblock을 갱신함 - logical volume manager에 의해 주로사용됨.

  • void unlockfs(struct super_block *sb)

: write_super_lockfs에 의한 lock을 unlock하는 함수

  • int statfs(struct super_block *sb, struct statfs *statfs)

: statfs에 있는 파일시스템의 통계를 얻기 위해 vfs에 의해 호출되는 함수

  • int remount_fs(struct super_block *sb, int *flags, char *data)

: 파일 시스템이 새로운 마운트 옵션과 함게 재마운트될때 vfs에 의해 호출되는 함수 / caller는 반드시 s_lock을 가지고 있어야 한다.

  • void clear_inode(struct inode *inode)

: inode를 해제하고, 관련 데이터를 담고 있는 page를 clear하기 위해 vfs에 의해 호출되는 함수

  • void umount_begin(struct super_block *sb)

: 마운트 오퍼레이션을 interrupt하기 위해 vfs가 호출하는 함수

-> network filesystem에 의해 사용됨

  • 모든 함수들은 process context에서 vfs에 의해 발생, 모든 함수를 다 구현하지 않아도 됨, 구현하지 않을 함수는 해당 함수를 가리키는 함수 포인터를 NULL로 설정

inode object

  • 커널이 파일/디렉토리를 조작하기 위해 필요한 모든 정보를 표현
  • 파일 시스템의 각 파일을 표현
  • 파일 접근시 메모리에서만 생성
  • inode가 가지고 있는 특성을 파일 시스템이 지원하지 않을 수도 있음. 이런 경우 파일시스템이 자유롭게 해당 특성을 구현할 수 있음.
  • (non-unix file system) 파일 시스템이 inode를 가지고 있지 않다면 파일시스템은 디스크에서 해당 정보들을 가져와야 함
  • → 파일의 일부로 파일 정보를 포함하고 있음. (파일 - 파일 제어 정보 분리 X)
  • vfs에 마운트되기 위해서는 메모리상에 inode객체로 존재해야 함.
  • <linux/fs.h>에 구조체 구현

inode operations

inode 포인터 i -> i_op -> 함수(i) ;

  • int create(struct inode *dir, struct dentry *dentry, int mode)

: 주어진 dentry object와 관련된 새로운 inode를 생성하기 위한 시스템콜

  • struct dentry * lookup(struct inode *dir, struct dentry *dentry)

: 해당 dentry의 filename에 대응하는 dir에 inode가 있는 디렉토리를 찾는 함수

  • int link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)

: 새로운 filename dentry와 이전 dentry를 연결하는 hard link를 호출

  • int unlink(struct inode *dir, struct dentry *dentry)

: dentry 가 가리티는 dir에서 inode를 제거하는 함수

  • int symlink(struct inode *dir, struct dentry *dentry, const char *symname)

: 디렉토리에 dentry를 대표하는 파일의 이름과 symbolic link를 생성하는 함수

  • int mkdir(struct inode *dir, struct dentry *dentry, int mode)

: 새로운 디렉토리 생성

  • int rmdir(struct inode *dir, struct dentry *dentry)

: dir에서 dentry에 의해 참조되는 디렉토리를 제거하는 함수

  • int mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)

: special file을 만드는 함수

  • int rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry)

: new dentry로 파일이름을 설정하고, new dir으로 파일을 옮기는 함수

  • int readlink(struct dentry *dentry, char *buffer, int buflen)

: 최대 buflen byte만큼의 경로를 특정 버퍼에 복사하는 함수

  • int follow_link(struct dentry *dentry, struct nameidata *nd)

: 포인터가 가리키는 inode의 symbolic link를 번역하는 함수 ???

  • int put_link(struct dentry *dentry, struct nameidata *nd)

: follow_link후 clean up

  • void truncate(struct inode *inode)

: *inode가 가리키는 파일의 사이즈를 수정하는 함수

  • int permission(struct inode *inode, int mask)

: inode가 가리키는 파일의 접근 모드를 확인하는 함수

-> 접근이 허용되는 파일이면 0을 반환하고, 접근 비허용이면 negative error code를 반환한다.

  • int setattr(struct dentry *dentry, struct iattr *attr)

: inode 수정된 후에 change event를 알리기 위한 함수

  • int getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)

: 디스크 refresh를 위해 필요한 inode를 알리기 위한 함수

  • int setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags)

: dentry에 의해 참조된 파일의 value를 확장된 속성 이름으로 설정하는 함수

  • ssize_t getxattr(struct dentry *dentry, const char *name, void *value, size_t size)

: 특정한 파일의 확장된 속성 이름의 값을 복사하는 함수

  • ssize_t listxattr(struct dentry *dentry, char *list, size_t size

: 버퍼 리스트에 특정 파일의 모든 속성을 복사하는 함수

  • int removexattr(struct dentry *dentry, const char *name)

: 특정 파일의 특정 속성을 제거하는 함수

dentry object

  • 특정 디렉토리 대상 operation 수행을 위한 것
  • path의 specific component
  • vfs에서는 디렉터리 = 파일
  • → directory도 inode로 표현
  • 종종 dirctory-specific한 연산이 필요함ex) path name lookuppath name 탐색시 on-the-fly 에 dentry 생성 (물리적 디스크에 생성 X)
  • ** path를 해석하고 구성요소를 탐색하는 작업을 문자열 연산으로 수행하면 시간, 비용이 많이 든다 / 문자열 비교 작업 → 상당한 비용
  • ⇒ directory entry (dentry) 개념 제공
  • inode의 번호와 파일 이름은 관련하여 파일과 inode를 연결시켜줌
  • dentry cache를 유지하여 자주 접근되는 경로를 빠르게 접근
  • ex) /homw/yun/Document/hello.txt -> home, yun, Document, hello.txt 각각의 모든 경로 컴포넌트를 객체화
  • <linux/dcache.h> 에 구조체로 정의
  • disk에 물리적으로 저장되지 않고 메모리에 사용자 패턴에 따라 동적으로 생성, 소멸
  • → 변경 여부를 나타내는 flag가 없음 (디스크에 저장되지 않으니까 변경 여부를 디스크에 write하지 않아도 돼서 변경 여부를 표시하지 않아도 된다)
  • dentry state유효한 데이터를 가리키고 있기 때문에 메모리 확장을 위해 해당 공간 버릴 수 없음 (cache에서 절대 삭제되지 않음)유효한 데이터를 가리키고 있기 때문에 메모리 확장을 위해 해당 공간 버릴 수 없음메모리 확보가 필요하면 dentry버릴 수 있음dentry 유지 → future lookup을 빠르게 해결할 수 있음.
  • iii) negative : 유효하지 않은 invalid inode (inode가 없어지거나 경로 이름이 옳지 않은 경우) + 사용하지 않는 상태
  • locality를 고려하여 dentry 필요할때 다시 만들지 않도록 cache 보관
  • ii) unused : valid inode + vfs가 해당 dentry object를 사용하지 않는 중
  • i) used : valid inode + inode가 vfs에 의해 사용중

dentry cache

  • vfs layer가 모든 path name element를 dentry object로 만들고 path의 끝에 도달했는데 문제가 생긴 경우 모든 작업을 버리는 것은 낭비!
  • → 커널은 dentry object를 dcache에 캐시한다.
  • 경로명을 사용할때마다 먼저 dentry cache에서 찾고 없으면 직접 경로 탐색 (부모 inode → i_op() → lookup() 함수를 통해 디스크 접근)
  • lists of used dentries (used 상태인 dentry 리스트)해당 inode는 다양한 link를 가질 수 있기 때문에 여러 개의 dentry object를 가질 수 있고 여러 개 dentry는 list를 이용해 나타낸다.
  • inode object의 i_dentry 변수를 통해 연결된 used dentry list
  • doubly linked least recently used list of unused and negative dentry object
  • head에 삽입, tail로 갈 수록 오래된 dentry (LRU방식)(최근에 사용되지 않은 것은 앞으로도 사용될 확률이 적다고 판단)
  • 메모리 확보를 위해 커널이 entry를 제거해야 할떄, tail에서부터 제거
  • hash table and hashing function→ hash table은 dentry_hashtable array로 나타난다.해시테이블의 각 요소들은 같은 해시값을 가진 dentries의 리스트에 대한 포인터hash value 는 d_hash()에 따라 결정 -> 파일시스템마다 독자적인 해시함수를 갖게 함.
  • hash table lookup은 d_lookup()함수를 통해 수행 -> 일치하는 dentry object가 dcache에서 발견하면 이를 반환 / 없으면 NULL반환
  • 해시테이블의 사이즈는 시스템의 물리적 RAM의 크기에 따라 결정됨 (즉, 하드웨어 요소에 따라 결정)
  • 부모 dentry와 파일이름을 키로 하는 hash table - 시스템 메모리 크기에 따라 동적으로 할당 (LRU리스트 유지)
  • : dentry object를 빠르게 풀 수 있게 함
  • vfs는 path를 통해 접근을 할 떄, 처음 dcache에서 path name loop up을 시도→ 없으면 vfs는 path의 모든 요소에 대한 파일시스템 walking을 통해 찾아야 함
  • → 파일 시스템 탐색을 통해 path 요소들을 찾는 작업을 완료하면 커널은찾은 dentry object들은 dcache에 저장
  • → dcache에 있으면 final dentry object를 쉽게 얻을 수 있음.
  • dcache는 inode cache (=icache)에 front end 제공
  • dentry object는 inode에 대하여 사용 count를 양수로 유지하고 있기 때문에 해당 dentry object와 연관된 inode object는 free할 수 없다.
  • → dentry object는 inode를 메모리에 저장시킬 수 있음
  • 즉, dentry가 cache되면 연관된 inode들도 cached in memorytemporal - 프로그램이 같은 파일에 대하여 반복 접근 시도 (캐시된 dentry, inode에 대하여 hit할 확률 높음(⇒ caching dentry → next 포인터 조작을 통해 관련 파일에 접근하여 cache hit 가능성이 높음.
  • spatial - 프로그램이 같은 디렉토리(비슷한 장소)의 여러 파일에 접근 시도
  • ⇒ temporal, spatial locality를 잘 활용할 수 있도록 파일 접근 성능 향상
  • dentry가 가리키는 inode는 inode 사용횟수를 양수로 카운트
  • → 메모리,에 저장 (free X) icache에 저장
  • hash table로 구현된 dcache에 저장되어 있다면 hash로 접근 가능
  • 사용 횟수 카운트가 음수인 경우 (unused, negative) -> doubly linked list로연결

** dcahce에 찾고자 하는 dentry가 없을때 inode를 통해 직접 디스크 접근을 하는 동안 경로 내부의 dentry가 해제되지 않도록 작업 중의 dentry ref_count를 양수로 만들어 놓음. = 기존의 ref-walk 방식

→ vfs/dcache-scalinng series / looup 시작과 끝에 lock을 걸고 해제

dentry operations in <linux/dcache.h>

  • int d_revalidate(struct dentry *dentry, struct nameidata *)

: *dentry가 유효한지 판단

-> dcache로 부터 dentry를 사용하려고 준비할때 호출하는 함수

대부분 dcache된 dentry는 유효하기 때문에 파일시스템들이 잘 사용하지 않는 함수

  • int d_hash(struct dentry *dentry, struct qstr *name)

: *dentry에 대한 해시값을 생성하는 것 , dentry 해시테이블에 넣기 위해

  • int d_compare(struct dentry *dentry, struct qstr *name1, struct qstr *name2)

: file name 1,2를 비교하기 위한 함수 / 단순한 문자열 비교 함수

  • int d_delete (struct dentry *dentry)

: dentry object의 d_count = 0 인 경우 호출

-> dcache_lookup, d_lock을 필요로 함

  • void d_release(struct dentry *dentry)

: 특정 dentry를 free하기 위한 함수

  • void d_iput(struct dentry *dentry, struct inode *inode)

: dentry가 관련된 inode를 잃어버렸을때 호출 (디스크로부터 entry가 제거되었을때)

file object

  • 프로세스에 의해 open된 파일을 나타내기 위해 사용하는 object
  • 프로세스는 superblock, inode, dentry가 아닌 파일과 직접적으로 상호작용
  • 파일 open 할 때 메모리 상에 생성 , dentry처럼 디스크상에 존재 하지 않고, on-the-fly 형태로 메모리에 생성
  • 사용자 공간에서 가장 먼저 보이는 객체
  • 해당 파일이 어디에 저장되어 있는지 어떤 프로세스들이 사용중인지 유지
  • in-memory 된 open file != physical file
  • open() system call -> file object 생성
  • close() system call -> file object 소멸
  • file related call = file operation table에 정의
  • 멀티 프로세스가 동시에 하나의 파일을 열고 조작 수 있기 떄문에 같은 파일에 대하여 여러개의 file object가 생성될 수 있다.
  • file strcut가 가진 f_dentry 포인터 → dentry object → inode
  • inode와 dentry object는 독자적
  • 구조체 정의 <linux/fs.h>
  • 디스크에 물리적 형태로 존재하지 않음 -> dirty flage X
  • f_dentry 포인터 -> dentry object -> inode (파일 dirty 여부 반영)

file operatins in <linux/fs.h>

/ 파일 object 관련 함수 포인터를 가지고 있음.

  • loff_t llseek(struct file *file, loff_t offset, int origin)

: offset으로 파일 포인터를 갱신하는 것

  • ssize_t read(struct file *file, char *buf, size_t count, loff_t *offset)

: count bytes만큼 *file의 offset위치를 *buf에 read -> 파일 포인터 update

  • ssize_t aio_read(struct kiocb *iocb, char *buf, size_t count, loff_t offset)

: *iocb에 기술된 파일 버퍼에 count byte만큼 비동기 read를 시작하는 것

  • ssize_t write(struct file *file, const char *buf, size_t count, loff_t *offset)

: *file의 offset위치에서 count byte만큼 *buf에 wrtie -> 파일포인터 update

  • ssize_t aio_write(struct kiocb *iocb, const char *buf, size_t count, loff_t offset)

: *iocb에 기술된 파일 버퍼에 count byte만큼 비동기 write를 시작하는 것

  • int readdir(struct file *file, void *dirent, filldir_t filldir)

: 디렉토리 리스트에 next 디렉토리를 반환하는 것

  • unsigned int poll(struct file *file, struct poll_table_struct *poll_table)

: *file의 sleep, wait을 위한 함수

  • int ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)

: 디바이스에 command, argument 보냄 / file = open device node일떄 사용

반드시 caller가 BKL hold

  • int unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

: ioctl과 같은데 caller가 BKLhold하지 않아도 됨 /

유저스페이스에 ioctl 시스템콜이 일어났을때 BKL이 해당 장소에 이미 가지고 있으므로 필요 없음

  • int compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

: 32bit machine을 위한 ioctl / BKL 가지고 있지 않아도 됨.

  • int mmap(struct file *file, struct vm_area_struct *vma)

: 주어진 주소 공간으로 *file을 메모리 매핑

  • int open(struct inode *inode, struct file *file)

: 새로운 file object를 생성하고 이를 대응하는 inode와 연결

  • int flush(struct file *file)

: open된 파일의 참조 count가 감소할 때 호출 / filesystem-dependent (파일시스템에 따라 다름)

  • int release(struct inode *inode, struct file *file)

: 마지막으로 해당 파일을 참조 중인 것이 소멸되었을 때 호출

/ file을 사용하는 마지막 프로세스가 close(), exit한 경우

/ filesystem - denpendent

  • int fsync(struct file *file, struct dentry *dentry, int datasync)

: 파일의 모든 cached data를 디스크에 wrtie

  • int aio_fsync(struct kiocb *iocb, int datasync)

: iocb와 관련된 파일의 cached data를 디스크에 write

  • int fasync(int fd, struct file *file, int on)

: 비동기 입출력의 signal 하거나 안하거나

  • int lock(struct file *file, int cmd, struct file_lock *lock)

: *file의 lock 조작

  • ssize_t readv(struct file *file, const struct iovec *vector, unsigned long count, loff_t *offset)

: *file을 read-> *vector에 의해 묘사된 count buffer에 결과 저장 -> *offset++

  • ssize_t writev(struct file *file, const struct iovec *vector, unsigned long count, loff_t *offset)

: 위와 같은데 write

  • ssize_t sendfile(struct file *file, loff_t *offset, size_t size, read_actor_t actor, void *target)

: 어떤 파일에서 복사한 데이터를 다른 곳으로 복사 / 커널에서 복사 전체를 수행하고 유저 스페이스에서 관계없는 복사 비허용

  • ssize_t sendpage(struct file *file, struct page *page, int offset, size_t size, loff_t *pos, int more)

: 어떤 파일의 데이터를 다른 곳으로 보내기

  • unsigned long get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, unsigned long offset, unsigned long flags)

: *file를 매핑하기 위해 빈공간의 주소를 얻는 것

  • int check_flags(int flags)

: SETFL에 따라 flag의 유효성 여부를 확인 / NFS만 사용

-> invalid SETFL 제한

  • int flock(struct file *filp, int cmd, struct file_lock *fl)

: advisory locking제공

** advisory locking:

협동 프로세스들이 락들을 사용하여 이들 간에 파일에 대한 접근을 조율할 수 있지만 비협동 프로세스들은 마음껏 락을 무시하며 이들이 선택하는 어떠한 방식으로든 파일에 접근한다

**BKL = Big Kernel Locking

: CPU가 동시에 커널에 진입하는 것을 막아 동기화 문제 해결

**ioctl 함수 : 하드웨어 제어 명령

read, write 처리 가능, 하드웨어 제어나 상태를 얻기 위해 사용, 응용 프로그램의 명령에 따라 디바이스 드라이버의 매개변수 해석이 달라짐

매개변수 - 파일 디스크립터 / 입출력지정자 - 일반 파일, 소켓, 장치 등

요청할 정보, 얻은 정보를 저장하기 위한 버퍼

  • unlocked_ioctl() : =ioctl(), BKL없어도 호출 가능 / lock없을 수도 있으니까 동기화 보장은 작성자에게 달려 있음
  • compat_ioctl() : BKL 없어도 호출 가능 / 32bit compatible method for 64bit system / 존재하는 ioctl 명령에 따라 다르게 구현 가능 /32bit 값을 64bit kernel에 저적한 차입으로 바꾸는 것 / 64bit machine에서 32, 64 모두 정상 동작하도록

5. Data Structures associated with filesystems

  • 파일 시스템 관련 데이터 관리를 위한 표준 자료 구조
  • 구체적으로 다양한 파일시스템을 묘사하는 object, 마운트된 파일시스템의 인스턴스를 묘사하는 자료구조
  • 리눅스는 다양한 파일시스템을 지원 -> 커널은 각 파일시스템의 capabilities, behavior를 묘사하는 특별한 자료구조를 가지고 있어야 함
  • file_system_type struct <linux/fs.h>
  • get_sb() : 디스크로부터 superblock을 read / 파일 시스템이 로드되면 supberblock을 채움 / 파일시스템의 특성 묘사
  • 파일시스템 하나당 file_system_type하나씩
  • 파일시스템이 마운트되면 superblock + vfsmount 구조체 생성 -> 파일시스템 구체적인 인스턴스를 나타내는 구조체 = mount point (디바이스가 연결되는 directory)

=> vfsmount struct <linux/mount.h>

  • 모든 마운트 포인트 리스트를 유지하는 부분은 파일시스템과 다른 모든 마운드포인트의 relation

-> vfsmount의 다양한 연결리스트들이 연결된 다른 마운트 포인트간의 관계 정보를 tracking

  • mnt_flag :

(마운트 할때 전달)

mnt_nosuid - 파일시스템에 setuid, setgid flag 금지

mnt_nodev - 파일시스템에서 디바이스 파일 접근 금지

mnt_noexec - 파일시스템에서 바이너리 실행 금지

신뢰할수없는 디바이스 사용시 유용

6. Data Structures associated with a process

각 프로세스는 각자 오픈 파일, 루트 파일시스템, 현재 워킹 디렉토리, 마운트 포인트 등에 대한 리스트를 가지고 있음

프로세스 구조체 (task_struct)는 superblock, dentry, inode와 직접 상호작용하지 않고 file과 한다

VFS layer와 시스템 프로세스를 연결하는 자료 구조

  • files_struct
  • <linux/fdtable.h> 에 구조체 정의

processor descriptor(task_struct)에 files entry -> table address

오픈된 파일, 파일디스크립터에 대한 정보가 담겨 있음.

  • fd_array 배열 ->오픈된 파일 오브젝트 리스트 포인팅

NR_OPEN_DEFAULT = BITS_PER_LONG (64bit 아키텍쳐)

**프로세스가 64개 이상의 파일 오브젝트를 열면 커널은 새로운 배열을 할당하고 fdt포인터로 새로운 fd배열을 가리킨다.

=> 많은 파일오브젝트에 대한 접근이 빠르다 (정적배열) / 64개 이상 파일을 열려고 하면 커널은 새로운 배열을 만들어야 함.

시스템의 프로세스 대부분이 64개 이상의 파일을 열고자 한다면 성능 측면에서 NR_OPEN_DEFAULT 를 수정하여 배열에서 열 수 있는 파일을 개수 조절 가능.

** 프로세스 디스크립터 (task_struct) (사용 중 파일 리스트, 루트 파일 시스템, 현재 작업 디렉터리 등) 의 file entry -> file_struct (프로세스가 사용중인 open file list, file descriptor 정보)의 fd 포인터 -> fd_array (각각의 엔트리가 struct file / file object)를 가리킴

fd_array 용량을 초과하여 해당 프로세스가 파일을 오픈한 경우 fdtable를 할당하여 fdt가 이를 가리키게 함. fdtable의 fd 포인터가 새로운 fd_array를 가리킴.

** fdtable= fd + next / next 포인터에 새로운 fdtable을 계속 연결 가능

***fd_array의 크기는 VR_OPEN_DEFAULT로 수정 가능

****3.10 버전 이후)

fdtable의 next 포인터 X -> 크기를 증가시킨 fd_array를 만들어 기존의 배열 복사해서 하나로 합침.

  • fs_struct
  • 프로세스와 관련된 파일시스템에 대한 정보 담고 있음

/ 루트 디렉터리 정보, 현재 작중인 디렉터리 패스워드 등

  • 프로세스 디스크립터(task_struct)의 fs 가 가리키는 구조체
  • <linux/fs_struct.h> 에 구조체 정의
  • curretn working directory, root directory 정보
  • mnt_namespace
  • <linux/mnt_namespace.h>에 구조체 정의

list 멤버는 namespace를 구성하는 마운트된 파일시스템의 이중연결리스트를 명세한다.

/ namespace : 시스템의 루트 파일시스템을 루트로 두는 마운트된 파일시스템의 트리 구조

  • 프로세스 디스크립터의 mnt_namespace가 가리키는 구조체
  • 프로세스가 시스템에서 마운트된 파일시스템의 unique view를 가지게 함.
  • root dir + 전체적인 파일시스템 계층 구조
  • 모든 프로세스가 같은 namespace 공유

=> 같은 마운트 테이블을 기준으로 같은 파일시스템 계층구조 인식

  • namespace 구조체 복사는 CLONE_NEWS flag가 명시되었을때만 발생
  • 세가지 자료 구조들은 process desciptor마다 연결되어 있음

대부분의 process descriptor-> unique file_struct , fs_struct

  • CLONE_FILES, CLONE_FS로 flag 복사하여 프로세스 생성 + 위 세 자료 구조 공유

=> 여러 개의 process descriptor가 같은 files/fs_struct를 공유할 수 있음.

  • 각 자료구조의 count 는 프로세스가 자료구조를 이용하는 둥에 소멸되는 것을 방지 위해 참조 카운트를 제공

https://hyoje420.tistory.com/53

모든 파일시스템은 정보를 유지하는 슈퍼블록을 하나씩 가짐.

슈퍼블록은 그 안에 속한 파일의 inode (파일의 메타데이터) 리스트를 가지고 있음.

아이노드는 각각의 파일에 대한 메타데이터를 저장하고 있음

파일의 경로는 모두 dentry 객체로 메모리에 저장

파일 객체는 프로세스가 사용중인 객체

슈퍼블록 - 아이노드 리스트 유지 -> 파일 객체에 접근

dentry - 캐시를 유지하여 자주 접근되는 경로를 더 빠르게 접할 수 있도록함.

디렉토리 ~ 파일 관계 유지