Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

15192
888
89788


[라즈베리파이] 가상 파일시스템 공통 모델이란 13장. 가상 파일시스템

가상 파일시스템 구조와 동작을 알려면 가상 파일시스템에서 쓰는 자료구조를 이해할 필요가 있습니다. 가상 파일시스템 자료구조를 소개하기 전 자료구조를 왜 생성했는지 생각 해 봅시다.

우리는 리눅스 시스템에서 파일을 열고 디렉토리를 검색하는 동작을 반복합니다. 이 과정에서 파일을 실행할 수 있는 권한이 있는지 해당 파일이 어떤 파일시스템에서 관리하는지 특별히 신경 쓰지 않습니다. 리눅스 가상 파일시스템에서 배경 작업으로 이런 동작을 수행하기 때문입니다.

어떤 파일시스템에서도 파일이나 디렉토리를 찾거나 유효성을 점검하는 공통 패턴이 있습니다. 또한 파일시스템에서 파일을 관리하기 위한 속성이 있습니다. 파일에 대한 공통 속성인 메타 데이터에 대해서 생각해 봅시다. 먼저 파일이 접근한 시간, 수정 시각 그리고 실행 권한을 예로 들 수 있습니다. 물론 파일 이름과 파일의 절대 경로, 상대 경로도 다른 속성 중 하나입니다.

메타 데이터는 파일 속성 정보를 의미합니다. 파일 생성 시간, 실행 권한 등과 같은 정보를 포함합니다.

이렇게 파일시스템 별로 공통으로 저장하는 파일시스템별 파일 속성과 디렉토리 그리고 파일시스템별 메타 데이터를 관리하는 구조체를 가상 파일시스템 공통 파일 모델이라고 말합니다.

그래서 리눅스 커널 가상 파일시스템에서 다양한 파일시스템을 지원하기 위해서 공통으로 파일을 관리할 수 있는 객체를 선언했습니다. 슈퍼블락 객체, 아이노드 객체, 덴트리 객체 그리고 파일 객체입니다. 각 객체별 구조체는 다음 테이블과 같습니다

객체 종류 구조체
슈퍼블락 객체 struct super_block
아이노드 객체 struct inode
덴트리 객체 struct dentry
파일 객체 struct file

가상 파일시스템에서 쓰는 4개 객체는 언제 어떻게 활용되고 접근할까요?
 “/home/pi/sample_text.text”란 파일을 여는 과정을 예로 들겠습니다. 이 동작을 조금 더 상세히 세밀히 분류해 봅시다.

1. 우선 파일이 위치한 디렉토리를 검색합니다. 파일이 위치한 디렉토리를 먼저 검색한 후 해당 파일이 있는지 알 수 있는 것입니다.

2. “/home/pi/” 디렉토리로 이동한 다음 “sample_text.text” 파일을 찾았다고 가정합시다. 이 파일을 열기 전에 파일 형태와 권한 그리고 파일을 어떤 프로그램으로 실행할 수 있는지 점검할 것입니다.

3. “sample_text.text” 파일이 읽기와 쓰기 권한이 있다면 vi 에디터와 같은 텍스트 수정 프로그램을 열어서 텍스트를 입력하거나 저장할 것입니다. 
 
만약 /proc/interrupts 와 같은 특수 파일인 경우는 파일을 열고 쓸 수 있을까요? 시스템 정보를 출력하는 파일은 대부분 읽을 수만 있습니다.

위와 같은 동작은 리눅스 시스템에서 파일을 읽을 때 흔히 쓰는 과정입니다. 

1~3번 단계로 파일을 검색하고 오픈하는 과정을 살펴봤습니다. 리눅스 커널 가상 파일시스템 입장에서는 위 동작을 어떻게 수행할까요? 이번에는 가상 파일시스템 관점에서 생각해 봅시다.
1. 우선 파일이 위치한 디렉토리를 검색할 때 덴트리 객체로 디렉토리 정보를 얻습니다.
디렉토리 위치와 디렉토리 관계를 관리하는 객체를 덴트리 객체라고 하며 모든 디렉토리는 덴트리에서 관리합니다.

이렇게 덴트리 객체는 해당 파일에 대한 특정 디렉토리를 관리하는 속성 정보를 표현합니다.

2. 파일을 찾으면 파일에 대한 상세 정보가 저장돼 있는 아이노드 정보를 읽습니다. 아이노드에는 파일이 수정된 시간과 접근한 시간이 모두 저장돼 있습니다. 또한 파일을 읽고 쓸 수 있는 지에 대한 권한 정보도 포함되어 있습니다.

아이노드 객체는 각 파일 그 자체를 관리하고 표현하는 모든 속성과 동작 정보를 저장하고 있습니다.

3. 파일을 열어서 어떤 텍스트를 입력하고 쓸 때 동작은 파일 객체에 정의돼 있습니다. 파일 속성과 위치에 따라 파일시스템 별로 파일을 쓰거나 읽을 때 가상 파일시스템에서 해당 파일 시스템에서 지원하는 쓰기 읽기 함수로 연결해 줍니다.

파일 객체는 프로세스가 오픈한 파일을 어떻게 다루고 상호 동작하는지에 대한 정보를 표현합니다. 이 정보는 프로세스가 파일을 열고 난 후 파일을 닫을 때까지 커널 메모리에 상주합니다. 

가장 중요한 파일시스템에 대한 상세 정보는 슈퍼블락 객체에서 확인할 수 있습니다. 파일시스템별로 등록된 슈퍼 블락 함수 오퍼레이션으로 아이노드를 생성하고 해제하는 동작을 수행합니다.

여기까지 가상 파일시스템 동작을 살펴보면서 슈퍼블록, 아이노드, 파일 그리고 덴트리 객체에 대해 소개했습니다. 조금 더 구체적으로 각각 객체가 어떻게 동작하는지 알려면 각각 객체별로 등록한 함수 오퍼레이션에 대해 이해할 필요가 있습니다.

다음에 이어각 객체별로 등록한 함수 오퍼레이션에 대해 소개합니다.

함수 오퍼레이션
가상 파일시스템 관련 소스 코드를 보면 함수 포인터 연산을 많이 볼 수 있습니다. 다음 코드를 보면서 함수 포인터를 자주 쓰는 이유를 살펴봅시다.
[https://elixir.bootlin.com/linux/v4.14.70/source/fs/inode.c]
1 static struct inode *alloc_inode(struct super_block *sb)
2 {
3 struct inode *inode;
4
5 if (sb->s_op->alloc_inode)
6 inode = sb->s_op->alloc_inode(sb);

5번째 줄을 보면 if 문으로 sb->s_op->alloc_inode 멤버가 NULL이 아닌지 점검합니다.

왼쪽 변수 순서부터 그 의미를 살펴보겠습니다.

*sb 는 포인터형 변수이고 구조체는 struct super_block입니다. struct super_block 구조체 멤버인 s_op란 멤버에 접근을 합니다. 

struct super_block 구조체 선언부를 같이 보면서 s_op 멤버의 의미를 살펴봅시다.
[https://elixir.bootlin.com/linux/v4.14.70/source/include/linux/fs.h]
1 struct super_block {
2 struct list_head s_list;
...
3 const struct super_operations *s_op;

3번째 줄을 보면 struct super_operations 구조체로 정의된 *s_op란 멤버가 보입니다.
이를 파일시스템별 함수 오퍼레이션이라고 말합니다.

struct super_operations 구조체를 보면서 함수 오퍼레이션의 의미를 확인합시다.
[https://elixir.bootlin.com/linux/v4.14.70/source/include/linux/fs.h]
1 struct super_operations {
2    struct inode *(*alloc_inode)(struct super_block *sb);
3 void (*destroy_inode)(struct inode *);
4
void (*dirty_inode) (struct inode *, int flags);
6 int (*write_inode) (struct inode *, struct writeback_control *wbc);
...

struct super_operations 멤버들은 모두 함수 포인터 연산을 위해 함수 주소를 담고 있습니다.

다시 파일시스템 함수 오퍼레이션 코드로 되돌아 가겠습니다.
5 if (sb->s_op->alloc_inode)
6 inode = sb->s_op->alloc_inode(sb);

struct super_block 구조체에서 struct super_operations 구조체 멤버까지 알아봤으니 6번째 줄 코드의 의미를 알아볼 차례입니다.

6번째 줄 코드는 struct super_operations 구조체 멤버인 alloc_inode가 저장한 함수 주소를 호출하는 것입니다. 만약 ext4 파일시스템인 경우 6번째 줄 코드를 실행하면 ext4_alloc_inode() 함수를 실행하고 proc 파일시스템은 proc_alloc_inode() 함수를 호출합니다.

그 이유는 무엇일까요? 각 파일시스템별로 alloc_inode 멤버에 서로 다른 함수 즉 ext4_alloc_inode() 함수와 proc_alloc_inode() 함수를 지정했기 때문입니다.


alloc_inode() 함수는 어떤 동작을 수행할까요? 파일을 생성할 때 이에 해당하는 아이노드를 생성하는데 이 때 alloc_inode() 함수를 호출합니다. 파일시스템별로 아이노드를 생성하는 방식이 다르니 6번째 줄과 같이 함수 포인터로 함수를 호출합니다.


가상 파일시스템은 파일시스템 계층 위에서 파일시스템별로 등록된 함수를 실행시키는 역할을 수행합니다. 그래서 가상 파일시스템 커널 함수에서 함수 포인터 연산이 많습니다.

여기까지 슈퍼블락 객체의 함수 오퍼레이션을 예를 들어서 설명을 드렸습니다. 
이렇게 각 객체별로 함수 오퍼레이션을 확인할 수 있는데 다음 테이블 내용을 참고합시다.
종류  속성
struct super_operations 특정 파일시스템별로 호출하는 함수
struct inode_operations 특정 파일에 대해 커널이 호출하는 함수
struct file_operations 오픈한 파일에 대해 프로세스가 호출하는 함수
struct dentry_operations 특정 디렉토리 항목에 대해 호출하는 함수

이렇게 가상 파일 시스템을 구현하기 위해 쓰는 4개의 객체들은 가상 파일시스템에서 지원하는 API와 연관이 돼 있습니다. 자료구조와 세부 동작을 잘 알 필요가 있습니다.


#Reference 시스템 콜


Reference(워크큐)
워크큐(Workqueue) Overview


덧글

댓글 입력 영역