System level I/O: File Structure(15)

G·2022년 11월 27일
0

2-2 System programming

목록 보기
13/15

File Structure

  • Unix I/O
  • File and Directory
  • Inode Table and File Table
  • File Descriptor Table
  • Pathname Resolution
  • Unix I/O vs. Standard I/O
  • FILE vs File descriptor
  • Standard I/O functions

Unix I/O Overview

전통적인 유닉스의 오버뷰를 보자. 리눅스는 유닉스 기반이다.
파일은 bytes의 연속이다.
그리고 모든 I/O device들은 파일들로 표현된다.
각각의 파일들은 각자의 역할을 나타내는 타입이 존재한다.

  • Regular file: arbitrary data를 가진다.
  • Directory: 관련된 파일들의 인덱스이다.
  • Socket: 다른 기계에 있는 프로세스와 의사소통하기 위한 파일이다.
  • Character and block devices
    만약 write()나 read() syscall이 호출된다면 내부에서 ID나 type을 보고 각각의 device driver를 호출한다.

파일을 읽거나 쓸 때, current file position이라는기능이존재한다. 이는 만약 커서가 처음부터 20문자열을 읽었다면 커서는 20 문자열 이후에 놓일 것이다. 이를 변경할 수 있는 것이 lseek()이고 이는 random access를 지원한다라고 표현한다.

유닉스는 root file system이란 디렉토리 안에 디렉토리가 있는 형태를 뜻한다 tree 형태로 나타난다.

Regular Files

Regular file이란 arbitrary data를 저장하고 있는 파일을 칭한다.
Regular file은 두 가지로 나뉜다. 둘 다 이진수이지만, Applications은 두 가지로 나뉜다.

  • text files: ASCII code나 Unicode로 이루어진 경우
    텍스트 파일은 리눅스의 \n나 윈도우의 0xa가 사용된다.
  • binary files: 이진파일은 object files, JPEG images 이다.
    커널은 두 가지의 차이점을 모른다.

Directories

  • Directory는 pair의 집합이다. 파일 이름과 inode number의 집합이다.
  • .(dot)은 디렉토리 그 자체이다.
  • ..(dot dot)은 parent directory이다.

Metadata for a File

파일의 실제 contents와 contents에 대한 정보가 Metadata이다.
이는 아래와 같다

  • 파일의 고유 ID
  • 권한: reading, writing, executing
  • Size
  • Time, date, and user identification: 만든 사람
  • Type: 파일의 타입
  • Location: 파일의 위치이다. 큰 파일은 물리적으로 연결되어 저장되지 않기에, 이를 이어서 나타내줄 주소(인덱싱을 위함)가 필요하다.
  • Pos: 커서 위치

Open()

open() 하면, 파일을 사용할거기 때문에 metadata를 disk에서 메모리로 load한다.
이후 사용할 것이면 metadata를 통해 location에 대한 주소를 가져와 실제 contents를 읽어온다.
파일 컨텐츠는 굉장히 클 수 있기 때문에, 메모리에 로딩하지 않는다.

만약 동일한 파일을 여러개의 프로세스가 open한다면 어떻게 될까? 그냥 하나의 loaded된 metadata를 share하면 된다. 만약 여러개의 metadata를 load한다면 효율적이지 않고 일관성 문제가 생긴다.

그러나 metadata를 공유한다해도 커서의 위치를 공유할 수 없다. 각자 서로 다른 부분을 읽어올 수도 있기 때문이다.
이는 변하는 데이터이고 나머지 metadata는 변하지 않는다.
Pos를 제외한 metadata는 Inode struct라 하고, Pos를 File struct라고 한다. (Pos가 공유될 수도 있다.)

Inode struct VS File struct

File struct

file struct는 현재의 file position이 가장 중요한 정보가 된다. 그리고 Inode struct의 포인터가 필요하다. 그리고 이도 공유할 수 있기 때문에 counter란 정보도 존재한다.(이는 file struct를 공유하는 프로세스의 개수이다.)

Inode struct

Inode struct는 index node를 뜻한다.
파일을 위한 metadata가 존재한다.

그리고 이 둘을 저장한 자료구조를 각각 file table, inode table이라 칭한다.

위에서 data block은 실제 contest가 있는 공간, inode list는 inode struct들의 공간이다.
Superblock은 free block이 어딘지, 위치가 어딘지의 집합이고, bootblock은 부팅될 때 모르겟다.

Inode는 파일마다 모두 사이즈가 같다. 그래서 array 형태로 저장할 수 있고 그 index를 i-number라고 칭한다.

File descriptor table

process마다 open한 파일 file struct 주소를 알아야한다. 이를 저장한 공간이 File descriptor table이다.
여기서 0,1,2는 process가 실행될 때, 기본적으로 만들어진다. 이 부분은 standard input/output/error를 위함이다. 키보드, 모니터 등을 위함이라고 생각하면 된다.

(inode는 vnode라는 이름을 사용하기도 한다.)


위의 그림은 하나의 프로세스가 같은 파일을 두 번 open했을 때의 이미지이다.
커서의 위치만 다르다.

위의 사진 stat은 파일 경로와 struct stat(metadata를 가진 구조체)를 인자로 전달하면 metadata에 이를 채워준다.

이후 매크로 함수들과 구조체의 멤버로 정보를 출력해볼 수 있다.

Directory

디렉토리는 다른 파일들과 같지만 실제 contents만 다른 것이다. 이의 contents는 파일들의 inode와 파일 이름이다.

Open("/a/b")

반환값은 file descriptor의 index값이다.
/은 root directory file이다. a/는 a directory file이다. 마지막으로 b를 open한다. b는 무슨 파일인지 모른다.
(root directory의 inode는 이미 메모리에 load 되어 있다.)

root 디렉토리는 2번에 존재한다. 2번에 a의 inode가 있어 이를 통해 a에 접근하고 이를 반복하여 b를 open한다.
data block과 metadata를 open할 때마다 메모리에 load한다.(data block)
metadata load -> inode struct load -> file struct load -> file descriptor table에 저장 마지막으로 file descriptor table index를 반환한다.

파일 오픈은 커널 모드에서 이루어지기 때문에 trap이 발생한다. 비용이 많이 발생한다.

Buffered I/O

모드 스위치를 줄여 효율적으로 I/O를 수행한다. 파일을 read/write 하기 위해 open한다면 입력된 크기보다 좀 더 많이 읽어와 mode switch를 하지 않고 다시 read/write할 때 비용을 줄일 수 있는 I/O이다.

Buffered read


위의 lcoal 버퍼 buf를 만들어 buf의 size가 0이라면 미리 read 해서 한 번에 많이 읽어온다. 그리고 사용자가 읽어올 때, 이 local buf의 값을 전달하면 된다. standard I/O가 다 이런 형식으로 구현되어 있다.
printf()도 마찬가지이다. 미리 buffer에 저장해놓고 \n을 만나면 한 번에 출력한다.(flushed)
위의 사진을 본다면 유저 레벨의 struct FILE을 확인할 수 있다.
이는 local buffer가 핵심 필드이다. 이 구조체는 file descriptor table의 index가 필요하다. 그리고 index를 통해 file struct -> inode struct -> contents순으로 참조하여 값을 미리 추출하여 저장해놓을 수 있다.

profile
열심히 안 사는 사람

0개의 댓글