[VFS]

Embedded Lab/linux, x86 2012. 2. 15. 17:57

□ 가상 파일 시스템(Virtual File System, VFS)

○ 파일시스템 인터페이스를 유저공간 프로그램에 제공하기 위해 구현된 커널의 서브 시스템

○ 모든 파일 시스템은 공존하는 것 뿐만 아니라 상호동작하기 위하여 VFS에 의존

○ 서로 다른 미디어의 서로 다른 파일 시스템에 대해 읽고 쓰는 작업을 하기 위해 표준 유닉스

시스템 콜을 사용할 수 있게 해줌

○ 실제 파일 시스템의 구현과 사용자 프로세스 사이에 존재하는 추상화 계층

○ 표준 유닉스 파일시스템과 관련한 모든 시스템 콜을 처리하는 kernel software layer

○ VFS 전체 구조

□ 공통 파일 시스템 인터페이스

○ open(), read(), write()와 같은 시스템 콜들이 파일시스템이나 물리적 매체와 무관하게 동작

할 수 있게 하는 역할

 

□ 파일 시스템 추상화 레이어

○ 모든 타입의 파일시스템에 대한 범용 인터페이스는 커널이 로우레벨 파일시스템, 인터페이스와

같은 추상 레이어에서 해당 인터페이스를 구현하기 때문에 사용 가능

⇒ 리눅스로 하여금 지원되는 속성이나 동작에 있어서 상이한 다른 파일시스템을 지원할 수 있게 해줌

○ 추상레이어는 모든 파일 시스템을 지원하는 기초 추상 인터페이스와 자료구조를 정의하는 방식으로 동작

○ 커널이 여러타입의 파일 시스템을 쉽고 명확하게 지원하도록 해줌

 

□ 유닉스 파일 시스템

○ 파일시스템 관련된 4가지 기본 추상화를 제공

◇ 파일

◇ 디렉토리 엔트리

◇ inode

◇ 마운트 포인트

○ 데이터를 특정한 구조를 표현하기 위한 계층적 저장소

◇ 파일, 디렉터리 관련 제어 정보를 포함

◇ 생성, 삭제및 마운팅이란 전형적인 동작을 제공

◇ UNIX에서 파일시스템은 네임스페이스란 알려진 전역 계층구조의 특정 마운트 포인트에 연결

⇒ 마운트된 모든 파일 시스템들은 단일 트리의 항목처럼 보임

파일이란 정렬된 바이트 스트링이라 할 수 있음

파일은 디렉터리 안에 존재

◇ 디렉터리는 서류철과 비슷한 것으로 일반적으로 서로 관련 있는 파일들을 포함

◇ 디렉터리는 서브디렉터리를 포함 가능

◇ 경로를 표현하기 위하여 디렉터리를 중첩사용 가능

◇ 경로의 각 요소를 디렉터리 엔트리라함

○ 파일자체와 파일에 대한 정보를 구분

◇ 이정보는 종종 파일 메타데이터라 불리며 inode라 불리는 분리된 자료구조에 저장

◇ superblock에 저장돼있는 파일시스템의 제어정보와 함께 포함

◇ superblock : 파일시스템 전체에 대한 정보를 담고 있는 자료구조

○ 위의 개념들을 디스크 레이아웃의 일부로 구현

○ 몇몇 비 유닉스 계열의 디스크 기반 파일시스템

◇ 디렉토리 트리에 각 파일이 있는 파일 할당 테이블(FAT) 를 사용

◇ 이 디렉토리들은 파일이 아닌데, 리눅스에서는 FAT-based filesystem 을 필요할 때 디렉토리와

유사한 파일을 즉석으로 구현

◇ 이러한 파일은 커널 메모리에 객체로만 존재

 

□ VFS 객체와 그 자료구조

○ 객체지향적, 일련의 자료구조들이 공통파일모델을 표현하기위해 존재

○ VFS의 네가지 주요 객체

◇ The superblock object

△ 마운트시킨 파일 시스템 정보를 저장

△ disk-based filesystem을 위해서 이 object는 보통 disk에 저장된 filesystem control block 에 해당

◇ The inode object

△ 특정 파일에 대한 일반 정보를 저장

△ disk-based filesystem을 위해서 이 object는 보통 disk에 저장된 filesystem control block에 해당

△ 각 object는 inode number를 가짐

◇ The file object

△ 열린 파일과 프로세스 사이의 상호 작용과 관련한 정보 저장

△ 이 정보는 단지 각 프로세스가 파일을 접근한 동안만 kernel memory에 존재

◇ The dentry object

△ 디렉토리 항목과 대응하는 파일간 연결에 대한 정보 저장

△ 각 디스크 기반 파일 시스템에서는 독특한 방법으로 정보를 디스크에 저장

○ VFS는 디렉토리도 일반파일로 취급한다.

○ 각 객체의 주요함수

◇ super_operation 객체 : read_inode(), sync_fs() 등

◇ inode_operation 객체 : create(), link() 등

◇ dentry_operation 객체 : d_compare(), d_delete() 등

◇ file 객체 : read(), write() 등

실제 C++처럼 명시적 오브젝트가 아니라 구조체를 객체처럼 사용하는 것이다.

 

■ 다른 VFS 객체

file_system_type : 각각의 등록된 파일시스템의 정보

vfsmount : 각각의 마운트 포인트의 정보

file_struct, fs struct, namespace : 프로세스당 3개씩 존재하는 구조체.

 

□ 수퍼블록 객체

<디스크 자료구조>

<superblock object>

○ 각 파일 시스템에 구현되어있음

○ 해당 파일 시스템을 표현하기 위한 다양한 정보들을 포함

(총 inode의 수, filesystem 크기, block 크기, 단편 크기, 그룹 당 inode 수, 마지막 mount한 시간, 마지막 쓰기 시간등 )

○ 디스크의 특별한 섹터에 저장되어 있는 수퍼블록이나 파일 시스템 제어블록에 해당

○ 디스크 기반이 아닌 파일시스템은 동작 중에 수퍼블록을 생성하고 이를 메모리에 저장

(sysfs등의 가상메모리 기반 파일시스템)

○ struct super_block으로 표현 <linux/fs.h>

struct super_block {

struct list_heads_list;//모든 수퍼블록의 목록

dev_ts_dev;//식별자

unsigned longs_blocksize;//bytes 단위의 블록 크기

unsigned longs_old_blocksize;//bytes 단위의 오래된 블록의 크기

unsigned chars_blocksize_bits;//bits 단위의 블록 크기

unsigned chars_dirt;//dirty 플래그

unsigned long longs_maxbytes;//파일 크기의 최대값

struct file_system_types_type;//파일시스템 타입

struct super_operations s_op;//수퍼블록 함수

struct dquot_operations*dq_op;//쿼터(quota) 함수

struct quotactl_ops*s_qcop;//쿼터 제어 함수

struct export_operations *s_export_op;//익스포트(export) 함수

unsigned longs_flags;//마운트 플래그

unsigned longs_magic;//파일시스템의매직넘버(magic number)

struct dentry*s_root;//디렉터리 마운트 포인트

struct rw_semaphores_umount;//언마운트 세마포어

struct semaphores_lock;//수퍼블록 세마포어

ints_count;//수퍼블록 참조 카운터

ints_syncing;//파일시스템 동기(syncing) 플래그

ints_need_sync_fs;//not-yet-synced 플래그

atomic_ts_active;//액티브 참조 카운터

void*s_security;//보안 모듈

struct list_heads_dirty;//dirty inode의 목록

struct list_heads_io;//writebacks의 목록

struct hlist_heads_anon;//익명 dentry

struct list_heads_files;//할당된 파일의 목록

struct block_device*s_bdev;//하당된 블록디바이스 드라이버

struct list_heads_instances;//이러한 형태의 파일시스템 목록

struct quota_infos_dquot;//quota 관련 옵션

chars_id[32];//텍스트 이름

void*s_fs_info;//파일시스템 특유의 정보

struct semaphores_vfs_rename_sem; //디렉터리 rename 세마포어

};

○ 수퍼블록 객체의 생성, 관리 및 파괴를 위한 코드 <fs/super.c>

○ alloc_super() 함수에 의해 생성/초기화

○ 파일 시스템이 마운트되면 파일시스템은 이 함수를 호출하여 디스크로부터 해당 수퍼블록을 읽어들여

스퍼불록 객체를 채움

 

■ 수퍼블록 연산

○ 수퍼블록 연산테이블(superblock operation table) s_op

○ struct super_operations으로 표현 <linux/fs.h>

struct super_oprations {

struct inode *(*alloc_inode) (struct super_block *sb);

void (*destroy_inode) (struct inode *);

void (*read_inode) (struct inode *);

void (*dirty_inode) (struct inode *);

void (*write_inode) (sruct inode *, int);

void (*put_inode) (struct inode *);

void (*drop_inode) (struct inode *);

void (*delete_inode) (struct inode *);

void (*put_super) (struct super_block *);

void (*write_super) (struct super_block *);

int (*sync_fs) (struct super_block *, int);

void (*write_super_lockfs) (struct super_block *)

void (*unlockfs) (struct super_block *);

int (*statfs) (struct super_block *, struct statfs *);

int (*remount_fs) (struct super_block *, int *, char *);

void (*clear_inode) (struct inode *);

void (*umount_begin) (struct super_block *);

int (*show_options) (struct seq_file *, struct vfsmount *);

};

○ 수퍼블록 객체에 대한 동작을 나타내는 포인터 파일시스템과 inode에 대한 로우레벨의 동작을 수행

○ 파일시스템이 수퍼블록에 대해 어떠한 동작을 수행해야 할 경우, 수퍼블록 객체에 대한 포인터 뒤에 필요한

함수를 명시하여 호출할 수 있음

ex) 파일시스템이 해당 수퍼블록을 쓰고(write)싶을 때,

sb->s_op->write_super(sb);

sb: 파일 시스템 수퍼블록에 대한 포인터

sb 포인터 뒤에 s_op를 명시하여 수퍼블록 연산테이블을 가리킬 수 있음

수퍼블록 연산(super_operations)

◇ struct inode *alloc_inode(struct super_block *sb)

- 주어진 수퍼블록에서 새로운 inode 객체를 생성하고 초기화

◇ void destroy_inode(struct inode *)

- 주어진 inode를 삭제

◇ void read_inode(struct inode *)

- inode->i_ino로 주어진 inode를 디스크로부터 읽어 inode 구조체의 나머지 부분을 채움

◇ void dirty_inode(struct inode *)

- inode가 수정된 경우 VFS로부터 호출

- ext3와 같은 저널링(journaling)파일 시스템은 기록을 갱신하기 위하여 이 함수를 사용

◇ void write_inode(sruct inode *, int)

- 주어진 inode를 디스크에 기록

- wait 파라미터는 이러한 동작이 동기적이어야 하는 지를 명시

◇ void put_inode(struct inode *)

- 주어진 inode를 해제

◇ void drop_inode(struct inode *)

- inode에 대한 최근 참조가 드롭(drop)된 경우 VFS로부터 호출

- 호출자는 반드시 inode_lock을 가지고 있어야 함

- 일반적인 유닉스 파일시스템은 VFS가 inode를 그냥 삭제하므로 이 함수를 정의하지 않음

◇ void delete_inode(struct inode *)

- 주어진 inode를 디스크로부터 삭제

◇ id put_super(struct super_block *)

- 언마운트 때 해당 수퍼블록을 해제하기 위하여 VFS로부터 호출

◇ void write_super(struct super_block *)

- 주어진 수퍼블록을 이용하여 디스크상의 수퍼블록을 갱신

- 메모리 상에서 수정된 수퍼블록과 데스크상의 수퍼블록을 동기화 하는데 사용

◇ int sync_fs(struct super_block *, int)

- 파일시스템 메타데이터를 디스크 상의 데이터와 동기화

- wait 파라미터는 해당 동작이 동기적이어야 하는지를 명시

◇ void write_super_lockfs(struct super_block *)

- 파일시스템에 대한 수정을 방지

- 디스크상의 수퍼블록을 명시된 수퍼블록으로 갱신

- 현재 LVM(Logical Volume Manager)에 의해 사용되고 있음

◇ void unlockfs(struct super_block *)

- write_super_lockfs()에 의한 변경이 있는 경우 파일 시스템의 락을 해제

◇ int statfs(struct super_block *, struct statfs *)

- 파일시스템에 대한 통계 정보를 얻기 위하여 VFS에 의해 호출

- 해당 파일시스템과 관련된 통계정보는 statfs에 존재

◇ int remount_fs(struct super_block *, int *, char *)

- 파일시스템이 새로운 마운트 옵션으로 다시 마운트 되는 경우 VFS로부터 호출

◇ void clear_inode(struct inode *)

- inode를 해제하고 이와 관련된 데이터를 포함하고 있는 페이지를 클리어하기 위하여 VFS로부터 호출

◇ void umount_begin(struct super_block *)

- 마운트 동작을 중단하기 위하여 VFS에 의해 호출

- NFS와 같은 네트워크 파일 시스템에서 사용

◇ int show_options(struct seq_file *, struct vfsmount *)

- 모든 함수는 VFS에 의해 프로세스 컨텍스트에서 호출, 모두 블록될 수 있음

- 파일시스템에 따라 일부의 수퍼블록 연산 구조체를 NULL로 설정할 수 있음

- 해당 포인터가 NULL인 경우, VFS는 동작에 따라 범용 함수를 호출하거나 아무런 일도 하지 않음

 

□ inode 객체

○ 파일이 생성될때 만들어진다.

○ 커널이 파일이나 디렉터리를 관리하기 위한 모든 필요한 정보를 표현.

○ 파일과 디렉토리 모두 한 개의 아이노드를 가진다.

○ 디스크에 정적으로 존재한다.

○ inode에는 디바이스파일이나 파이프와 같은 특별한 파일들이 포함된다.

○ 특수파일과 관련된 필드로는 i_pipe, i_devices, i_bdev, i_cdev 등이 있다.

○ 객체는 include/linux/fs.h 에 정의되어있다.

<inode 구조체>

 

<inode에서의 디렉터리 표현>

○ 구조체

struct inode {

struct hlist_nodei_hash;// 해시 목록

struct list_headi_list;// inode들에 대한 연결리스트

struct list_headi_dentry;// dentry들에 대한 연결리스트

unsigned longi_ino;// inode 번호

// 만약이것을 cid로 사용하면 32bit 제약이 따른다

 

atomic_ti_count// 참조카운터

umode_ti_mode;// 접근권한

unsigned inti_nlink;// 하드링크의 개수

uid_ti_uid;// 소유자의 사용자 id

gid_ti_gid;// 소유자의 그룹 id

kdev_ti_rdev;// 실제 디바이스 노드

// device special file인 경우, 실제 device장치 번호를 가지고 있음

 

loff_ti_size;// bytes 단위의 파일크기

struct timespeci_atime;// 최종 파일 접근시간

struct timespeci_mtime;// 최종 파일 수정시간

struct timespeci_ctime;// 최종 파일 변경시간

unsigned inti_blkbits;// bits 단위의 블록 크기

unsigned longi_blksize;// bytes 단위의 블록 크기

// file system independent부분에서 활용하는부분은 stat관련 뿐. <활용가능필드>

 

unsigned longi_version;// 버전번호

// directory cache에서 cache에 대한 버젼 비교 수행

 

unsigned longi_blocks;// block 단위의 파일크기

// independent부분에서 활용되는 부분은 stat관련 뿐이다. <활용 필드>

 

unsigned shorti_bytes;// 소비된 바이트

spinlock_ti_lock;// 스핀락

struct rw_semaphorei_alloc_sem;// si_sem으로 통제된 구역에서 이용

struct semaphorei_sem;// inode 세마포어

// write, writev,truncate inode create/delete 사이의 세마포어

 

struct inode_operations*i_op;// inode 연산테이블

// inode에 대한 operation function pointer

 

struct file_operations*i_fop;// 파일 연산

struct super_block*i_sb;// 연관 슈퍼블락

struct file_lock*i_flock;// 파일 lock 목록

struct address_space*i_mapping// 관련 맵핑

// VM상의 메모리 영역을 나타내는 vm_area_struct 객체의 포인터, 이를 이용하여 현재의

// inode에 걸린 VM상의 메모리 오브젝트를 접근할 수 있다. VM을 위한 필드이며, FS에서

// 는 큰 쓰임이 없다. FS에서는 mandatory lock이 Shared VM으로 걸린 inode인 경우 사

// 용하지 못하도록 하는 것이 고작이다.

 

struct address_spacei_data// 장치 주소 공간

struct dquot*i_dquot[MAXQUOTAS] // inode를 위한 디스크 quota

struct list_headi_devices;// 블록 장치 목록

struct pipe_inode_info*i_pipe;// 파이프 정보

struct block_device*i_bdev;// 블록 장치 드라이버

unsigned longi_dnotify_mask;// 디렉터리 notify 이벤트

struct dnotify_struct*i_dnotify;// 디렉터리 notify 용

unsigned longi_state;// 상태 플래그

unsigned longdirtied when;// 최초로 수정이 발생한 시각

unsigned inti_flags;// 파일시스템 플래그

unsigned chari_sock;// 소켓 여부

atomic_ti_writecount;// 쓰기 작업 카운터

void*i_securiry;// 보안모듈

__u32i_generation;// inode 버전번호

union {

void*generic_ip;// 파일 시스템 관련 정보

} u;

};

 

■ inode 연산

○ 슈퍼블록 연산과 더불어 VFS가 inode에 대해 호출할 수 있는 파일시스템 함수를 설명하는

inode_operations 역시 중요하다.

○ inode 연산의 호출법

◇ i->i_op->truncate(i)// 여기서 I 는 특정한 inode를 가리킨다.

<inode operations 호출과정>

inode 에 관련된 method

◇ 인덱스노드 연산 테이블

create(dir, dentry, mode)

- 새로운 disk inode 를 생성

lookup(dir, dentry)

- dentry object 에 포함된 filename 에 대응하는 inode 를 위해 directory 를 탐색

link(old_dentry, dir, new_dentry)

- dir 에서 old_dentry 에 의해 지정된 file 을 참조하는 새로운 hard link 를 생성

- 새 hard link 는 new_dentry 라는 이름을 사용

unlink(dir, dentry)

- dir 로부터 dentry object 에 의해 지정된 file 의 hard link 를 삭제

symlink(dir, dentry, symname)

- 같은 directory에 있는 dentry object를 가지는 symbolic link를 위한 새로운 inode를 생성

mkdir(dir, dentry, moder)

- 새로운 inode 를 생성

rmdir(dir, dentry)

- dentry object에 포함된 이름의 subdirectory를 삭제

mknod(dir, dentry, mode, rdev)

- 새로운 disk inode를 생성, mode와 rdev는 file type과 device의 major number지정

rename(old_dir, old_dentry, new_dir, new_dentry)

- 디렉토리 내부에서 디엔트리 객체가 나타내는 심볼릭 링크를 위해 새로운 아이노드 생성

readlink(dentry, buffer, buflen)

- buffer에 dentry에 명시된 symbolic link에 대응하는 file pathname을 복사

follow_link(inode, dir)

- inode object에 지정된 symbolic link를 변환

readpage(file, pg)

- 열린 파일에서 자료를 한 페이지 읽어온다.

writepage(file, pg)

- 자료 한 페이지를 열린 파일에 기록한다.

bmap(inode, block)

- inode와 관련된 파일의 file block number에 대응하는 logical block number를 return.

truncate(inode)

- inode에 있는 file의 크기를 수정

permission(inode, mask)

- file에 특정 access mode가 허용되는지 check.

smap(inode, sector)

- 디스크 섹터 번호 결정

updatepage(inode, pg, buf, offset, count, sync)

- 아이노드가 나타내는 파일 자료 한 페이지를 갱신

revalidate(dentry)

- dentry object에 의해 지정된 파일의 cache attribute를 갱신

 

□ 덴트리 객체

○ 디렉토리 entry를 메모리에서 읽으면, VFS에 의해서 dentry structure 의 dentry 객체가 바뀜

○ 프로세스가 찾은 경로의 각각의 component 로 구성된 dentry객체는 커널에 의해 생성

○ dentry객체는 inode와 유사한 구성요소와 연결

○ /tmp/test 경로명을 찾을 때

◇ 커널은 / 디렉토리를 위해 dentry 객체를 생성

◇ 두번째 / 디렉토리의 tmp entry 를 위한 dentry 객체를 생성

◇ 세번째 /tmp 디렉토리의 test entry 를 위한 dentry 객체를 생성

○ dentry 객체는 디스크에 대응하는 이미지가 없음

◇ dentry구조체에는 변경된 객체를 나타내는 field가 없음

◇ dentry객체는 dentry_cache라고 불리는 slab allocator cache에 저장

◇ dentry객체는 kmem_cache_alloc()와 kmem_cache_free()에 의해 생성과 소멸

○ 덴트리 객체는 strcut dentry로 표현, <linux/dcache.h>에 정의

struct dentry {

atomic_t d_count;

unsigned int d_flags;

struct inode * d_inode; /* Where the name belongs to - NULL

is negative */

struct dentry * d_parent; /* parent directory */

struct list_head d_vfsmnt;

struct lis t_head d_hash; /* lookup hash list */

struct list_head d_lru; /* d_count = 0 LRU list */

struct list_head d_child; /* child of parent list */

struct list_head d_subdirs; /* our children */

struct list_head d_alias; /* inode alias list */

struct qstr d_name;

unsigned long d_time; /* used by d_revalidate */

struct dentry_operations *d_op;

struct super_block * d_sb; /* The root of the dentry tree */

unsigned long d_reftime; /* last time referenced */

void * d_fsdata; /* fs-specific data */

unsigned char d_iname[DNAME_INLINE_LEN]; /* small names */

};

Field

Description

atomic_d d_dount

Dentry Object의 사용계수

unsigned int d_flags

Dentry의 flag

struct inode *d_inode

파일 이름과 관련된 inode에 대한 포인터

struct dentry *d_parent

Parent directory에 대한 dentry 포인터

struct list_head d_vfsmnt

VFS mount에 대한 연결 구조체

struct list_head d_hash

Dentry를 찾기위한 hash 연결 구조체

struct list_head d_lru

사용되지 않은 Dentry에 대한 연결 구조체

struct list_head d_child

Parent directory에 포함된 dentry object들에 대한

연결 구조체

struct list_head d_subdirs

하위(sub) directory에 있는 dentry에 대한 연결 구조체

struct list_head d_alias

관련된 inode들에 대한 연결 구조체

struct qstr d_name

파일의 이름

unsigned long d_time

d_revalidate연산에 사용되는 필드로 어떤 시간이 흐른

뒤에 다시 유효한지를 검사하기 위한 것이다.

struct dentry_operations *d_op

Dentry에 대한 연산을 정의한 구조체에 대한 포인터

struct super_block *d_sb

Super block object에 대한 포인터

unsigned long d_reftime

Dentry가 제거된 시점을 가리키는 시간

void *d_fsdata

파일 시스템에 의존적인 데이터 구조를 가르키는 포인터:

이 구조체에 파일 시스템에 고유한 데이터 구조를저장해

둘 수 있다.

unsigned char d_iname[DNAME_INLINE_LEN]

짧은 파일 이름에 대해서 할당된 공간

○ Dentry는 프로세스가 파일시스템에 접근해서 구조적인 정보(directory)를 읽어와서 생성하는 데이터 구조체

○ 실행중인 프로세스는 번번한 접근이 일어나기에 이것을 캐쉬에 넣어서 보관하고 사용을 마쳤을

경우에는 사용이 끝났다는 것을 알리고 다시 재사용되기 위해서 연결리스트로 관리

 

■ 덴트리 상태

○ 덴트리구조체는 상태정보를 가지며,

아무런 유효한 정보를 가지고 있지 못할때는 FREE

◇ 커널에서는 사용하지 않지만 관련된 inode object에 대한 포인터를 가지고 있을경우에는 UNUSED

◇ 현재 커널에서 사용중이라면 Used

◇ dentry와 관련된 inode object를 없지만 나중에 다시 이것을 사용할 가능성이 있을경우를 대비 하여

캐쉬내에 보관할 경우에는 Negaticve

○ Unused

◇ d_count는 0이고, d_inode는 여전히 관련된 inode를 가리킴

◇ 유효한 정보를 유지하지만, 그 내용은 삭제될 수 있다.

○ Used

◇ dentry는 유효한 inode에 해당

◇ 객체에 대한 하나의 사용자가 존재 할수 있음

◇ d_count사용 카운터는 양수, d_inode필드는 연관된 아이노드 객체를 가리킴

◇ 유효한 정보를 유지하지만 이를 제거할 수 없다.

○ Negative

◇ 경로명이 정확하지 않거나 inode가 삭제됐기 때문에 유효한 inode와 연관돼 있지 않음

◇ dentry와 관련된 inode가 더 이상 존재하지 않고 d_inode는 null로 설정

◇ 향후의 참조가 빠르게 처리될수 있도록 dentry는 유지

◇ 같은 file pathname에 대해 탐색 연산을 빨리 해결하기 위해 여전히 dentry cache에 남아있음

○ Free

◇ 유효한 정보가 없고 VFS에 의해 사용되지 않음

◇ 해당 메모리 역역은 슬랩 할당자(slab allocator)에 의해 처리

 

■ 덴트리 캐쉬

○ 디스크에서 디렉토리 엔트리를 읽어서 이에 대응 하는 덴트리 객체를 생성하는 일은 비경제적

⇒ 사용을 끝낸 다음에도 다시 사용할 것 같은 덴트리 객체를 계속 메모리에 유지하는 것이 도움이 됨

⇒ dentry cache를 이용하여 덴트리 객체를 캐싱

⇒ 예를 들어, 사용자는 파일을 편집하고,파일을 컴파일한 다음 다시 편집하고, 인쇄하거나 복사하고,

복사한 파일을 편집한다. 이러한 경우 동일한 파일에 반복하여 접근하게된다.

○ 덴트리 캐쉬는 3부분으로 이루어져 있음

◇ 상태가 used인 덴트리들의 리스트

- inode객체의 필드 I_dentry를 통하여 inode들과 연결됨

- inode는 다수의 링크를 가질수 있으므로 다수의 덴트리 객체가 존재 할수 있으며 따라서 리스트가 사용

◇ 최근에 가장 적게 사용된 리스트

- 상태가 unused와 negative인 덴트리 객체에 대한 이중 연결 리스트

- 최산항목이 헤드(head)에 가깝도록 삽입시간을 기준으로 정렬

- 커널이 메모리를 회수하기 위해 항목을 제거하는 경우 리스트의 꼬리부터 제거

⇒ 리스트의 꼬리에 잇는 항목이 가장 오래 됐고 따라서 앞으로 사용할 확률이 가장 적음

◇ 해시테이블(hash table)과 해시함수

- 주어진 경로와 관련된 덴트리 객체를 빠르게 연관시키기 위한

- dentry_hashtable 배열을 사용하여 해시 테이브을 구현

- 각 항목은 해시 함수를 거치면 같은 해시 테이블 값으로 해시되는 디엔트리들의 리스트를 가리키는 포인터

- 배열의 크기는 시스템의 메모리 용량에 따라 다름

- 디엔트리 객체의 d_hash 필드는 해시 값이 같은 리스트의 인접 항목에 대한 포인터를 포함

- 해시 함수는 디렉토리와 파일명의 디엔트리객체 주소 두 개로부터 해서 값을 생성한다.

- 해시테이블 참조는 d_lookup()을 통하여 수행

⇒ 캐시를 검색하여 일치하는 덴트리 객채가

발견될 경우 해당항목을 반환

구걿지 않을 경우 NULL이 반환

○ dcache_lock 스핀 락은 멀티프로세서 시스템에서 디엔트리 캐시 자료 구조에 대한 동시

접근을 방지

 

■ 덴트리 연산

○ dentry_operations구조체는 VFS가 해당 파일시스템의 데릭터리에 대해 호출하는 함수가 명시

○ dentry_operations은 <linux/dcache.h>에 정의

◇ d_revalidate(dentry, flag)

- 파일 경로명을 변환하기 위해 디엔트리 객체를 사용하기 전에 디엔트리 객체가 아직도 유효한지 결정

- 기본 vfs 함수는 아무 일도 하지 않지만, 네트워크 파일시스템의 경우에 자신의 함수를 지정

○ d_hash(dentry, name)

- 해시 값을 생성

- 덴트리 해시 테이블에 사용되며, 파일시스템마다 독자적인 해시함수

- dentry 매개 변수는구성 요소를 포함하는 디렉토리

- name 매개 변수는 탐색하고자 하는 경로명 구성 요소, 해시 함수가 생성하는 값을포함하는 구조체

○ d_compare(dir, name1, name2)

- 두파일명을 비교

- name1은 반드시 dir이 가리키는 디렉토리에 속해야 함

- 기본 VFS함수는 단순 문자열 비교 함수

- 그러나 각 파일시스템은 자체의 방법으로 이 메소드를 구현할 수 있다.

예를 들어, MS-DOS는 대소문자를 구별하지 않는다.

○ d_delete(dentry)

- 덴트리 객체에 대한 마지막 참조가 삭제되면(d_count가 0이 될 때) 호출

- 기본 VFS 함수는 아무 일도 하지 않는다.

○ d_release(dentry)

- 덴트리 객체를 해제할 때(슬랩 할당자에 반납) 호출

- 기본 VFS 함수는 아무 일도 하지 않는다.

○ d_iput(dentry, ino)

- 덴트리 객체가 '네거티브'가 될 때 즉 아이노드를 잃을 때 호출

- 기본 VFS 함수는 아이노드 객체를 해제하기 위해 iput()을 호출

 

□ 파일 객체

○ 파일객체는 프로세스에 의해 열려있는 파일을 표현하기 위하여 사용된다.

○ 프로세스에서는 디렉토리를 수퍼블록이나 inode, 또는 덴트리가 아닌 파일로 취급한다. 그렇기

때문에 파일객체에 포함된 정보는 낯설지가 않다. 예를들어 연산의 read()나 write()와 같은 시스템

콜과 매우 유사한 명령어도 되어있다.

○ 또, 파일객체는 열려있는 파일에 대한 메모리 내부의 표현이다. 객체는 open()시스템 콜의 결과로

생성되며, close()시스템 콜로 삭제된다. 이와 같이 파일과 관련된 모든 호출들은 파일연산 테이블에

정의돼있는 실제 함수들이다. 여러 개의 프로세스가 동시에 하나의 파일을 열고 조작할 수 있으므로

동일파일에 대하여 여러 개의 파일 객체가 존재할 수 있다.

○ 파일 객체는 단지 열려진 파일에 대한 프로세스의 뷰를 표현해 줄 뿐이다.

○ 파일 객체는 struct file로 표현되며 <linux/fs.h>에 정의되어있다.

○ 구조체

struct file {

 

struct list_headf_list;/*파일 객체 리스트*/

struct dentry*f_dentry;/*연관돼있는 덴트리 객체*/

struct vfsmount*f_vfsmnt;/*연관돼있는 마운트된 파일시스템*/

struct file_operations*f_op;/*파일 연산 테이블*/

atomic_tf_count;/*파일 객체의 사용량 카운터*/

unsigned int f_flags;/*open시 명시되는 플래그*/

mode_tf_mode;/*파일 접근 모드*/

loff_tf_pos;/*파일 오프셋*/

struct fown_structf_owner;/*signal을 위한 사용자 데이터*/

unsigned int f_uid;/* 사용자의 UID*/

unsignen int f_gid;/*사용자의 GID*/

int f_error;/*에러코드*/

struct file_ra_statef_ra;/*read_ahead상태*/

unsigned longf_version;/*버전 번호*/

void *f_security; /*보안 모듈*/

void *private_data;/*tty 드라이버 훅(hook)*/

struct list_headf_ep_links;/* eventpoll 링크드리스트*/

spinlock_tf_ep_lock;/* eventpoll 락(lock)*/

struct address_space*f_mapping;/*페이지 캐시 매핑*/

};

○ 덴트리 객체와 유사하게 파일 객체는 디스크의 어떤 데이터에도 대응되지 않는다. 따라서 파일 객체에는

객체가 수정되어 디스크로 써져야 하는지를 나타내기 위한 어떠한 플래그도 존재하지 않는다. 파일 객체는

포인터 f_dentry를 이용하여 해당 객체와 연관된 덴트리 객체를 가리킨다. 이 덴트리 객체는 다시 해당 inode를

가리킨다.

 

■ 파일 연산

○ 파일 객체와 연관돼있는 연산들은 표준 유닉스 시스템콜의 기본을 구성하는 잘 알려진 시스템콜들이다.

○ 파일 객체 함수는 <linux/fs.h>에 정의된 file_operations 구조체에 명시돼있다.

○ 구조체

struct file_operations {

 

struct module *owner;

loff_t (*llseek) (struct file *, char *, loff_t, int);

ssize_t (*read) (struct file*, char *, size_t, loff_t *);

ssize_t (*aio_read) (struct kiocb *, char *, size_t, loff_t);

ssize_t (*write) (struct file *, const char *, size_t, loff_t *);

ssize_t (*aio_write) (struct kiocb *, const char *, size_t, loff_t);

int (*readdir) (struct file *, void *, filldir_t);

unsigned int (*poll) (struct file *, sturct poll_table_struct *);

int (*ioctl) ( struct inode *, struct file *, ubsigned int, unsigned long);

int (*mmap) (struct file *, struct vm_area_struct *);

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

int (*flush) (struct file *);

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

int (*fsync) (struct file *, struct dentry *, int);

int (*aio_fsync) (struct kiocb *, int);

int (*fasync) (int, struct file *, int);

int (*lock) (struct file *, int, struct file_lock *);

ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);

ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);

ssize_t (*sendpage) (struct file *, loff_t *, size_t, read_actor_t, void *);

ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);

unsigned long (*get_unmapped_area) (struct file *, unsigned long, unsigned,

long, unsigned long, unsigned long);

int (*check_flags) (int flags);

int (*dir_nitify) (struct file *flip, unsigned long arg);

int (*flock) (struct file *flip, int cmd, struct file_lock *fl);

};

파일 시스템은 각각의 연산들에 대하여 고유의 함수를 구현하거나 가능한 경우 범용함수를 사용한다.

필요하지 않을때는 해당 함수를 NULL로 설정한다.

각각의 연산들을 보면

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

- 파일 포인터를 주어진 오프셋으로 갱신하며 시스템콜 llseek()에 의해서 호출된다.

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

- 주어진 파일의 위치 offset에서 count만큼의 바이트를 buf로 읽어들인다. 이후 파일 포인터는 갱신된다.

이 함수는 시스템콜 read에 의해서 호출

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

- iocb에 명시된 파일에서 count바이트를 buf로 읽어들이는 비동기 읽기동작은 시작한다.

이 함수는 시스템콜 aio_read()에 의해서 호출된다.

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

- 해당 파일의 위치 offset에 buf의 내용을 count 바이트 만큼 기록한다. 이 함수는 write()함수에

의해서 호출된다.

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

- buf의 내용에서 count 바이트만큼을 iocb가 나타내는 파일에 기록하는 비동기 쓰기 동작을 시작한다.

이 함수는 시스템콜 aio_write함수에 의해 호출된다.

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

- 이 함수는 디렉토리 목록의 다음 디렉토리를 반환한다. 이 함수는 시스템콜 readdir()에 의해 호출된다

◇ unsigned int (*poll) (struct file *file, sturct poll_table_struct *poll_table)

- 해당 파일에 특정한 동작이 있을때까지 휴면하며 기다린다. 함수는 시스템콜 poll()에 의해 호출된다.

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

- 디바이스에 명령과 매개변수를 전송한다. 이 함수는 파일이 오픈 디바이스 노드일 때 사용되며,

시스테콜 ioct1()에 의해 호출된다.

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

- 주어진 파일을 주어진 주소공간 메모리 맵핑하며 시스템콜 mmap()에 의해서 호출된다.

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

- 새로운 파일 객체를 생성하고 이를 해당 inode객체에 연결한다. 이 함수는 시스템콜 open()에 의해서 호출된다.

◇ int (*flush) (struct file *file)

- 오픈된 파일의 참조 카운터가 감소할 때마다 VFS에 의해서 호출되며 이 함수의 사용 목적은 파일

시스템마다 다르다.

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

- 파일에 대한 최종 참조가 파괴될 때 VFS에 의해서 호출된다. 예를 들어 파일 서술자를 공유하는

마지막 프로세스가 close()를 호출하거나 종료되는 경우를 들 수 있다. 이 함수의 사용목적은

파일 시스템마다 다르다.

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

- 파일에 대한 모든 캐시된 데이터를 디스크에 쓰기 위해 사용되며 시스템콜 fsync()dp 의해서 호출된다.

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

- iocb와 연관된 파일의 모든 캐시된 데이터를 디스크에 쓰기 위해 사용되며 시스템콜 aio_fsync()에

의해 호출된다.

◇ int (*fasync) (int, struct file *file, int on)

- 비동기 I/O의 시그널 통지를 활성화 시키거나 비활성화시킨다.

◇ int (*lock) (struct file *file, int, struct file_lock *lock)

- 주어진 파일의 파일 락을 조작한다.

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

- 주어진 파일에서 데이터를 읽고 그 결고를 vector로 표현되는 count개의 버퍼들에 저장하기 위해

시스템콜readv()로부터 호출된다. 이후 오프셋은 증가된다.

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

- vector로 표현되는 count개의 버퍼들에서 특정 파일로 쓰기 위해 시스템콜writev()로부터 호출된다.

이후 파일 오프셋은 증가된다.

◇ ssize_t (*sendpage) (struct file *file, loff_t *page, 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)

- 주어진 파일과 매핑하기 위한 미사용 주소 영역을 획득하기 위해 사용된다.

◇ int (*check_flags) (int flags)

- 이 함수는 SETFL명령이 주어졌을때 fcnt1()시스템 콜에 전달될 플래그들의 유효성을 검사하기 위해

사용된다. 많은 VFS동작에 대해서는, 파일 시스템에 check_flags()를 구현할 필요가 없다. 현재로서

NFS에만 구현되어 있다. 이 함수는 해당 파일시스템이 범용 fcnt1()함수에서는 허영하지만 우효하지

않은 SETFL 플래그를 제한할 수 있게 해준다. NFS의 경우, O_APPEND와 O_DIRECT를 함께 쓰는

것은 허용되지 않는다.

◇ int (*flock) (struct file *flip, int cmd, struct file_lock *fl)

- 이 함수는 권고 잠금을 지원하기 위한 flock()시스템콜을 구현하기 위해 사용된다.

□ 파일 시스템과 관련된 자료구조

○ 기본적인 VFS 객체들과 함께, 파일시스템과 관련된 자료들을 다루기 위하여 다른 표준 자료

구조를 사용

○ 다수의 서로 다른 파일시스템을 지원하기 때문에 각 파일시스템의 기능이나 동작을 표현하기

위한 특별한 구조체가 필요

○ struct file_system_type: ext3나 XFS와 같은 파일시스템의 변종을 표현하기 위해 사용

struct file_system_type {

const char*name;//파일시스템의 이름

struct subsystemsubsys;//sysfs 서브시스템 객체

intfs_flags;//파일시스템 타입 플래그

 

//이 함수는 디스크로부터 수퍼블록을 읽어 들이는데 사용된다.

struct super_block *(*get_sb) (strut file_system_type *, int, char *, void *);

 

//이 함수는 수퍼블록에 대한 접근을 종료시키기 위하여 사용된다.

void(*kill_sb)(struct super_block *);

 

struct module *owner;//연관된 모듈 (하나라도 존재하는 경우)

struct file_system_type *next;//리스트의 다음 file_system_type 객체

struct list_head fs_supers;//수퍼블록 객체들의 리스트

};

 

◇ get_sb(): 디스크로부터 수퍼블록을 읽어 들이고 파일시스템이 로드뒜을 때 수퍼블록 객체를 생성하기

위해 사용

◇ 나머지 함수들은 파일 시스템의 속성들을 표현

◇ 파일시스템마다 오직 하나의 file_system_type이 존재

(시스템에 마운트된 파일시스템의 수에 상관없이)

○ 파일시스템의 마운트된 인스턴스를 표현하기 위해 사용

○ 파일 시스템의 특정한 인스턴스, 즉 마운트 포인트(mount point)를 나타내는데 사용

○ vfsmount <linux/mount.h>

struct vfsmount {

struct list_head mnt_hash;//해시 테이블 리스트

struct vfsmount*mnt_parent;//부모 파일시스템

struct dentry*mnt_mountpoint//해당 마운트 포인트의 dentry

struct dentry*mnt_root;//해당 파일시스템의 root의 dentry

struct super_block *mnt_sb;//해당 파일시스템의 수퍼블록

struct list_headmnt_mounts;//자식들(children)의 리스트

struct list_headmnt_child;//자식들의 리스트

atomic_tmnt_count;//사용량 카운터

intmnt_clags;//마운트 플래그들

char*mnt_devname;//디바이스 파일명

struct list_headmnt_list;//서술자들의 리스트

struct list_headmnt_fslink;//파일시스템별 만료 리스트

struct namespace *mnt_namespace//관련 네임스페이스

};

○ vfsmount의 연결 리스트들로 파일 시스템과 다른 모든 마운트 포인트들 간의 관계유지

○ 필드에 마운트에 관한 플래그를 저장

◇ 표준 마운트 플래그 목록

플래그

설명

MNT_NOSUID

해당 파일시스템의 바이너리에 대한 setuid와 setgd 플래그 금지

MNT_NODEV

해당 파일시스템의 디바이스 파일에 대한 접근 금지

MNT_NOEXEC

해당 파일시스템의 바이너리에 대한 실행 금지

- 관리자가 신뢰할 수 없는 이동식 디바이스에 가장 유용 <linux/mount.h>에 정의

 

□ 프로세스와 연관된 자료구조

○ VFS 계층과 시스템의 프로세스를 하나로 묶어줌

○ file_struct <linux/file.h>

◇ 이 테이블에 대한 주소는 프로세서 서술자의 files 항목에 저장

◇ 모든 프로세스마다 열린 파일과 파일 서술자 정보가 들어있음

struct files_struct {

atomic_tcount;//구조체의 사용량 카운트

spinlock_tfile_lock;//해당 구조체를 보호하는 락(lock)

intmax_fds;//파일 객체의 최대 개수

intmax_fdset;//파일 서술자의 최대 개수

struct file **fd;//모든 파일 객체의 배열

fd_set*close_on_exec; //exec() 시 close 하기 위한 파일 서술자

fd_set*open_fds;//오픈된 파일 서술자들에 대한 포인터

fd_setclose_on_exec_init;//exec() 시 close하기 위한 초기파일

fd_setopen_fds_init;//파일 서술자들의 초기 집합

struct file *fd_array[NR_OPEN_DEFAULT]; //파일 객체의 기본 배열

};

◇ fd 배열: 열린 파일 객체의 목록 (기본 fd_array 배열)

◇ NR_OPEN_DEFAULT = 32

◇ 32개 파일 객체에 대한 공간을 포함

◇ 프로세스가 32개 이상의 파일 객체를 여는 경우,

◇ 커널은 새로운 배열을 할당하고 해당 배열의 fd 포인터를 저장

(정적 배열을 사용하여 적당한 수의 파일 객체에 대한 접근은 빠르게 처리)

◇ 시스템관리자는 프리프로세서로 정의된 NR_OPEN_DEFAULT 값을 증가시킬 수 있음

○ fs_struct <linux/fs_struct.h>

◇ 프로세스와 관련된 파일시스템 정보를 포함

◇ 포인터가 프로세스 서술자의 fs 필드에 저장됨

struct fs_struct {

atomic_tcount;//구조체 사용량 카운트

rwlock_tlock;//구조체 보호 락(lock)

intumask;//디폴트 파일 퍼미션

struct dentry*root;//루트 디렉터리의 dentry

struct dentry*pwd;//현재 디렉터리의 dentry

struct dentry*altroot;//alternative 루트에 대한 dentry

struct vfsmount*rootmnt;//루트 디렉터리의 마운트 객체

struct vfsmount*pwdmnt;//현재 디렉터리의 마운트 객체

struct vfsmount *altrootmnt;//alternative 루트 디렉터리에 대한 마운트 객체

};

◇ 현재의 작업 디렉터리와 현 프로세스의 루트 디렉터리를 포함

○ namespace <linux/namespace.h>

◇ 프로세스 서술자의 namespace 필드에 의해 참조

◇ 리눅스 커널 2.4에서는 프로세스마다 네임스페이스가 추가되어서 각 프로세스가 시스템에 마운트된

파일시스템에 대한 유일한 뷰를 가질 수 있도록 해줌

(유일한 루트디렉터리 뿐만 아니라, 필요한 경우 유일한 전체적인 파일시스템의 계층제공)

struct manespace {

atomic_tcount;//구조체 사용량 카운트

struct vfsmount*root;//루트 디렉터리의 마운트 객체

struct list_headlist;//마운트 포인트들의 리스트

struct rw_semaphoresem;//네임스페이스를 보호하는 세마포어

};

- list: 네임스페이스를 구성하는 마운트된 파일시스템의 이중 연결리스트를 명시

○ 자료구조는 각 프로세스 서술자와 연관됨

○ 대부분의 프로세스는 그들의 프로세스 서술자가 유일한 files_struct과 fs_struct 구조체를 가리킴

○ 복제 플래그 CLONE_FILES 또는 CLONE_FS와 함께 생성된 프로세스들에 대해서는 공유

◇ 스레드: 일반적으로 CLONE_FILES과 CLONE_FS 플래그를 사용, 하나의 files_struct과 fs_struct을 공유

◇ 정규 프로세스: 각자 다른 파일시스템 정보와 열린 파일 테이블을 갖음

다중 프로세스 서술자는 동일한 files_struct 또는 fs_struct 구조체를 가리킬 수 있음

각 구조체의 count 멤버는 프로세스가 해당 구조체를 사용하는 동안에 구조체가 파괴되는 것을 막기 위한

참조 카운트를 제공

○ namespace 구조체는 정반대의 방식으로 동작

◇ 기본적으로 모든 프로세스들은 동일 네임스페이스를 공유

(동일한 마운트 테이블로부터 동일한 파일시스템 계층을 참조)

◇ clone()에서 CLONE_NEWNS 플래그가 명시된 경우에만 해당 프로세스는 유일한 namespace 구조체의

사본을 부여받음

◇ 대부분의 프로세스들은 이 플래그를 사용하지 않으므로, 부모의 네임스페이스를 상속

◇ 그러므로 많은 시스템들에선 단 하나의 네임스페이스가 존재

 

□ 리눅스의 파일시스템

○ 약 50개 이상의 파일 시스템을 공식 커널에서 지원

(네이티브(native) 파일시스템(ext2, ext3 등), 네트워크 파일시스템(NFS, Coda 등))

○ VFS 레이어

◇ 여러 종류의 파일시스템들을 각각의 구현, 표준 시스템콜을 이용한 인터페이스란 두 가지 관점에 대한

통일된 프레임워크를 제공함으로써 지원해줌

◇ 리눅스에서의 새로운 파일시스템 구현을 명확하게 해줌

◇ 새로 구현된 파일시스템이 유닉스의 표준 시스템콜을 통하여 자동적으로 상호작용할 수 있도록 해줌

 

Posted by cyj4369
,