Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

117199
1107
135845


[리눅스커널][가상파일시스템] 슈퍼블록 객체란 무엇인가 - (struct super_block, struct super_operations) 13. 가상 파일 시스템

슈퍼블락 객체란 무엇인가?

슈퍼블록 객체는 파일시스템에 대한 메타 정보와 각 파일시스템 별 슈퍼블락 함수 오퍼레이션으로 구성돼 있습니다. 구체적으로 슈퍼블록 객체는 파일시스템 마운트 정보와 실행 플래그를 저장합니다.

예를 들면 디스크 저장 기반 ext4 파일시스템과 시스템 정보를 램에서 출력하는 proc 파일시스템은 서로 다른 슈퍼블록 마운트와 실행 플래그 정보를 확인할 수 있습니다.

슈퍼 블록에 대한 이해를 돕기 위해 간단한 테스트를 해보겠습니다. 다음 경로에 가서 touch 명령어로 파일을 하나 생성합시다.
root@raspberrypi:/home/pi# cd /proc
root@raspberrypi:/proc# touch RPi_VFS.c
touch: cannot touch 'RPi_VFS.c': No such file or directory

문제가 생겼습니다. 파일을 생성할 수 없습니다.
이 이유는 무엇일까요? proc 파일시스템에서 파일이나 디렉토리를 유저가 생성할 수 없도록 설정했기 때문입니다.

proc 파일시스템은 시스템에 대한 모니터링 용도로 /proc 경로에 마운트된 파일시스템이라 유저가 임의로 파일을 생성할 수 없습니다. proc 파일시스템에서는 유저가 임의로 파일을 생성할 수 없을까요? 그 이유는 proc 파일시스템 정보를 표현하는 블록 객체에서 확인할 수 있습니다.

슈퍼 블록 객체는 어떤 자료구조로 구현돼 있을까요? struct super_block 구조체로 확인할 수 있습니다. 슈퍼블록 객체는 파일시스템에 대한 속성 정보를 저장하며 struct super_block 구조체로 표현할 수 있습니다. 예를 들면 ext4, proc, sysfs 파일시스템 별로 각각 슈퍼블럭 객체가 있으며 서로 다른 정보를 포함하고 있습니다. 해당 파일시스템 마운트 옵션과 최대 파일 크기와 같은 정보를 확인할 수 있습니다.

struct super_block 구조체 내 struct super_operations 멤버는 해당 파일 시스템을 처리하는 함수 목록을 알 수 있습니다.

이번 소절에서는 struct super_block 구조체를 설명하고 이후 슈퍼 블록 함수 테이블을 소개합니다.

struct super_block 구조체 분석

struct super_block 구조체 분석으로 슈퍼 블락이 어떤 파일 시스템 정보를 저장하고 있는지 점검하겠습니다. 슈퍼 블록 객체는 다음 경로에 struct super_block 구조체로 선언돼 있습니다.
[https://elixir.bootlin.com/linux/v4.14.70/source/include/linux/fs.h]
struct super_block {
struct list_head s_list; /* Keep this first */
dev_t s_dev; /* search index; _not_ kdev_t */
unsigned char s_blocksize_bits;
unsigned long s_blocksize;
loff_t s_maxbytes; /* Max file size */
struct file_system_type *s_type;
const struct super_operations *s_op;
const struct dquot_operations *dq_op;
const struct quotactl_ops *s_qcop;
const struct export_operations *s_export_op;
unsigned long s_flags;
unsigned long s_iflags; /* internal SB_I_* flags */
unsigned long s_magic;
struct dentry *s_root;
struct rw_semaphore s_umount;
int s_count;
atomic_t s_active;

struct super_block 구조체 멤버 중에 중요한 속성을 알아 보겠습니다. 다음은 테이블을 슈퍼 블록 객체 멤버에 대한 속성을 설명합니다.
타입 멤버 설명
struct list_head s_list; 수퍼 블록을 연결하는 연결 리스트 포인터
dev_t s_dev; 디바이스 식별자
unsigned char s_blocksize_bits 비트 단위의 블럭 크기
unsigned long s_blocksize 바이트 단위의 블럭 크기
loff_t s_maxbytes 파일의 최대 크기
struct file_system_type *s_type     파일시스템 유형
const struct super_operations *s_op     수퍼 블럭 오퍼레이션
const struct dquot_operations *dq_op  사용량을 제한 함수 오퍼레이션
const struct quotactl_ops *s_qcop  사용량을 제어하는 함수 오퍼레이션
const struct export_operations   *s_export_op 파일 시스템 외부 함수 오퍼레이션
unsigned long s_flags 마운트 플래그
unsigned long s_magic 파일시스템의 고유번호(매직넘버)
struct dentry *s_root 파일시스템의 루트 디렉터리에 대한 dentry 객체
struct rw_semaphore s_umount 마운트를 해제할 때 쓰는 세마포어
Int s_count 슈퍼블록 참조 카운터
atomic_t s_active 보조 참조 카운터
const struct xattr_handler    **s_xattr 수퍼 블록 확장 속성 구조체를 가리키는 포인터
struct hlist_bl_head s_anon 익명 dentry들의 리스트 항목
struct block_device *s_bdev  관련 블럭 드라이버 장치를 가리키는 포인터
struct hlist_node s_instances  특정 파일 시스템 유형의 수퍼 블럭 객체들이 
모여있는 리스트를 가리키는 포인터
struct quota_info s_dquot; 사용량 제한 관련 옵션
char s_id[32]; 수퍼블록 블럭 장치의 이름

위 속성 중에 눈여겨볼 필요가 있는 멤버를 살펴보겠습니다.

s_flags

파일 시스템의 실행 옵션을 알 수 있습니다.
[https://elixir.bootlin.com/linux/v4.14.70/source/include/linux/fs.h]
#define SB_RDONLY  1 /* Mount read-only */
#define SB_NOSUID  2 /* Ignore suid and sgid bits */
#define SB_NODEV  4 /* Disallow access to device special files */
#define SB_NOEXEC  8 /* Disallow program execution */
#define SB_SYNCHRONOUS 16 /* Writes are synced at once */
#define SB_MANDLOCK 64 /* Allow mandatory locks on an FS */
#define SB_DIRSYNC 128 /* Directory modifications are synchronous */
#define SB_NOATIME 1024 /* Do not update access times. */
#define SB_NODIRATIME 2048 /* Do not update directory access times */
#define SB_SILENT 32768
#define SB_POSIXACL (1<<16) /* VFS does not apply the umask */
#define SB_KERNMOUNT (1<<22) /* this is a kern_mount call */
#define SB_I_VERSION (1<<23) /* Update inode I_version field */
#define SB_LAZYTIME (1<<25) /* Update the on-disk [acm]times lazily */

proc 파일 시스템의 경우 s_flags는 다음과 같은 매크로의 OR 연산입니다.
SB_RDONLY | SB_NOEXEC | SB_NODIRATIME

proc 파일시스템은 램에서만 시스템 정보는 출력하는 역할을 수행합니다. 커널에서 정의한 함수로만 파일이나 디렉토리를 생성할 수 있는데 대신 유저가 임의로 파일을 생성하거나 지울 수 없습니다. 따라서 s_flags가 SB_RDONLY | SB_NOEXEC | SB_NODIRATIME인 것입니다.

s_iflags

커널 내부 슈퍼블락 동작을 위해 추가된 플래그입니다.
[https://elixir.bootlin.com/linux/v4.14.70/source/include/linux/fs.h]
#define SB_I_CGROUPWB 0x00000001 /* cgroup-aware writeback enabled */
#define SB_I_NOEXEC 0x00000002 /* Ignore executables on this fs */
#define SB_I_NODEV 0x00000004 /* Ignore devices on this fs */
#define SB_I_MULTIROOT 0x00000008 /* Multiple roots to the dentry tree */

다음 경로에서 SB_I_CGROUPWB 매크로가 추가된 이력을 확인할 수 있습니다.
https://lkml.org/lkml/2015/6/16/808

이번 소절에서는 슈퍼 블록 객체 구조체를 알아봤으니 다음 소절에서 이어 슈퍼블록 함수 오퍼레이션에 대해 살펴보겠습니다.

슈퍼 블록 함수 오퍼레이션

수퍼 블럭과 연관된 함수를 호출하는 동작을 수퍼블럭 함수 오퍼레이션이라고 말합니다. 함수 오퍼레이션은 super_operations 자료구조로 표현되는데, 이 자료구조의 주소는 s_op 필드에 포함되어 있습니다. 

슈퍼 블록 객체에서 슈퍼 블락 함수 오퍼레이션 동작이 중요합니다. 파일시스템 별 세부 동작이 슈퍼블록 함수 오퍼레이션에 정의돼 있기 때문입니다.

슈퍼블락 함수 오퍼레이션은 다음 구조체로 선언돼 있습니다.
[https://elixir.bootlin.com/linux/v4.14.70/source/include/linux/fs.h]
struct super_operations {
    struct inode *(*alloc_inode)(struct super_block *sb);
void (*destroy_inode)(struct inode *);

    void (*dirty_inode) (struct inode *, int flags);
int (*write_inode) (struct inode *, struct writeback_control *wbc);
int (*drop_inode) (struct inode *);
void (*evict_inode) (struct inode *);
void (*put_super) (struct super_block *);
int (*sync_fs)(struct super_block *sb, int wait);
int (*freeze_super) (struct super_block *);
int (*freeze_fs) (struct super_block *);
int (*thaw_super) (struct super_block *);
int (*unfreeze_fs) (struct super_block *);
int (*statfs) (struct dentry *, struct kstatfs *);
int (*remount_fs) (struct super_block *, int *, char *);
void (*umount_begin) (struct super_block *);

int (*show_options)(struct seq_file *, struct dentry *);
...
long (*free_cached_objects)(struct super_block *,
    struct shrink_control *);
};

각각 구조체 멤버를 점검하기 전에 슈퍼 블록 함수 오퍼레이션 멤버가 어떻게 동작하는지 알아봅시다.

슈퍼 블락 함수 오퍼레이션 중 한 가지 예를 듭시다.
파일을 생성하면 이에 해당하는 아이노드를 파일시스템에서 생성합니다. 아이노드를 누가 언제 생성하고 삭제할까요? 이는 각 파일시스템별로 지정한 슈퍼블락 함수 오퍼레이션에서 실행합니다.
[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~6번째 줄 코드를 보겠습니다.
sb->s_op->alloc_inode 포인터가 지정돼 있는지 점검하고 alloc_inode 멤버에 저장된 함수를 호출합니다.

각 파일시스템은 각각 수퍼블럭 함수 오퍼레이션을 정의할 수 있습니다. 다음 디버깅 정보는 ext4 파일시스템 슈퍼 블록 함수 오퍼레이션을 Trace32 프로그램으로 확인한 겁니다.
(static struct super_operations) ext4_sops = (
    (struct inode * (*)()) alloc_inode = 0x8035D900 = ext4_alloc_inode,
    (void (*)()) destroy_inode = 0x8035F5D8 = ext4_destroy_inode,
    (void (*)()) dirty_inode = 0x8033543C = ext4_dirty_inode,
    (int (*)()) write_inode = 0x8032F79C = ext4_write_inode,

ext4 파일시스템에서 아이노드를 생성할 때 alloc_inode() 함수를 실행하면 6번째 줄 코드에서 ext4_alloc_inode() 함수를 실행합니다. ext4 파일시스템 슈퍼 블록 오퍼레이션에서 alloc_inode() 함수는 ext4_alloc_inode() 함수로 등록돼 있기 때문입니다.

이렇게 슈퍼 블록 객체에서 슈퍼 블락 함수 오퍼레이션 동작이 중요합니다.
각 파일시스템별로 어떻게 아이노드를 생성하고 해제하고 파일이 변경(dirty)됐을때 어떤 흐름으로 아이노드를 처리하는지 알 수 있기 때문입니다.


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

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


Reference(가상 파일시스템)

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


핑백

덧글

댓글 입력 영역