[Kernel of Linux] 11. File System (1)
Kernel/Theory

[Kernel of Linux] 11. File System (1)

반응형

지난 강의 요약 - Interrupt (3)

IRQ를 처리하는 과정은 크게 두 가지로 나뉜다. Top-Half와 Bottom-Half가 그것이다. Bottom-Half는 소프트웨어가 후속 처리를 위해 건 인터럽트를 처리하는 과정이다.

Bottom-Half를 처리하기 위해 프로그래머가 사용할 수 있는 handler로는 총 3가지가 존재한다. Softirq, Tasklet, Workqueue.

Softirq는 여러 다른 CPU가 동시에 실행이 가능해 concurrency가 높다는 장점이 있지만 코딩하기 복잡하다는 단점이 있고,
Tasklet은 Softirq와는 반대로 같은 Tasklet이 여러 다른 CPU가 동시에 돌릴 수 없다는 단점이 있지만 그만큼 reentrant와 data access 부분을 신경 쓰지 않아도 되니 코딩하기 편하다.
Workqueue는 function을 handler로 등록하는 Softirq, Tasklet과는 달리 process(thread)를 등록한다. 그래서 셋 중 가장 느리고 sleep, schedulable 하지만 process인 만큼 다룰 수 있는 폭이 넓다.

* speed: softirq > tasklet > workqueue


1. About File

분홍색 부분은 커널이다. 커널이 하는 역할은 위로는 processes를 컨트롤하고 아래로는 hardware를 컨트롤하는 것이다. 그리고 process, hardware마다 각자의 data structure가 존재한다. 마찬가지로 file에도 file을 위한 data structure가 존재한다. 일단 임시로 이것을 FCB(File Control Block)이라고 명명하자.

 

2. Metadata for a File

파일을 위한 메타 데이터를 살펴보자. owner, protection, device, content, device driver routines, accessing where now, ... 등의 정보가 있다. 그런데 저기서 offset이 무엇을 의미하는 것일까?

먼저 read() system call의 manual을 보고 read()는 어떻게 사용하는지 알아보자.

전달되는 인자로 fd(어떤 파일인지), buf(읽은 데이터 담을 공간), count(몇 바이트를 읽어올 것인지)가 필요하다. 하지만 여기에 한 가지가 빠졌는데 그것이 바로 "어디서부터 읽을지"다. 그리고 이걸 알려주는 지표가 바로 메타 데이터의 offset이다. 예를 들어 read()로 읽고 난 뒤, 같은 파일을 다시 read() 한다면 읽는 부분을 처음부터가 아니라 previous offset부터 시작해서 읽기 시작한다.

인터넷 검색하면 금방 알 수 있겠지만 그냥 한 번 실습해봤다.

다음과 같이 파일 하나와 이를 읽을 코드를 준비한 후에 결과를 살펴봤다.

결과는 예상대로였고 엔터도 하나의 문자로 인식하고 읽어 들인 것을 확인할 수 있다. 즉 sequential 하게 data를 가져오는 것이다. 만약 임의로 offset을 정해서 access 하고 싶다면 기존에 존재하는 시스템 콜인 lseek()를 사용하면 된다.

이번에는 파일을 저장하는 동작을 살펴보자. storage에 sequential 하게 파일을 저장하게 될 텐데 여기서 생기는 문제점은 중간에 있던 파일들이 삭제되는 경우다. 이렇게 되면 파일은 지웠지만 전체 공간은 차지하게 놔두는 꼴이어서 없는 공간을 있다고 생각하게 되는 현상이 발생할 것이다. hole이 생기는 현상을 external fragmentation(외부 단편화)라고 한다.

이런 문제가 생기는 이유는 파일들이 다 다른 크기를 가지고 있기 때문이다. 이 문제를 해결하기 위해서는 storage를 같은 크기로 다 자른 뒤에 그 크기에 맞게 넣어주면 관리하기 편할 것이다. 그렇지만 다음 섹터를 탐색할 시간이 필요한 만큼 시간적으로 손해가 발생할 수밖에 없다.

흩어진 데이터 섹터들의 포인터들은 file metadata에서 보관하고 있다.

그래서 이걸 이용할 수 있다. 파일을 open 했다고 해서 모든 file의 contents들을 가져오는 것은 비효율적이고 메모리에도 다 적재 못 시키므로 file metadata를 가져와서 다룬다.

그런데 여기서 생길 수 있는 문제가 있다. file metadata를 여러 프로세스에서 복사해서 쓴다고 가정하면 자원에 접근할 때 프로세스마다 서로 다른 operation 요청 시 혼란을 불러일으킬 수 있다.

그래서 file metadata에서 같이 쓸 수 있는 부분은 같이 쓰고 서로 달리 해야만 하는 부분은 따로 쓰면 되도록 만들어놓았다. 여기서 같이 쓸 수 없는 부분이 바로 offset이다.

이를 구분 지어 만들어놓은 data structure가 바로 inode / file이다. offset이라고 하면 file이고, offset을 제외한 나머지 정보들이라고 하면 inode라고 한다.

 

3. Data Structure for a File

위에서 보여주는 그림과 같이 process가 각자 가지고 있는 private info인 file과 공용으로 사용하는 inode가 있다.

inode 구조체를 보면 다음과 같이 구성돼있다. 그리고 마지막에 보면 inode라는 이름으로 배열을 구성해놓았다. 열 수 있는 file의 개수는 NINODE 만큼 열 수 있다는 뜻이다.

마찬가지로 file 구조체도 보게 되면 구성은 위와 같고 NFILE만큼 열 수 있다는 것을 알 수 있다. 가장 중요한 정보는 f_offset이다.

 

4. In Disk

disk에는 inode를 저장하는 공간과 실제 데이터를 저장하는 공간이 있다. inode의 크기는 inode data struct의 크기만큼이므로 고정된 크기를 가지고 있다.

그래서 inode에 순서를 붙일 수 있는데 이를 i-number라고 한다. 만약 disk name과 i-number를 알게 되면 inode를 알 수 있게 되고 거기에 해당하는 content를 access 할 수 있게 된다.

또 하나의 관련된 구조체로는 Device switch table이 있다. devswtab[]라는 array의 형태로 존재하는데 device와 가해질 operation을 알면 해당 driver routine의 starting address를 구할 수 있다.

중요한 특징으로는 이 array로 오기 전까지만 해도 모든 것들이 file(sequence of bytes in Unix)로 추상화되어 있었다. 하지만 오고 나서는 특정 device로 갈라서게 된다. 그러니 여기까지는 device independence 하게 동작했다는 특징을 가지고 있는 것이다.

반응형