Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

133199
1107
135861


[리눅스커널][가상파일시스템] 수퍼블록: 슈퍼블록 함수 연산과 시스템 콜 연동 동작 알아보기 13. 가상 파일 시스템

이번 시간에는 슈퍼블록 함수 오퍼레이션이 시스템 콜과 어떻게 연동해서 실행하는지 살펴봅니다.

alloc_inode

파일시스템별로 inode 객체를 생성할 때 호출합니다. 파일시스템별 기능별로 파일을 관리하는 메타 정보도 업데이트합니다.

[선언부]
struct inode *(*alloc_inode)(struct super_block *sb);

파일시스템별로 inode 객체를 생성할 때 호출합니다. 파일시스템별 기능별로 파일을 관리하는 메타 정보도 업데이트합니다.

슈퍼 블락 함수 오퍼레이션으로 alloc_inode 함수 포인터는 다음 alloc_inode() 함수 6번째 줄 코드에서 실행합니다.
[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);

슈퍼 블락 함수 오퍼레이션 멤버와 함수 이름이 같습니다.

alloc_inode() 함수는 언제 호출될까요?
보통 파일을 생성하는 과정에서 sys_open() 시스템 콜 핸들러 하부 루틴으로 alloc_inode() 함수가 실행됩니다.

destroy_inode

아이노드 객체와 각 파일시스템별로 지정한 세부 데이터를 삭제합니다.

[선언부]
void (*destroy_inode)(struct inode *);

슈퍼블락 함수 오퍼레이션 멤버인 destory_inode 함수 포인터는 언제 실행할까요? 다음 destory_inode() 함수 6번째 줄 코드에서 실행합니다.
[https://elixir.bootlin.com/linux/v4.14.70/source/fs/inode.c]
1 static void destroy_inode(struct inode *inode)
2 {
3 BUG_ON(!list_empty(&inode->i_lru));
4 __destroy_inode(inode);
5 if (inode->i_sb->s_op->destroy_inode)
6 inode->i_sb->s_op->destroy_inode(inode);

destroy_inode() 함수는 파일을 삭제할 때 호출됩니다.

dirty_inode

보통 파일 내용이 변경됐을 때 아이노드에 수정 표시를 합니다. 이 과정에서 호출됩니다. 주로 Ext4처럼 디스크에 저장된 저널을 업데이트하는 파일시스템에서 사용합니다. 

[선언부]
void (*dirty_inode) (struct inode *, int flags);

destroy_inode 슈퍼블락 오퍼레이션은 다음 __mark_inode_dirty() 함수에서 실행됩니다.
1 void __mark_inode_dirty(struct inode *inode, int flags)
2 {
3 #define I_DIRTY_INODE (I_DIRTY_SYNC | I_DIRTY_DATASYNC)
4 struct super_block *sb = inode->i_sb;
5 int dirtytime;
6
7 trace_writeback_mark_inode_dirty(inode, flags);
8
9 if (flags & (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_TIME)) {
10 trace_writeback_dirty_inode_start(inode, flags);
11
12 if (sb->s_op->dirty_inode)
13 sb->s_op->dirty_inode(inode, flags);

13번째 줄 코드를 보면 슈퍼블락 파일 오퍼레이션 정보인 s_op에서 dirty_inode 함수 포인터를 실행합니다.

write_inode

첫 번째 인자로 전달되는 아이노드 객체의 내용으로 세부 파일시스템 정보를 업데이트합니다.
여기에서 inode 객체의 i_ino 필드는 디스크에 있는 파일시스템 inode를 가리킵니다. flag 파라미터는 I/O 연산을 동기적으로 처리해야 하는지를 표시합니다. 
[선언부]
int (*write_inode) (struct inode *, struct writeback_control *wbc);

슈퍼 블락 write_inode 함수 오퍼레이션은 write_inode() 함수에서 실행합니다.
[https://elixir.bootlin.com/linux/v4.14.70/source/fs/fs-writeback.c]
1 static int write_inode(struct inode *inode, struct writeback_control *wbc)
2 {
3 int ret;
4
5 if (inode->i_sb->s_op->write_inode && !is_bad_inode(inode)) {
6 trace_writeback_write_inode_start(inode, wbc);
7 ret = inode->i_sb->s_op->write_inode(inode, wbc);
8 trace_writeback_write_inode(inode, wbc);
9 return ret;
10 }
11 return 0;
12}

write_inode() 함수 7번째 줄 코드를 보면 슈퍼 블락 함수 오퍼레이션 멤버인 s_op에서 write_inode() 함수 포인터를 실행합니다.

drop_inode

가상 파일시스템에서 해당 아이노드를 참조하지 않을 때 호출됩니다. 대부분 파일시스템에서 geteric_drop_inode() 함수를 호출해 아이노드를 삭제합니다. 

[선언부]
int (*drop_inode) (struct inode *);

put_super
파일시스템은 언마운트한 후 슈퍼 블록 객체를 제거할 때 호출됩니다.

[선언부]
void (*put_super) (struct super_block *);

슈퍼 블락 put_super 함수 오퍼레이션은 다음 코드와 같이 generic_shutdown_super() 함수에서 실행합니다.
[https://elixir.bootlin.com/linux/v4.14.70/source/fs/super.c]
1 void generic_shutdown_super(struct super_block *sb)
2 {
3 const struct super_operations *sop = sb->s_op;
4
5 if (sb->s_root) {
..
6 if (sop->put_super)
7 sop->put_super(sb);

6번째 줄 코드에서 슈퍼 블락 오퍼레이션 멤버 중 put_super에 함수 포인터가 지정됐는지 점검합니다. put_super에 함수 포인터가 지정됐으면 7번째 줄 코드와 같이 해당 함수를 호출합니다.

sync_fs

파일시스템 상세 정보인 메타 데이터를 디스크에 저장된 자료구조와 동기화를 맞출 때 실행합니다. 보통 ext4와 같은 저널링 파일시스템에서 사용됩니다.

[선언부]
int (*sync_fs)(struct super_block *sb, int wait);

슈퍼 블락 sync_fs 함수 오퍼레이션은 sync_fs_one_sb() 혹은 __sync_filesystem() 함수에서 실행합니다.
[https://elixir.bootlin.com/linux/v4.14.70/source/fs/sync.c]
1 static void sync_fs_one_sb(struct super_block *sb, void *arg)
2 {
3 if (!sb_rdonly(sb) && sb->s_op->sync_fs)
4 sb->s_op->sync_fs(sb, *(int *)arg);
5 }
6
7 static int __sync_filesystem(struct super_block *sb, int wait)
8 {
9 if (wait)
10 sync_inodes_sb(sb);
11 else
12 writeback_inodes_sb(sb, WB_REASON_SYNC);
13
14 if (sb->s_op->sync_fs)
15 sb->s_op->sync_fs(sb, wait);
16 return __sync_blockdev(sb->s_bdev, wait);
17}

sync_fs_one_sb() 함수와 __sync_filesystem() 함수 코드 각각 4번째와 15번째 줄 코드를 눈여겨봅시다. 둘다 sync_fs 멤버에 함수 포인터가 지정됐는지 점검 후 함수 포인터에 지정된 함수를 호출합니다.

statfs

파일시스템 통계 정보를 읽으려 할 때 호출됩니다. 지정한 파일시스템 통계 정보는 struct kstatfs 구조체에 저장됩니다.

[선언부]
int (*statfs) (struct dentry *, struct kstatfs *);

슈퍼 블락 sync_fs 함수 오퍼레이션은 유저 공간에서 statfs 함수를 호출하면 시스템 콜을 실행한 다음에 vfs_statfs() 함수 호출로 수행됩니다.

vfs_statfs() 함수와 statfs_by_dentry() 함수 코드를 분석합시다.
[https://elixir.bootlin.com/linux/v4.14.70/source/fs/statfs.c]
1 int vfs_statfs(const struct path *path, struct kstatfs *buf)
2 {
3 int error;
4
5 error = statfs_by_dentry(path->dentry, buf); 
...
6
7 static int statfs_by_dentry(struct dentry *dentry, struct kstatfs *buf)
8 {
...
9 memset(buf, 0, sizeof(*buf));
10 retval = security_sb_statfs(dentry);
11 if (retval)
12 return retval;
13 retval = dentry->d_sb->s_op->statfs(dentry, buf);  
14 if (retval == 0 && buf->f_frsize == 0)
15 buf->f_frsize = buf->f_bsize;
16 return retval;
17}

vfs_statfs() 함수는 5번째 줄 코드와 같이 statfs_by_dentry() 함수를 호출합니다. 
statfs_by_dentry() 함수 13번째 줄 코드에서 statfs 함수 포인터를 실행합니다.

remount_fs

가상 파일시스템이 파일시스템을 새로운 마운트 옵션으로 다시 마운트할 때 호출합니다.

[선언부]
int (*remount_fs) (struct super_block *, int *, char *);

해당 동작은 do_remount_sb() 함수 6번째 줄 코드에서 수행합니다.
[https://elixir.bootlin.com/linux/v4.14.70/source/fs/super.c]
1 int do_remount_sb(struct super_block *sb, int sb_flags, void *data, int force)
2 {
3 int retval;
4 int remount_ro;
..
5 if (sb->s_op->remount_fs) {
6 retval = sb->s_op->remount_fs(sb, &sb_flags, data);
..
7 }

umount_begin

가상 파일시스템이 마운트 동작을 중단할 때 호출됩니다.

[선언부]
void (*umount_begin) (struct super_block *);

관련 동작은 do_umount() 함수 7번째 줄 코드에서 수행합니다.
[https://elixir.bootlin.com/linux/v4.14.70/source/fs/namespace.c]
1 static int do_umount(struct mount *mnt, int flags)
2 {
3 struct super_block *sb = mnt->mnt.mnt_sb;
4 int retval;
...
5
6 if (flags & MNT_FORCE && sb->s_op->umount_begin) {
7 sb->s_op->umount_begin(sb);
8 }

여기까지 슈퍼 블락 함수 오퍼레이션 멤버의 특징과 해당 함수 오퍼레이션이 수행하는 코드를 살펴봤습니다. 모두 각각 파일시스템별로 아이노드를 생성 및 제거하는 동작에서 파일시스템 재마운트 동작까지 슈퍼 블락 함수 오퍼레이션으로 지원합니다.

실제 슈퍼 블락 함수 오퍼레이션이 제대로 실행하는지 알려면 리눅스 커널에서 실제 함수 호출 흐름을 눈으로 확인할 필요가 있습니다. 하지만 슈퍼 블락 함수 오퍼레이션 동작은 대부분 그 종류만 알아도 될만한 내용들이 많습니다.

다음 소절에서는 슈퍼 블락 함수 오퍼레이션 중 시스템 콜과 연동되서 자주 실행되는 동작을 선택해서 살펴보겠습니다.



"혹시 궁금한 점이 있으면 댓글로 질문 남겨주세요. 아는 한 성실히 답변 올려드리겠습니다!" 

Thanks,
Austin Kim(austindh.kim@gmail.com)


Reference(가상 파일시스템)

가상 파일시스템 소개
파일 객체
파일 객체 함수 오퍼레이션 동작
프로세스는 파일객체 자료구조를 어떻게 관리할까?
슈퍼블록 객체
아이노드 객체
덴트리 객체
가상 파일시스템 디버깅


핑백

덧글

댓글 입력 영역