# 사용자의 task에서 file system으로의 접근
: open(), read(), write(), close(), ... 호출

# POSIX 표준 시스템
: Portable Operating System Interface. 서로 다른 UNIX OS의 공통 API를 정리하여 이식성이 높은 유닉스 응용 프로그램을 개발하기 위한 목적으로 IEEE가 책정한 애플리케이션 인턴페이스 규격.  
-> https://ko.wikipedia.org/wiki/POSIX

- 운영체제와 관련하여 IEEE에서 만들 규약
- UNIX를 고려하여 만들어졌기 때문에 X를 붙임임
- OS들의 호환성을 고려함.
- 시스템콜, 프로세스 환경, 파일/디렉터리, 시스템 데이터베이스, tar압출 포맷 등을 다룸

# VFS
Virtual File System
- POSIX 표준 시스템 호출을 이용
- 파일시스템과 사용자 태스크 사에에 가상층을 도입.
- 인자에 담겨있는 파일의 이름을 보고 파일을 관리하고있는 파일시스템이 무엇인지 판단.
- 해당 파일시스템의 함수를보고 해당하는 함수를 호출.
- 함수로부터 결과를 리턴받아 사용자 태스크에게 전달.

-> VFS를 통해 리눅스에서 다양한 파일시스템을 지원
- UFS, 유닉스 파일시스템
- ext2(3), 리눅스 기본 파일시스템
- NFS(Network File System), SUN에서 개발한 네트워크 파일시스템
- LFS(Log Structured File System), small write에 좋은 성능을 제공하는 파일 시스템
- CODA, CMU대학에서 개발하였으며 disconnected 연산 기능을 제공하는 파일시스템
- iso9660, CD를 위한 파일 시스템
- msdos, VFAT, MS의 DOS 파일 시스템
- 윈도우 NT 파일 시스템
- proc, 커널의 내부 상태를 볼 수 있는 파일 시스템
- sysfs, 장치를 통합 관리하는 파일 시스템

그림 출처. http://e2fsprogs.sourceforge.net/ext2intro.html


# VFS 동작 원리
: 예시. ext2 file system, /dev/hda2, b.txt

- 사용자 태스크가 b.txt라는 인자로 open()시스템 콜을 호출
- VFS가 b.txt의 정보를 저장하기위해 구조체를 만듬
- 생성한 구조체를 인자로 ext2 파일시스템 내부의 open()함수를 호출
- ext2는 자신의 inode table에서 b.txt의 inode를 찾아 해당 정보를 인자로 받은 구조체에 저장하여 리턴
- VFS는 리턴받은 구조체의 내용을 바탕으로 사용자 태스크에게 필요한 정보를 전달

# VFS의 4가지 객체
: VFS에서 다양한 파일시스템과의 호환을 위해 사용하며 사용자 태스크에게 제공할 일관된 인터페이스를 정의

1. 수퍼블럭 객체
: 파일시스템 자신이 관리하는 파티션에 파일시스템 마다 고유한 정보를 수퍼블록에 저장. 이를 관리하기위해  VFS에서 수퍼블럭 객체라는 범용적인 구조체를 정의
- 현재 마운트 되어 사용 중인 파일시스템당 하나씩 주어짐

2. 아이노드 객체
: 특정한 파일과 관련한 정보를 저자하기위해 정의된 구조체
- 파일에 대한 메타데이터 정보를 저장하기 위해 아이노드객체를 생성하고 파일시스템에 특정파일에 대한 정보를 요청하면
  파일 시스템은 자신의 관리영역에서 해당 정보를 아이노드 객체에 저장
- ext2의 경우 해당 파일의 디렉터리 엔트리와 inode를 찾아서 아이노드 객체에 저장
- msdos의 경우 해당 파일의 디렉터리 엔트리를 읽어서 아이노드 객체에 저장

3. 파일 객체
: 태스크가 open한 파일과 연관되어 있는 정보를 관리하기 위해 사용하는 구조체
- 2개 이상의 태스크가 하나의 파일에 접근할 때, 각각의 offset등과 같은 정보를 관리해야함
- 태스크 관련 정보를 유지하는 용도로 파일 객체를 사용
- 각 태스크가 아이노드 객체에 접근하는 동안만 메모리상에 유지

4. 디엔트리 객체
: 사용자 태스크가 파일에 접근하게 하기위해 아이노드 객체와 파일객체의 연결이 필요, 이때 빠른 연결을 위해 캐시역할을 수행하는 객체
   

# task_struct

: 프로세스는 자신이 사용하는 자원, 자원에서 수행되는 수행흐름으로 구성(resource + flow of control). 이를 관리하기 위해 각 프로세스마다 생성하는 자료구조.

- 프로세스, 스레드 마다 생성, 수행 이미지 공유여부 및 스레드 그룹정보를 통해 구분

- 리눅스에서는 프로세스와 쓰레드 모두 커널내부에서 태스크로 관리


fork(), clone(), pthread_create()     -->     sys_clone()     -->    do_fork()    ,task_struct

vfork()                                            -->     sys_vfork()     -->    do_fork()    ,task_struct


cf. task_struct 구조체 내부 필드 중

- pid : 태스크 별로 유일한 값

- tgid : 한 프로세스내의 쓰레드는 동일한 PID를 공유해야한다는 POSIX 표준에 따라 리눅스에서는 Thread Group ID라는 개념을 도입


- files : file_struct라는 자료구조를 가리킴(file_struct의 max_fds : 한 태스크가 오픈할 수 있는 파일의 최대 갯수를 나타내는 변수)

- fd_array : file_struct구조체의 file이라는 자료구조에 대한 포인터를 갖는 배열

유닉스 계열의 운영체제에서 일반적으로 fd, file descriptor라고 부름

fd는 함수의 리턴값을 받는 변수로, fd_array에서 해당되는 인덱스로 사용됨

일반적으로 fd_array[0]은 표준입력(stdin), fd_array[1]은 표준 출력(stdout), fd_array[2]는 표준 에러출력(stderr)으로 설정


# fd_array 

: task_struct의 files변수가 가리키는 file_struct구조체내의 값으로 fd_array의 각 값들은 파일 객체를 가리킴

- 유닉스 운영체제에서는 파일 테이블이라고 부름

- 사용자 태스크에게 fd라는 정수로 파일에 접근할 수 있도록 하는 추상화 계층을 제공하는 변수


- f_dentry : 디엔트리 객체를 가리킴(해당 디엔트리 객체는 다시 연결되어있는 아이노드 객체를 가리킴)

- f_pos : 현재 피일에서 읽거나 쓸 위치를 저장(처음 파일을 오픈하였을 때는 값이 0), lseek호출을 통해 f_pos값을 바꿀 수 있음

- f_op : file_operations라는 자료구조를 가리키는 포인터(8장) 

 

# file_operations와 inode_operations

- file_operations 구조체 : 가상적인 파일연산이 요청되면 커널에서는 요청된 파일의 유형정보 및 파일의 고유 함수를 사용하여 서비스를 제공해야하는데 이때 사용하는 구조

                                     각 파일 유형에 적합한 파일 연산들이 저장되는 변수


inode_operations 구조체 : 아이노드 객체에 있는 i_op 포인터 변수가 inode_operations를 가리킴
사용자가 create(), mkdir() 등 파일시스템의 메타데이터 관련된 연산을 요청하면,
커널은 요청한 연산이 어떤 파일시스템에서 발생하였는지 파악하고,
적절한 파일시스템의 고유한 함수를 사용하여 서비스를 제공하는데 이때 사용하는 구조체

각 파일시스템 유형에 적합한 파일시스템 연산들이 저장되는 변수


=> 리눅스가 다양한 파일과 파일시스템을 지원할 수 있는 비밀병기 



# task_struct와 VFS



그림 출처. http://www.redirfs.org/docs/thesis/


- 파일의 이름을 인자로 시스템콜을 호출

- VFS가 아이노드 객체를 인자로 파일시스템 내부의 해당 함수를 호출

- 필요한 정보를 아이노드 객체에 채운 후 리턴

- VFS는 해당 아이노드 객체를 디엔트리 객체에 연결시킨 후 사용자 태스크 구조와 연결



# f_op

: 파일시스템마다 구현된 함수들이 다른데, 해당되는 파일 유형에 맞는 연산이 등록되는 변수


예.  file -> f_op -> open()

- ext2 : ext2의 open(), linux/fs/ext2/file.c에 구현

- NFS : nfs_file_open()

- 파이프 : fifo_open()

- 장치파일 : chrdev_open() / blkdev_open(), linux/fs/device.c에 구현


# sys_open() 

- 특정파일 각각의 open()함수들의 호출 후 filp_open()함수 리턴

: 태스크에서 현재 사용하지 않는 fd_array의 한 항을 할당

- 해당 항이 생성된 파일 객체를 가리키도록 설정

- 태스크 구조와 VFS이 연결됨


# sys_read()함수 

: 인자로 전달된 fd를 이용해 파일 객체를 찾고 해당 f_op에 등록된 read함수를 호출, linux/fs/reead_write.c에 구현

- 우선 요청 데이터가 캐시에 있는지 찾음

- 캐시에 있으면 디스크에 안가고 바로 데이터를 사용자에게 전달, 없다면 디스크로 접근

- 이때 각 파일시스템은 서로 다른 방식으로 디스크를 관리하므로 각 파일시스템은 서로다른 디스크 연산함수를 사용하며 해당 처리가 필요함

-> 특정 파일시스템에 맞는 디스크 연산 함수를 호출할 수 있도록 inode_operations구조체를 사용

-> include/linux/fs.h에 정의

-> inode와 관련한 연산을 나타내는 변수로 구성(create, lookup, link, mkdir, mknod, readpage...)

-> 즉, 각 파일세스템에 고유한 연산들이 저장



# 파일의 사용과 파일시스템의 사용

- f_op와 i_op : 각 파일과 파일시스템에 고유한 연산을 제어를 전달하는 진입점 역할을 수행하는 변수

- 파일에 접근(새로운 장치를 위한 장치파일 연결)을 위해 디바이스 드라이버를 사용하여 새로운 file_operations 구조를 작성해 커널에 등록

- 파일시스템의 경우 file_operations구조 및 register_filesystem()이라는 커널내부함수를 사용하여 inode_operations구조도 작성하여 커널에 등록


# register_filesystem()

- linux/fs/filesystems.c에 구현

- struct_file_system_type 자료구조를 인자로 받음, include/linux/fs.h에 정의


<항목>

- name : 파일시스템을 나타내는 변수. ext2, ext3 ...

- fs_flags : 속성정보 저장 변수. 물리장치필요여부, 읽기전용여부...

- mount : 수퍼블록을 읽어 파티션을 마운트 하는 함수의 포인터 저장 변수

- 복수개의 file_system_type구조들의 연결을 위한 리스트와 모듈 정보 등

- ...


-  register_filesystem()으로 커널에 등록된 파일시스템은 하나의 file_system_type 자료구조를 가짐

- 커널의 존재하는 모든 file_system_type은 리스트로 연결

- file_systems라는 커널 내의 전역변수가 file_system_type리스트의 시작을 가리킴



# File System의 마운트 요청,

- file_systems에서 검색하여 요청된 file_system_type 자료구조 찾기

- get_sb에 기록된 함수를 호출하여 파일시스템의 수퍼블럭정보를 얻어옴

- 얻어온 수퍼블럭정보를 VFS의 수퍼블럭 객체에 저장

- 수퍼블럭을 읽어 해당 파일시스템의 자세한 정보를 얻어옴

- 이 자세한 정보로 inode_operations, file_operations에 접근이 가능



# VFS의 내부구조

그림 출처. http://www.read.cs.ucla.edu/111/2006fall/notes/lec14



그림 출처. https://www.usenix.org/legacy/event/usenix01/full_papers/kroeger/kroeger_html/node8.html



- 태스크는 시스템 콜 인터페이스로 VFS와 통신

- 태스크가 원하는 내용은 커널내부의 페이지캐시(캐시역할)을 통할수도 있음

- 캐시에 없다면 실제 디스크와 I/O 수행

- 이때 디엔트리 객체와 아이노드 객체를 위한 캐시를 일반데이터블럭을 위한 캐시공간과 분리하여 성능향상을 도모

- VFS내에 존재하는 객체의 연결관계를 이용해 inode_operations, file_operations 구조체의 함수를 호출

- 가상계층과 물리는 실제 디스크와 I/O하는 부분에서는

   커널내의 일반 블록 게층(Generic Block Layter)의 gendisk를 관리하는 블록 디바이스 드라이버에게 I/O 요청을 해야함

- 성능향상을 위해 리눅스는 자체적인 디스크 I/O 스케줄링 알고리즘(3가지 Elevator알고리즘)을 통해 요청을 보냄




참고. 

리눅스 커널 내부구조(책)





'System > Linux Kernel' 카테고리의 다른 글

Device Tree, 리눅스 커널 4.0  (0) 2015.08.29
메모리관리  (0) 2015.08.18
인터럽트 / 트랩  (0) 2015.08.16
리눅스 부팅과정과 런레벨  (0) 2015.08.13
Linux File System  (0) 2015.08.13

+ Recent posts