리눅스 - 가상 파일 시스템
가상 파일 시스템(Virtual Filesystem)
※ 아직 해당 포스팅 작성이 완료되지 않았으므로 참고만 하기 바람
1. 개요
가상 파일 시스템(Virtual Filesystem, 혹은 Virtual File Switch 라고도 한다)인 VFS는 파일시스템 관련 인터페이스를 사용자 공간 애플리케이션에 제공하고 파일을 구현하는 커널 서브 시스템이다. 모든 파일시스템은 VFS를 통해 공존이 가능할 뿐만 아니라 상호 동작도 가능하다. 요컨대 ext3 파일 시스템을 쓰는 하드 디스크와 ext2 파일 시스템을 사용하는 이동식 디스크라도 VFS를 통해 서로간에 데이터를 옮길 수 있다는 뜻이다.
VFS는 open(), write(), read() 같은 시스템 호출이 파일 시스템이나 물리적 매체의 종류와 상관없이 동작하게 해주는 역할을 한다. 이는 가상 인터페이스를 통해 파일 시스템에 접근하기 때문에 가능한 것으로 과거에는 이런 일이 불가했다. 어떻게 가능하게 되었을까?
이는 리눅스에서 커널이 하위 파일 시스템 인터페이스에 대한 추상화 계층을 제공하기 때문에 가능한 것이다. 리눅스에서 제공하는 기능과 동작이 다른 여러 파일 시스템을 이 추상화 계층을 통해 지원한다. 그렇기 때문에 다른 포맷으로 이루어진 파일 시스템에서 서로간의 데이터를 전달 할 수 있는 것이다. 이 모델은 파일 시스템 하부의 자세한 동작을 이해할 필요가 없게 만들어주었다.
하지만 우리는 VFS의 세세한 구조를 알아야하므로 좀 더 세부적으로 알아보겠다.
2. 유닉스 파일 시스템
기본적으로 VFS는 유닉스 스타일 파일 시스템에 편향된 디자인을 하고 있다. 따라서 우리가 VFS에 대해서 알아보기 위해서는 유닉스 파일 시스템 스타일을 알아야 한다. 역사적으로 유닉스에는 아래의 네 가지 추상화 개념이 있었다.
1) 마운트(mount)
유닉스에서 파일 시스템은 이름공간이라는 전역 계층 구조의 특정 지점에 부착(mount)된다. 이렇게 해서 모든 파일 시스템이 하나의 노드에 나무 가지와 같은 형태로 붙어있다.
2) 파일
바이트가 정렬된 문자열이다.
3) 디렉토리
관련 파일을 모아두는 폴더에 비유 할 수 있음 하위 디렉토리라는 다른 디렉토리가 디렉토리에 들어갈 수 있으므로 중첩하여 경로를 구성할 수 있다.
4) 아이노드(inode)
접근 권한, 크기, 소유자, 생성 시간 같은 파일 관련 정보들은 파일 개념과 분리되어있으며 이런 정보들을 파일 메타데이터라고 한다. 이 정보는 파일과 별도로 존재하는 자료 구조에 저장하는데 이를 아이노드라고 한다. index node의 줄임말이다.
3. VFS 객체와 자료구조
리눅스 커널은 객체지향 언어가 아닌 C로 짜여있지만 객체 지향 프로그래밍을 사용하는 부분이 많다.
특히 VFS의 경우 객체 관점으로 생각하는 것이 좋다. 자료구조는 C 구조체로 짜여있으며 이 구조체에는 데이터와 데이터를 조작하는 파일 시스템 구현 함수 포인터가 들어 있다.
VFS 객체는 아래와 같이 네 가지 유형이 있다.
1) 슈퍼 블록 객체
마운트된 파일 시스템을 표현하는 객체이다. 각 파일 시스템 별로 구현하며 파일 시스템을 기술하는 정보를 저장한다. 이 객체는 보통 디스크의 특별한 섹터에 저장하는 파일 시스템 슈퍼 블록 또는 파일 시스템 제어 블록에 대응된다. (파일 시스템 2 - EXT 포스팅을 참고하면 좋다) 디스크 기반이 아닌 파일 시스템의 경우에는 슈퍼블록을 실시간으로 생성해 메모리에 저장한다.
슈퍼블록 객체는 “include/linux/fs.h” 파일에 정의된 super_block 구조체를 이용하여 표현되어있다. 아는 몇가지 부분은 한글로 주석을 달아두었다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
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; // 활성화 상태 참조 횟수
#ifdef CONFIG_SECURITY
void *s_security; // 보안 모듈
#endif
const struct xattr_handler * const *s_xattr; // 확장 속성 핸들러
#ifdef CONFIG_FS_ENCRYPTION
const struct fscrypt_operations *s_cop;
struct fscrypt_keyring *s_master_keys; /* master crypto keys in use */
#endif
#ifdef CONFIG_FS_VERITY
const struct fsverity_operations *s_vop;
#endif
#if IS_ENABLED(CONFIG_UNICODE)
struct unicode_map *s_encoding;
__u16 s_encoding_flags;
#endif
struct hlist_bl_head s_roots; /* alternate root dentries for NFS */
struct list_head s_mounts; /* list of mounts; _not_ for fs use */
struct block_device *s_bdev; // 관련 블록 디바이스ㄴ
struct bdev_handle *s_bdev_handle;
struct backing_dev_info *s_bdi;
struct mtd_info *s_mtd; // 메모리 디스크 정보
struct hlist_node s_instances; // 같은 파일시스템 인스턴스
unsigned int s_quota_types; /* Bitmask of supported quota types */
struct quota_info s_dquot; /* Diskquota specific options */ // 사용량 제한 관련 옵션
struct sb_writers s_writers;
/*
* Keep s_fs_info, s_time_gran, s_fsnotify_mask, and
* s_fsnotify_marks together for cache efficiency. They are frequently
* accessed and rarely modified.
*/
void *s_fs_info; /* Filesystem private info */
/* Granularity of c/m/atime in ns (cannot be worse than a second) */
u32 s_time_gran;
/* Time limits for c/m/atime in seconds */
time64_t s_time_min;
time64_t s_time_max;
#ifdef CONFIG_FSNOTIFY
__u32 s_fsnotify_mask;
struct fsnotify_mark_connector __rcu *s_fsnotify_marks;
#endif
char s_id[32]; /* Informational name */ // 이름 문자열
uuid_t s_uuid; /* UUID */
unsigned int s_max_links;
/*
* The next field is for VFS *only*. No filesystems have any business
* even looking at it. You had been warned.
*/
struct mutex s_vfs_rename_mutex; /* Kludge */
/*
* Filesystem subtype. If non-empty the filesystem type field
* in /proc/mounts will be "type.subtype"
*/
const char *s_subtype; // 하부 유형 이름
const struct dentry_operations *s_d_op; /* default d_op for dentries */
struct shrinker *s_shrink; /* per-sb shrinker handle */
/* Number of inodes with nlink == 0 but still referenced */
atomic_long_t s_remove_count;
/*
* Number of inode/mount/sb objects that are being watched, note that
* inodes objects are currently double-accounted.
*/
atomic_long_t s_fsnotify_connectors;
/* Read-only state of the superblock is being changed */
int s_readonly_remount;
/* per-sb errseq_t for reporting writeback errors via syncfs */
errseq_t s_wb_err;
/* AIO completions deferred from interrupt context */
struct workqueue_struct *s_dio_done_wq;
struct hlist_head s_pins;
/*
* Owning user namespace and default context in which to
* interpret filesystem uids, gids, quotas, device nodes,
* xattrs and security labels.
*/
struct user_namespace *s_user_ns;
/*
* The list_lru structure is essentially just a pointer to a table
* of per-node lru lists, each of which has its own spinlock.
* There is no need to put them into separate cachelines.
*/
struct list_lru s_dentry_lru; // 미사용 디렉토리 항목 리스트
struct list_lru s_inode_lru;
struct rcu_head rcu;
struct work_struct destroy_work;
struct mutex s_sync_lock; /* sync serialisation lock */
/*
* Indicates how deep in a filesystem stack this SB is
*/
int s_stack_depth;
/* s_inode_list_lock protects s_inodes */
spinlock_t s_inode_list_lock ____cacheline_aligned_in_smp;
struct list_head s_inodes; /* all inodes */
spinlock_t s_inode_wblist_lock;
struct list_head s_inodes_wb; /* writeback inodes */
} __randomize_layout;
이 구조체를 조작하기 위한 함수 역시 있는데 이는 동일한 파일에 super_operations 구조체로 표현된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
struct super_operations {
struct inode *(*alloc_inode)(struct super_block *sb);
void (*destroy_inode)(struct inode *);
void (*free_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 *, enum freeze_holder who);
int (*freeze_fs) (struct super_block *);
int (*thaw_super) (struct super_block *, enum freeze_holder who);
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 *);
int (*show_devname)(struct seq_file *, struct dentry *);
int (*show_path)(struct seq_file *, struct dentry *);
int (*show_stats)(struct seq_file *, struct dentry *);
#ifdef CONFIG_QUOTA
ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t);
ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);
struct dquot **(*get_dquots)(struct inode *);
#endif
long (*nr_cached_objects)(struct super_block *,
struct shrink_control *);
long (*free_cached_objects)(struct super_block *,
struct shrink_control *);
void (*shutdown)(struct super_block *sb);
};
※ 함수 설명
alloc_inode
이 메서드는 alloc_inode()
에 의해 호출되어 struct inode
의 메모리를 할당하고 초기화한다. 이 함수가 정의되지 않은 경우 단순한 ‘struct inode’가 할당된다. 일반적으로 alloc_inode
는 ‘struct inode’를 포함하는 더 큰 구조를 할당하는 데 사용된다.
destroy_inode
이 메서드는 destroy_inode()
에 의해 호출되어 struct inode
에 대해 할당된 리소스를 해제한다. 이는 ->alloc_inode
가 정의된 경우에만 필요하며, ->alloc_inode
에서 수행된 작업을 되돌리는 역할을 한다.
free_inode
이 메서드는 RCU 콜백에서 호출된다. ->destroy_inode
에서 call_rcu()
를 사용하여 ‘struct inode’ 메모리를 해제하는 경우, 이 메서드에서 메모리를 해제하는 것이 더 적합하다.
dirty_inode
이 메서드는 VFS에 의해 inode가 더티 상태로 표시될 때 호출된다. 이는 inode 자체가 더티 상태로 표시될 때만 해당하며, 데이터와는 관련이 없다. 만약 fdatasync()
를 통해 업데이트가 유지되어야 한다면, I_DIRTY_DATASYNC
플래그가 설정된다. Lazytime이 활성화된 경우, struct inode
에 마지막 ->dirty_inode
호출 이후 시간이 업데이트되었다면 I_DIRTY_TIME
플래그가 설정된다.
write_inode
이 메서드는 VFS가 inode를 디스크에 기록해야 할 때 호출된다. 두 번째 매개변수는 쓰기가 동기적으로 이루어져야 하는지 여부를 나타내며, 모든 파일 시스템이 이 플래그를 확인하는 것은 아니다.
drop_inode
inode에 대한 마지막 접근이 해제될 때 호출되며, 이때 inode->i_lock
스핀락이 유지된다.
이 메서드는 NULL(일반적인 UNIX 파일 시스템 동작) 또는 “generic_delete_inode”(inode를 캐시하지 않으려는 파일 시스템에 사용) 중 하나여야 한다. “generic_delete_inode”를 사용하면 i_nlink 값에 상관없이 항상 “delete_inode”가 호출된다.
“generic_delete_inode()”의 동작은 과거에 put_inode()의 경우에 사용되던 “force_delete” 관행과 유사하지만, “force_delete()” 접근 방식에서 발생하던 경합(race condition)을 방지한다.
evict_inode
VFS가 inode를 제거하려고 할 때 호출된다. 호출자는 페이지 캐시나 inode 관련 메타데이터 버퍼를 제거하지 않으며, 메서드가 truncate_inode_pages_final()
을 사용하여 이를 제거해야 한다. 비동기 쓰기 작업이 inode에서 실행되지 않도록 보장한 상태에서 호출된다. 필수는 아니다.
put_super
VFS가 슈퍼블록을 해제(언마운트)하려고 할 때 호출된다. 슈퍼블록 잠금이 유지된 상태에서 호출된다.
sync_fs
VFS가 슈퍼블록과 연관된 모든 더티 데이터를 기록하려고 할 때 호출된다. 두 번째 매개변수는 쓰기 완료를 대기해야 하는지 여부를 나타낸다. 필수는 아니다.
freeze_super
->freeze_fs 콜백 대신 호출된다. 주요 차이점은 ->freeze_super가 down_write(&sb->s_umount)
를 사용하지 않고 호출된다는 것이다. 파일 시스템이 이를 구현하고 ->freeze_fs도 호출되길 원한다면, 이 콜백에서 명시적으로 ->freeze_fs를 호출해야 한다. 필수는 아니다.
freeze_fs
VFS가 파일 시스템을 잠그고 일관된 상태로 만드는 경우 호출된다. 현재 논리 볼륨 관리자(LVM)와 ioctl(FIFREEZE)에서 사용된다. 필수는 아니다.
thaw_super
->freeze_super 이후 파일 시스템을 다시 쓰기 가능 상태로 전환할 때 VFS에서 호출된다. 필수는 아니다.
unfreeze_fs
->freeze_fs 이후 파일 시스템을 다시 쓰기 가능 상태로 전환할 때 VFS에서 호출된다. 필수는 아니다.
statfs
VFS가 파일 시스템 통계를 가져올 때 호출된다.
remount_fs
파일 시스템이 다시 마운트될 때 호출된다. 커널 잠금 상태에서 호출된다.
umount_begin
VFS가 파일 시스템을 언마운트하려고 할 때 호출된다.
show_options
/proc/
show_devname
필수가 아니다. VFS가 /proc/
show_path
필수가 아니다. VFS가 /proc/
show_stats
필수가 아니다. VFS가 /proc/
quota_read
VFS가 파일 시스템 쿼터 파일에서 읽기 위해 호출한다.
quota_write
VFS가 파일 시스템 쿼터 파일에 쓰기 위해 호출한다.
get_dquots
쿼터가 특정 inode에 대한 ‘struct dquot’ 배열을 가져오기 위해 호출한다. 필수는 아니다.
nr_cached_objects
파일 시스템에 sb 캐시 축소 기능이 해당 파일 시스템에 있는 캐시된 해제 가능한 객체 수를 반환하기 위해 호출한다. 필수는 아니다
free_cache_objects
파일 시스템 sb 캐시 축소 기능이 주어진 수의 객체를 스캔하여 해제하려고 시도할 때 호출된다. 필수는 아니지만 이 메서드를 구현하는 파일 시스템은 ->nr_cached_objects도 구현해야 한다.
- 구현에는 스캔 루프 내부에서 조건부 재스케줄 호출이 포함되어야 한다.
- VM이 GFP_NOFS 조건에서 메모리를 회수하려고 할 때는 호출되지 않는다.
- 오류를 처리할 수 없으므로 반환 타입은 void이다.
2) 아이노드 객체
파일을 표현하는 객체이다. 위에서 언급했듯이 메타데이터를 표현하는 객체이며, 커널이 파일이나 디렉토리를 관리하는데 필요한 모든 정보를 담고 있따. 유닉스 스타일의 시스템에서는 간단히 디스크상의 아이노드를 읽기만하면 되지만 그런 시스템이 아니라면 어딘가 저장되어있는 데이터를 읽어들여야하는데 아이노드가 없는 시스템이라면 파일과 함께 저장하는 경우가 일반적이다.
이러한 아이노드 객체는 “include/linux/fs.h”에 정의된 struct inode 구조체를 사용하여 표현한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
struct inode {
umode_t i_mode; // 접근 권한
unsigned short i_opflags;
kuid_t i_uid; // 소유자 사용자 id
kgid_t i_gid; // 소유자 그룹 id
unsigned int i_flags; // 파일시스템 플래그
#ifdef CONFIG_FS_POSIX_ACL
struct posix_acl *i_acl;
struct posix_acl *i_default_acl;
#endif
const struct inode_operations *i_op; // 아이노드 동작 테이블
struct super_block *i_sb; // 아이노드가 속한 슈퍼블록
struct address_space *i_mapping; // 아이노드 관련 연결 정보
#ifdef CONFIG_SECURITY
void *i_security; // 보안 모듈
#endif
/* Stat data, not accessed from path walking */
unsigned long i_ino; // 아이노드 번호
/*
* Filesystems may only read i_nlink directly. They shall use the
* following functions for modification:
*
* (set|clear|inc|drop)_nlink
* inode_(inc|dec)_link_count
*/
union {
const unsigned int i_nlink; // 하드링크 개수
unsigned int __i_nlink;
};
dev_t i_rdev; // 실제 디바이스 노드
loff_t i_size; // 바이트 단위 파일 크기
struct timespec64 __i_atime; // 마지막 접근 시간
struct timespec64 __i_mtime; // 마지막 수정시간
struct timespec64 __i_ctime; /* use inode_*_ctime accessors! */ // 마지막 변경 시간
spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */ // 스핀락
unsigned short i_bytes; // 사용한 바이트
u8 i_blkbits; // 비트 단위 블록 크기
u8 i_write_hint;
blkcnt_t i_blocks; // 블록 단위 파일 크기
#ifdef __NEED_I_SIZE_ORDERED
seqcount_t i_size_seqcount; // i_size 변수 직렬화를 위한 카운터
#endif
/* Misc */
unsigned long i_state; // 상태 플래그
struct rw_semaphore i_rwsem;
unsigned long dirtied_when; /* jiffies of first dirtying */ // 최초변경 시간
unsigned long dirtied_time_when;
struct hlist_node i_hash; // 해시 리스트
struct list_head i_io_list; /* backing dev IO list */
#ifdef CONFIG_CGROUP_WRITEBACK
struct bdi_writeback *i_wb; /* the associated cgroup wb */
/* foreign inode detection, see wbc_detach_inode() */
int i_wb_frn_winner;
u16 i_wb_frn_avg_time;
u16 i_wb_frn_history;
#endif
struct list_head i_lru; /* inode LRU list */
struct list_head i_sb_list; // 슈퍼블록 리스트
struct list_head i_wb_list; /* backing dev writeback list */
union {
struct hlist_head i_dentry; // 디렉토리 항목 리스트
struct rcu_head i_rcu;
};
atomic64_t i_version;
atomic64_t i_sequence; /* see futex */
atomic_t i_count; // 참조 횟수
atomic_t i_dio_count;
atomic_t i_writecount;
#if defined(CONFIG_IMA) || defined(CONFIG_FILE_LOCKING)
atomic_t i_readcount; /* struct files open RO */
#endif
union {
const struct file_operations *i_fop; /* former ->i_op->default_file_ops */ // 기본 아이노드 동작
void (*free_inode)(struct inode *);
};
struct file_lock_context *i_flctx;
struct address_space i_data;
struct list_head i_devices; // 블록 장치 리스트
union {
struct pipe_inode_info *i_pipe; // 파이프 정보
struct cdev *i_cdev; // 캐릭터 장치 드라이버
char *i_link;
unsigned i_dir_seq;
};
__u32 i_generation;
#ifdef CONFIG_FSNOTIFY
__u32 i_fsnotify_mask; /* all events this inode cares about */
struct fsnotify_mark_connector __rcu *i_fsnotify_marks;
#endif
#ifdef CONFIG_FS_ENCRYPTION
struct fscrypt_inode_info *i_crypt_info;
#endif
#ifdef CONFIG_FS_VERITY
struct fsverity_info *i_verity_info;
#endif
void *i_private; /* fs or device private pointer */ // vkdlf tltmxpa soqndyd vhdlsxj
} __randomize_layout;
아이노드는 파일시스템의 각 파일을 나타낸다. 하지만 아이노드 객체는 파일에 접근할 때 메모리에서만 생성된다. 이 객체 역시 함수가 있다. 이 함수는 동일한 파일에 inode_operations 라는 구조체에 정의되어있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
struct inode_operations {
struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
const char * (*get_link) (struct dentry *, struct inode *, struct delayed_call *);
int (*permission) (struct mnt_idmap *, struct inode *, int);
struct posix_acl * (*get_inode_acl)(struct inode *, int, bool); //
int (*readlink) (struct dentry *, char __user *,int);
int (*create) (struct mnt_idmap *, struct inode *,struct dentry *,
umode_t, bool);
int (*link) (struct dentry *,struct inode *,struct dentry *);
int (*unlink) (struct inode *,struct dentry *);
int (*symlink) (struct mnt_idmap *, struct inode *,struct dentry *,
const char *); // 지정된 이름으로 심볼릭 링크를 생성한다.
int (*mkdir) (struct mnt_idmap *, struct inode *,struct dentry *,
umode_t); // 새로운 디렉터리를 생성한다.
int (*rmdir) (struct inode *,struct dentry *);
int (*mknod) (struct mnt_idmap *, struct inode *,struct dentry *,
umode_t,dev_t);
int (*rename) (struct mnt_idmap *, struct inode *, struct dentry *,
struct inode *, struct dentry *, unsigned int);
int (*setattr) (struct mnt_idmap *, struct dentry *, struct iattr *);
int (*getattr) (struct mnt_idmap *, const struct path *,
struct kstat *, u32, unsigned int);
ssize_t (*listxattr) (struct dentry *, char *, size_t);
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
u64 len);
int (*update_time)(struct inode *, int);
int (*atomic_open)(struct inode *, struct dentry *,
struct file *, unsigned open_flag,
umode_t create_mode);
int (*tmpfile) (struct mnt_idmap *, struct inode *,
struct file *, umode_t);
struct posix_acl *(*get_acl)(struct mnt_idmap *, struct dentry *,
int);
int (*set_acl)(struct mnt_idmap *, struct dentry *,
struct posix_acl *, int);
int (*fileattr_set)(struct mnt_idmap *idmap,
struct dentry *dentry, struct fileattr *fa);
int (*fileattr_get)(struct dentry *dentry, struct fileattr *fa);
struct offset_ctx *(*get_offset_ctx)(struct inode *inode);
} ____cacheline_aligned;
※ 함수 설명
create
open(2)
및 creat(2)
시스템 호출에 의해 호출된다.
정규 파일을 지원하려면 필요하다.
전달받은 dentry
에는 inode가 없어야 하며(즉, 음수 dentry여야 함),
여기서 dentry와 새로 생성된 inode를 사용하여 d_instantiate()를 호출할 것이다.
lookup
VFS가 부모 디렉토리에서 inode를 검색해야 할 때 호출된다.
검색할 이름은 dentry
에 포함되어 있다.
이 메서드는 찾은 inode를 dentry
에 삽입하기 위해 d_add()
를 호출해야 하며,
inode 구조체의 i_count
필드를 증가시켜야 한다.
해당 이름의 inode가 존재하지 않을 경우, NULL
inode를 dentry
에 삽입해야 한다.(이를 음수 dentry라고 함).
이 루틴에서 오류 코드를 반환하는 것은 실제 오류가 발생한 경우에만 해야 한다.
그렇지 않으면 create(2)
, mknod(2)
, mkdir(2)
등의 시스템 호출을 통해 inode를 생성할 수 없다.
dentry 메서드를 오버로드하려면 dentry
의 d_dop
필드를 초기화해야 한다.
이 필드는 struct dentry_operations
를 가리키는 포인터이다.
이 메서드는 디렉토리 inode 세마포어가 유지된 상태에서 호출된다.
link
link(2)
시스템 호출에 의해 호출된다.
하드 링크를 지원하려면 필요하다.
create()
메서드와 마찬가지로 d_instantiate()
를 호출해야 할 가능성이 높다.
unlink
unlink(2)
시스템 호출에 의해 호출된다.
inode 삭제를 지원하려면 필요하다.
symlink
symlink(2)
시스템 호출에 의해 호출된다.
심볼릭 링크를 지원하려면 필요하다.
create()
메서드와 마찬가지로 d_instantiate()
를 호출해야 할 가능성이 높다.
mkdir
mkdir(2)
시스템 호출에 의해 호출된다.
서브디렉토리 생성을 지원하려면 필요하다.
create()
메서드와 마찬가지로 d_instantiate()
를 호출해야 할 가능성이 높다.
rmdir
rmdir(2)
시스템 호출에 의해 호출된다.
서브디렉토리 삭제를 지원하려면 필요하다.
mknod
mknod(2)
시스템 호출에 의해 호출되어 디바이스(char, block) inode, FIFO(named pipe),
또는 소켓을 생성한다.
이러한 inode 유형 생성을 지원하려면 필요하다.
create()
메서드와 마찬가지로 d_instantiate()
를 호출해야 할 가능성이 높다.
rename
rename(2)
시스템 호출에 의해 호출되며, 객체를 두 번째 inode와 dentry로 주어진 부모와 이름으로 변경한다.
- 파일 시스템은 지원하지 않거나 알 수 없는 플래그에 대해
-EINVAL
을 반환해야 한다.
현재 구현된 플래그:RENAME_NOREPLACE
:
대상이 이미 존재할 경우, 대상을 대체하지 않고-EEXIST
오류로 실패하도록 지정한다.
VFS가 이미 존재 여부를 확인하므로, 로컬 파일 시스템에서는 일반rename
과 동일한 방식으로 구현된다.RENAME_EXCHANGE
:
소스와 대상을 교환한다.
둘 다 존재해야 하며, 이는 VFS가 확인한다.
일반rename
과 달리, 소스와 대상의 유형이 서로 달라도 가능하다.
get_link
VFS가 심볼릭 링크를 따라가 해당 inode를 찾기 위해 호출한다. 심볼릭 링크를 지원하려면 필요하다. 이 메서드는 탐색할 심볼릭 링크 본문을 반환하며, 필요하면 nd_jump_link()
를 사용해 현재 위치를 재설정할 수도 있다. 해당 본문이 inode가 제거될 때까지 유지되어야 한다면 추가 조치가 필요 없다. 그렇지 않고 본문을 고정해야 한다면, get_link(..., ..., done)
에서 set_delayed_call(done, destructor, argument)
를 사용하여 해제 작업을 설정해야 한다. 이 경우, 반환된 본문 처리가 끝나면 destructor(argument)
가 호출된다.
RCU 모드에서 호출될 수 있으며, 이는 NULL dentry
인수로 표시됩니다. 요청을 RCU 모드에서 처리할 수 없는 경우 ERR_PTR(-ECHILD)
를 반환해야 한다.
파일 시스템이 심볼릭 링크 대상을 ->i_link
에 저장하는 경우 VFS는 ->get_link()
를 호출하지 않고 이를 직접 사용할 수 있다. 하지만 ->get_link()
는 여전히 제공되어야 한다. ->i_link
는 RCU 유예 기간이 끝날 때까지 해제되어서는 안된다. iget() 이후 ->i_link
를 수정하려면 ‘release’ 메모리 배리어가 필요하다.
readlink
현재는 readlink(2)
호출을 오버라이드하여 ->get_link
가 nd_jump_link()
를 사용하는 경우나 객체가 실제로 심볼릭 링크가 아닐 때 사용된다. 일반적으로 파일 시스템은 심볼릭 링크에 대해 ->get_link
만 구현하면 되고, readlink(2)
는 이를 자동으로 사용한다.
permission
POSIX 유사 파일 시스템에서 VFS가 접근 권한을 확인하기 위해 호출한다.
- RCU-Walk 모드(
mask & MAY_NOT_BLOCK
)에서 호출될 수 있다. RCU-Walk 모드에서는 inode를 수정하거나 블로킹 없이 권한을 확인해야 한다. - RCU-Walk 모드에서 처리할 수 없는 상황이 발생하면
-ECHILD
를 반환해야 하며, Ref-Walk 모드에서 다시 호출된다.
setattr
파일 속성을 설정하기 위해 VFS가 호출한다. 이 메서드는 chmod(2)
와 관련된 시스템 호출에서 사용된다.
getattr
파일 속성을 가져오기 위해 VFS가 호출한다. 이 메서드는 stat(2)
와 관련된 시스템 호출에서 사용된다.
listxattr
파일에 대한 모든 확장 속성을 나열하기 위해 VFS가 호출한다. 이 메서드는 listxattr(2)
시스템 호출에서 사용된다.
update_time
inode의 특정 시간이나 i_version을 업데이트하기 위해 VFS가 호출한다. 이 메서드가 정의되지 않은 경우 VFS는 inode를 직접 업데이트하고 mark_inode_dirty_sync
를 호출한다.
atomic_open
open 호출의 마지막 구성 요소에서 호출된다. 이 선택적 메서드를 사용하면 파일 시스템이 파일을 조회하고, 필요하면 생성 및 열기 작업을 하나의 원자적 연산으로 수행할 수 있다. 파일이 심볼릭 링크나 장치 등의 특수한 경우라면 finish_no_open(file, dentry)
를 반환해 호출자에게 열기를 맡길 수 있다.
마지막 구성 요소가 부정적이거나 조회가 필요한 경우에만 호출된다. 파일이 생성되었다면 file->f_mode
에 FMODE_CREATED
플래그를 설정해야 한다. O_EXCL
의 경우 파일이 존재하지 않아야 성공하며, 이 경우 성공 시 항상 FMODE_CREATED
가 설정되어야 한다.
tmpfile
O_TMPFILE
호출의 끝에서 호출된다. 선택적으로 디렉토리에서 파일을 원자적으로 생성, 열기, 언링크하는 것과 동등한다. 성공 시 이미 열린 파일을 반환해야 하며, 이는 finish_open_simple()
을 호출해 수행할 수 있다.
fileattr_get
ioctl(FS_IOC_GETFLAGS)
및 ioctl(FS_IOC_FSGETXATTR)
호출에서 다양한 파일 플래그와 속성을 검색하기 위해 호출된다. 관련 설정 작업 전에 변경 사항을 확인하기 위해 호출될 수도 있다(i_rwsem
이 독점 잠금 상태). 설정되지 않은 경우 f_op->ioctl()
로 대체된다.
fileattr_set
ioctl(FS_IOC_SETFLAGS)
및 ioctl(FS_IOC_FSSETXATTR)
호출에서 다양한 파일 플래그와 속성을 변경하기 위해 호출된다. 호출자는 i_rwsem
을 독점적으로 잠금 상태로 유지한다. 설정되지 않은 경우 f_op->ioctl()
로 대체된다.
get_offset_ctx
디렉터리 inode에 대한 오프셋 컨텍스트를 가져오기 위해 호출된다. 파일 시스템이 이 메서드를 정의해야 simple_offset_dir_operations
을 사용할 수 있다.
3) 덴트리 객체
경로를 구성하는 요소인 디렉토리 항목을 표현하는 객체이다. 이는 VFS가 디렉토리를 파일의 일종으로 간주하기 때문에 이런 별도의 객체가 필요하다. 실질적으로 “/bin/node”라는 경로가 있을때 bin과 node 둘다 파일이다. 물론 여기서 bin은 디렉토리 파일이고 node는 보통 파일이 될 것이다. 아이노드 객체로 각각의 구성요소를 나타낼 수는 있겠으나 경로명 탐색과 같은 디렉토리 전용 작업을 수행해야하는 경우가 많다. 때문에 이런 기능을 구현하기 위해 디렉토리 항목(덴트리)라는 개념을 도입했으며 이는 경로상의 항목을 말한다.
이 덴트리 객체는 “include/linux/dcache.h” 파일에 정의된 struct dentry 구조체를 사용해 표현한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
struct dentry {
/* RCU lookup touched fields */
unsigned int d_flags; /* protected by d_lock */
seqcount_spinlock_t d_seq; /* per dentry seqlock */
struct hlist_bl_node d_hash; /* lookup hash list */
struct dentry *d_parent; /* parent directory */
struct qstr d_name;
struct inode *d_inode; /* Where the name belongs to - NULL is
* negative */
unsigned char d_iname[DNAME_INLINE_LEN]; /* small names */
/* Ref lookup also touches following */
struct lockref d_lockref; /* per-dentry lock and refcount */
const struct dentry_operations *d_op;
struct super_block *d_sb; /* The root of the dentry tree */
unsigned long d_time; /* used by d_revalidate */
void *d_fsdata; /* fs-specific data */
union {
struct list_head d_lru; /* LRU list */
wait_queue_head_t *d_wait; /* in-lookup ones only */
};
struct list_head d_child; /* child of parent list */
struct list_head d_subdirs; /* our children */
/*
* d_alias and d_rcu can share memory
*/
union {
struct hlist_node d_alias; /* inode alias list */
struct hlist_bl_node d_in_lookup_hash; /* only for in-lookup ones */
struct rcu_head d_rcu;
} d_u;
} __randomize_layout;
이 구조체에 대한 함수는 동일한 파일에 dentry_operation 구조체에 정의되어있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct dentry_operations {
int (*d_revalidate)(struct dentry *, unsigned int);
int (*d_weak_revalidate)(struct dentry *, unsigned int);
int (*d_hash)(const struct dentry *, struct qstr *);
int (*d_compare)(const struct dentry *,
unsigned int, const char *, const struct qstr *);
int (*d_delete)(const struct dentry *);
int (*d_init)(struct dentry *);
void (*d_release)(struct dentry *);
void (*d_prune)(struct dentry *);
void (*d_iput)(struct dentry *, struct inode *);
char *(*d_dname)(struct dentry *, char *, int);
struct vfsmount *(*d_automount)(struct path *);
int (*d_manage)(const struct path *, bool);
struct dentry *(*d_real)(struct dentry *, const struct inode *);
} ____cacheline_aligned;
※ 함수 설명
d_revalidate
VFS가 dentry를 다시 검증해야 할 때 호출된다. 이는 이름 조회 시 dcache에서 dentry를 찾을 때마다 호출된다. 대부분의 로컬 파일 시스템에서는 모든 dcache의 dentry가 유효하기 때문에 이 메서드를 NULL로 설정한다. 그러나 네트워크 파일 시스템은 서버에서 변경 사항이 발생할 수 있으므로 다르게 동작한다.
dentry가 여전히 유효하다면 양수를 반환한다. 유효하지 않다면 0 또는 음수 에러 코드를 반환한다.
RCU-Walk 모드(
flags & LOOKUP_RCU
)에서 호출될 수 있다. 이 경우 파일 시스템은 dentry를 블로킹 없이 검증해야 하며, dentry, d_parent, d_inode를 신중하게 사용해야 한다(변경될 수 있고, d_inode는 NULL이 될 가능성도 있음).RCU-Walk 모드에서 처리할 수 없는 상황이 발생하면
-ECHILD
를 반환하고, Ref-Walk 모드에서 다시 호출된다.
d_weak_revalidate
“점프된” dentry를 다시 검증할 때 VFS가 호출한다. 이는 경로 탐색이 부모 디렉토리에서 조회되지 않은 dentry에서 끝날 때 호출된다. 예: “/”, “.”, “..”, procfs 스타일의 심볼릭 링크, 마운트 포인트 트래버설 등.
이 경우, dentry가 완전히 올바른지보다는 inode가 여전히 유효한지에 더 중점을 둔다. d_revalidate와 마찬가지로, 대부분의 로컬 파일 시스템은 dcache의 엔트리가 항상 유효하기 때문에 이를 NULL로 설정한다.
이 함수는 d_revalidate와 동일한 반환 코드 규칙을 따른다.
d_weak_revalidate는 RCU-Walk 모드를 벗어난 이후에만 호출된다.
d_hash
VFS가 dentry를 해시 테이블에 추가할 때 호출된다. 첫 번째 dentry는 이름이 해시될 부모 디렉토리를 나타낸다.
d_compare
와 동일한 잠금 및 동기화 규칙이 적용된다.
d_compare
주어진 이름과 dentry 이름을 비교할 때 호출된다. 첫 번째 dentry는 비교 대상 dentry의 부모이고, 두 번째는 비교될 하위 dentry이다. len
과 name
은 비교될 dentry의 속성을 나타내며, qstr
은 비교할 이름이다.
- 상수적이고 멱등적이어야 한다.
- 가능하면 잠금을 사용하지 말아야 하며, dentry에 저장하지 않아야 한다.
- dentry 외부의 포인터(d_parent, d_inode, d_name 등)를 신중하게 참조해야 한다.
- vfsmount는 고정되어 있고 RCU로 유지되므로, dentries와 inodes는 사라지지 않고, sb나 파일 시스템 모듈도 마찬가지로 사라지지 않는다. ->d_sb를 사용할 수 있다.
- RCU-Walk 모드에서 호출되므로 잠금 없이 동작해야 한다.
d_delete
dentry에 대한 마지막 참조가 제거되고 dcache가 dentry를 삭제할지 여부를 결정할 때 호출된다.
- 즉시 삭제하려면 1을 반환한다.
- 캐시하려면 0을 반환한다.
- 기본값(NULL)은 도달 가능한 dentry를 항상 캐시한다.
- 이 메서드는 상수적이고 멱등적이어야 한다.
d_init
dentry가 할당될 때 호출된다.
d_release
dentry가 실제로 할당 해제될 때 호출된다.
d_iput
dentry가 inode를 잃었을 때(할당 해제 직전) 호출된다. 기본적으로 NULL이면 VFS가 iput()
을 호출한다. 이 메서드를 정의하는 경우 직접 iput()
을 호출해야 한다.
d_dname
dentry의 경로 이름을 생성해야 할 때 호출된다. 일부 가상 파일 시스템(sockfs, pipefs 등)에서는 경로명 생성을 지연시키는 것이 유용하다. (dentry가 생성될 때가 아니라 필요할 때만 생성됨). 실제 파일 시스템은 아마도 이를 사용하지 않을 것인데, 왜냐하면 실제 파일시스템에서 dentries가 글로벌 dcache 해시 테이블에 존재하므로 해시는 불변이어야 하기 때문이다.
- 주의 사항
- 락이 없기 때문에, d_dname()은 적절한 SMP 안전성이 사용되지 않는 한 dentry 자체를 수정하려 해서는 안 된다.
- d_path() 로직은 상당히 복잡할 수 있다. 예를 들어, “Hello”를 반환하는 올바른 방법은 버퍼의 끝에 넣고 첫 번째 문자를 가리키는 포인터를 반환하는 것이다.
dynamic_dname()
헬퍼 함수가 제공된다.
d_automount
자동 마운트 dentry가 탐색될 때 호출된다(선택 사항). 이 함수는 새로운 VFS 마운트 기록을 생성하고 해당 기록을 호출자에게 반환해야 한다. 호출자는 자동 마운트 디렉토리와 자동 마운트 대상을 설명하는 경로 매개변수를 제공받으며, 상위 VFS 마운트 기록을 통해 상속 가능한 마운트 매개변수를 제공한다.
만약 누군가가 먼저 자동 마운트를 설정했다면 NULL을 반환해야 한다. vfsmount 생성에 실패한 경우, 오류 코드를 반환해야 한다. -EISDIR이 반환되면, 디렉토리는 일반 디렉토리로 간주되며 pathwalk로 되돌아가 탐색이 계속된다.
vfsmount가 반환되면 호출자는 해당 마운트 지점에 vfsmount를 시도하며 실패 시 vfsmount를 만료 목록에서 제거한다. vfsmount는 자동 만료를 방지하기 위해 2개의 참조가 있어야 하며, 호출자는 추가 참조를 정리한다.
이 함수는 dentry에 DCACHE_NEED_AUTOMOUNT이 설정된 경우에만 사용된다. 이는 __d_instantiate()에 의해 설정되며, 추가되는 inode에 S_AUTOMOUNT이 설정된 경우이다.
d_manage
파일 시스템이 dentry 전환을 관리하도록 허용하는 함수 (선택사항). 예를 들어, autofs는 ‘마운트포인트’ 뒤의 탐색을 기다리는 클라이언트를 유지하면서 데몬이 그곳을 지나서 하위 트리를 생성할 수 있게 한다. 0을 반환하여 호출 프로세스가 계속 진행되도록 해야 한다.
- -EISDIR을 반환하면 pathwalk가 이 디렉토리를 일반 디렉토리로 사용하도록 하며, 이 디렉토리에 마운트된 항목을 무시하고 자동 마운트 플래그를 확인하지 않는다. 기타 모든 오류 코드는 pathwalk를 완전히 중단시킨다.
- ‘rcu_walk’ 매개변수가 true인 경우, 호출자는 RCU-walk 모드에서 pathwalk을 수행 중이다. 이 모드에서는 sleep 상태로 들어가는 것이 허용되지 않으며, 호출자는 -ECHILD를 반환하여 호출을 종료하고 다시 호출하도록 요청할 수 있다. 또한, -EISDIR이 반환되어 d_automount나 기타 마운트를 무시하도록 할 수 있다.
- 이 함수는 DCACHE_MANAGE_TRANSIT이 설정된 dentry에 대해만 사용된다.
d_real
오버레이/유니온 파일 시스템에서 숨겨진 실제 dentry를 반환하기 위해 구현된다.
- 동작 모드
- 특정 inode와 일치하는 실제 dentry를 반환.
- NULL inode와 함께 호출 시 최상위 실제 dentry 반환.
4) 파일 객체
프로세스가 사용하는 열린 파일을 표현하는 객체이다. 파일 객체는 열린 파일을 메모리 상에서 나타낸 것으로 이 객체는 open() 시스템 호출에 의해 메모리에 만들어지고 close() 시스템 호출로 메모리에서 사라진다. 한 파일을 여러 프로세스에서 사용할 수 있기 때문에 한 개의 파일에 대해 다수의 파일 객체가 있을 수 있다.
파일 객체는 “include/linux/fs.h”에 정의된 struct file 구조체로 표현한다. 여기서 file 구조체는 디스크 상의 실제 데이터가 포함되어있지 않다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
struct file {
union {
struct llist_node f_llist;
struct rcu_head f_rcuhead;
unsigned int f_iocb_flags;
};
/*
* Protects f_ep, f_flags.
* Must not be taken from IRQ context.
*/
spinlock_t f_lock;
fmode_t f_mode;
atomic_long_t f_count;
struct mutex f_pos_lock;
loff_t f_pos;
unsigned int f_flags;
struct fown_struct f_owner;
const struct cred *f_cred;
struct file_ra_state f_ra;
struct path f_path;
struct inode *f_inode; /* cached value */
const struct file_operations *f_op;
u64 f_version;
#ifdef CONFIG_SECURITY
void *f_security;
#endif
/* needed for tty driver, and maybe others */
void *private_data;
#ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct hlist_head *f_ep;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
errseq_t f_wb_err;
errseq_t f_sb_err; /* for syncfs */
} __randomize_layout
__attribute__((aligned(4))); /* lest something weird decides that 2 is OK */
파일 객체에 대한 함수도 역시 동일한 파일에 file_opertaions 구조체로 표현된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
int (*iopoll)(struct kiocb *kiocb, struct io_comp_batch *,
unsigned int flags);
int (*iterate_shared) (struct file *, struct dir_context *);
__poll_t (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
unsigned long mmap_supported_flags;
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
void (*splice_eof)(struct file *file);
int (*setlease)(struct file *, int, struct file_lock **, void **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
unsigned (*mmap_capabilities)(struct file *);
#endif
ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
loff_t, size_t, unsigned int);
loff_t (*remap_file_range)(struct file *file_in, loff_t pos_in,
struct file *file_out, loff_t pos_out,
loff_t len, unsigned int remap_flags);
int (*fadvise)(struct file *, loff_t, loff_t, int);
int (*uring_cmd)(struct io_uring_cmd *ioucmd, unsigned int issue_flags);
int (*uring_cmd_iopoll)(struct io_uring_cmd *, struct io_comp_batch *,
unsigned int poll_flags);
} __randomize_layout;
※ 함수 설명
llseek
VFS가 파일 위치 색인 이동이 필요할 때 호출된다.
read
read(2) 및 관련 시스템 호출에서 호출된다.
read_iter
iov_iter를 목적으로 한 비동기식 읽기.
write
write(2) 및 관련 시스템 호출에서 호출된다.
write_iter
iov_iter를 소스로 사용한 비동기식 쓰기.
iopoll
aio가 HIPRI iocbs에서 완료를 기다릴 때 호출된다.
iterate_shared
VFS가 디렉토리 내용을 읽어야 할 때 호출된다.
poll
프로세스가 파일에서 활동 여부를 확인하고 필요시 대기할 때 VFS에 의해 호출된다. select(2), poll(2) 시스템 호출에서 호출된다.
unlocked_ioctl
ioctl(2) 시스템 호출에서 호출된다.
compat_ioctl
64비트 커널에서 32비트 시스템 호출이 사용될 때 ioctl(2) 시스템 호출에서 호출된다.
mmap
mmap(2) 시스템 호출에서 호출된다.
open
VFS가 inode를 열어야 할 때 호출된다. VFS가 파일을 열 때, 새로운 “struct file”을 생성하고, 새로 할당된 파일 구조체의 open 메소드를 호출한다.
flush
close(2) 시스템 호출에 의해 파일을 플러시할 때 호출된다.
release
열려있는 파일의 마지막 참조가 닫힐 때 호출된다.
fsync
fsync(2) 시스템 호출에서 호출된다. “Handling errors during writeback” 섹션도 참고하라.
fasync
비동기식(비블록 모드)으로 활성화된 파일에 대해 fcntl(2) 시스템 호출에서 호출된다.
lock
F_GETLK, F_SETLK, F_SETLKW 명령어에 대해 fcntl(2) 시스템 호출에서 호출된다.
get_unmapped_area
mmap(2) 시스템 호출에서 호출된다.
check_flags
F_SETFL 명령어에 대해 fcntl(2) 시스템 호출에서 호출된다.
flock
flock(2) 시스템 호출에서 호출된다.
splice_write
VFS가 파이프에서 파일로 데이터를 스플라이스할 때 호출된다. splice(2) 시스템 호출에서 사용된다.
splice_read
VFS가 파일에서 파이프로 데이터를 스플라이스할 때 호출된다. splice(2) 시스템 호출에서 사용된다.
setlease
VFS가 파일 잠금 임대를 설정하거나 해제할 때 호출된다. setlease 구현은 generic_setlease를 호출하여 inode에 lock을 기록하거나 제거해야 한다.
fallocate
VFS가 블록을 미리 할당하거나 구멍을 뚫을 때(punch a hole) 호출된다.
- punch a hole : 파일 내부에 특정 크기의 공간을 비우거나 파일의 중간에 공백을 만드는것
copy_file_range
copy_file_range(2) 시스템 호출에서 호출된다.
remap_file_range
ioctl(2) 시스템 호출에서 FICLONERANGE, FICLONE 및 FIDEDUPERANGE 명령어로 파일 범위를 다시 매핑할 때 호출된다. 구현은 원본 파일에서 pos_in에서 len 바이트를 대상으로 pos_out에 있는 목적 파일로 매핑해야 한다. len == 0인 경우는 “소스 파일의 끝까지 매핑”을 의미한다. 오류가 발생하기 전에 매핑된 바이트 수 또는 일반적인 음수 오류 코드를 반환해야 한다. remap_flags 매개변수는 REMAP_FILE_* 플래그를 수락한다. REMAP_FILE_DEDUP가 설정된 경우 요청한 파일 범위가 동일한 콘텐츠만 매핑해야 한다. REMAP_FILE_CAN_SHORTEN이 설정된 경우 호출자는 요청 길이를 정렬 또는 EOF 요구 사항을 충족하기 위해 단축시키는 것을 허용한다.
fadvise
fadvise64() 시스템 호출에서 호출될 수 있다.
참고문헌
- 리눅스 커널 심층분석 (에이콘 임베디드 시스템프로그래밍 시리즈 33, 로버트 러브 저자(글) · 황정동 번역)
- 리눅스 커널 6.6.7 버전
- 리눅스 커널 소스내 공식 도큐먼트 (Documentation/filesystems/vfs.rst)