Linux文件系统3(基于6.1内核)---VFS数据结构
一、结构体种类
1.1 超级块对象struct super_block *sb
超级块代表了整个文件系统,超级块是文件系统的控制块,有整个文件系统信息,一个文件系统所有的inode都要连接到超级块上,可以说,一个超级块就代表了一个文件系统。每次一个实际的文件系统被安装时,内核会从磁盘的特定位置读取一些控制信息来填充内存中的超级块对象。super_block定义在include/linux/fs.h里
struct super_block {
struct list_head s_list; /*指向超级块链表的指针*/
dev_t s_dev; /* search index; _not_ kdev_t */
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; /*super_block的操作函数集合*/
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; /*指向根目录的dentry结构体*/
struct rw_semaphore s_umount;
int s_count; /*引用计数*/
char s_id[32]; /* 文件系统的名字 */
void *s_fs_info; /* 文件系统的私有信息指针 */
struct list_head s_inodes; /* 文件系统的inode结构体都在这个队列上 */
//......
};
super block操作函数接口:include/linux/fs.h
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 __rcu **(*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);
};
1.2 索引节点对象struct inode * inode
索引节点对象存储了文件的相关信息,代表了存储设备上的一个实际的物理文件。当一个 文件首次被访问时,内核会在内存中组装相应的索引节点对象,以便向内核提供对一个文件进行操 作时所必需的全部信息;这些信息一部分存储在磁盘特定位置,另外一部分是在加载时动态填充的。include/linux/fs.h
struct inode {
umode_t i_mode; /*访问权限控制*/
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; /*inode 操作接口*/
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; /*最后修改(modify)时间*/
struct timespec64 __i_ctime; /*最后改变(change)时间*/
spinlock_t i_lock; /*文件的块数*/
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;
#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_info *i_crypt_info;
#endif
#ifdef CONFIG_FS_VERITY
struct fsverity_info *i_verity_info;
#endif
void *i_private; /* fs or device private pointer */
} __randomize_layout;
1.3 文件对象struct file *file
struct file结构体定义在include/linux/fs.h中定义。文件结构体代表一个打开的文件,系统中的每个打开的文件在内核空间都有一个关联的 struct file。它由内核在打开文件时创建,并传递给在文件上进行操作的任何函数。
在文件的所有实例都关闭后,内核释放这个数据结构。一个文件对应的文件对象可能不是惟一的,但是其对应的索引节点和 目录项对象无疑是惟一的。在内核创建和驱动源码中,struct file的指针通常被命名为file或filp。
/*
* f_{lock,count,pos_lock} members can be highly contended and share
* the same cacheline. f_{lock,mode} are very frequently used together
* and so share the same cacheline as well. The read-mostly
* f_{path,inode,op} are kept on a separate cacheline.
*/
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; /* 读写模式:open的mod_t mode参数 */
atomic_long_t f_count; /* 文件的引用计数(有多少进程打开该文件) */
struct mutex f_pos_lock;
loff_t f_pos; /* 该文件在当前进程中的文件偏移量 */
unsigned int f_flags; /* 这些是文件标志, 例如 O_RDONLY, O_NONBLOCK, 和 O_SYNC */
struct fown_struct f_owner; /* 该结构的作用是通过信号进行I/O时间通知的数据*/
const struct cred *f_cred;
struct file_ra_state f_ra;
struct path f_path; /* 包含dentry和mnt两个成员,用于确定文件路径 */
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 */
对应的函数操作接口:
include/linux/fs.h
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;
1.4 struct dentry 目录项
include/linux/dcache.h
struct dentry {
/* RCU lookup touched fields */
unsigned int d_flags; /* 目录项标志 */
seqcount_spinlock_t d_seq; /* per dentry seqlock */
struct hlist_bl_node d_hash; /* 散列表表项的指针 */
struct dentry *d_parent; /* 父目录的目录项对象 */
struct qstr d_name; /* 文件名 */
struct inode *d_inode; /* 与文件名关联的索引节点 */
unsigned char d_iname[DNAME_INLINE_LEN]; /* 存放短文件名 */
/* 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; /* 文件的超级块对象 */
unsigned long d_time; /* used by d_revalidate */
void *d_fsdata; /* 与文件系统相关的数据 */
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; /* 子目录 */
/*
* 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;
对应的函数操作接口:include/linux/dcache.h
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;
二、数据结构之间的关系
在进程中打开一个文件F,实际上就是要在内存中建立的dentry和inode结构,并让它们与进程结构联系来,把VFS中定义的接口给接起来,可以用如下两图表明:
2.1 进程打开文件
在 Linux 中,当进程打开一个文件时,实际上涉及到多个数据结构的创建和操作。主要的数据结构包括:
- dentry(目录项):用于表示路径名到具体文件的映射。
- inode(索引节点):描述文件的元数据(如权限、大小、文件类型等)。
- file(文件对象):用于进程与文件的交互,每个打开的文件都会在进程中创建一个文件对象。
- 进程结构(task_struct):每个进程都会有一个
task_struct
,它包含该进程的所有信息,包括该进程打开的文件描述符。
进程打开文件的操作:
+-------------------------------+
| 用户调用 open() 或者 fopen() |
| (调用 glibc 库中的函数) |
+-------------------------------+
|
v
+-------------------------------+
| 系统调用进入内核,调用 sys_open() |
| (处理 open 系统调用) |
+-------------------------------+
|
v
+-------------------------------+
| 调用 do_sys_open() |
| (处理打开文件的实际操作) |
+-------------------------------+
|
v
+-------------------------------+
| 检查文件路径是否合法 |
| (路径解析,检查文件权限等) |
+-------------------------------+
|
v
+-------------------------------+
| 查找 dentry(路径解析) |
| (通过路径查找 dentry 对象) |
+-------------------------------+
|
v
+-------------------------------+
| dentry 已存在? |
| (若存在,直接返回 dentry) |
+-------------------------------+
| Yes | No
v v
+-------------------------------+ +-------------------------------+
| 获取 inode(文件的元数据) | | 创建新的 dentry,并查找 inode |
| (从 dentry 中获取 inode) | | (可能需要从磁盘加载 inode) |
+-------------------------------+ +-------------------------------+
| |
v v
+-------------------------------+ +-------------------------------+
| 创建进程的文件描述符 | | 将 inode 与 dentry 绑定 |
| (创建 struct file 对象) | | (将文件的 inode 绑定到路径) |
+-------------------------------+ +-------------------------------+
| |
v v
+-------------------------------+ +-------------------------------+
| 进程结构中保存文件描述符 | | 更新进程的文件描述符表 |
| (task_struct->files) | | (task_struct->files->fd[]) |
+-------------------------------+ +-------------------------------+
|
v
+-------------------------------+
| 返回文件描述符给用户 |
| (用户空间可以使用文件) |
+-------------------------------+
详细步骤解析:
-
用户调用
open()
或fopen()
:- 在用户空间,应用程序调用
open()
或fopen()
等函数请求打开一个文件。这些函数通过 glibc(GNU C 库)进行封装,最终会触发系统调用。
- 在用户空间,应用程序调用
-
系统调用进入内核,调用
sys_open()
:- 用户空间的
open()
调用会触发系统调用sys_open()
,进入内核进行文件操作。
- 用户空间的
-
调用
do_sys_open()
:sys_open()
会进一步调用内核的do_sys_open()
函数,该函数会处理文件的实际打开逻辑。
-
检查文件路径是否合法:
- 内核会对传入的文件路径进行合法性检查,如路径是否有效,是否具有读写权限等。
-
查找 dentry(路径解析):
- 内核首先会根据传入的文件路径查找相应的
dentry
(目录项),这是内核中表示路径名到文件实际位置的映射。
- 内核首先会根据传入的文件路径查找相应的
-
dentry 已存在?:
- 如果该文件路径的
dentry
已经存在(即文件路径已经被解析过并存储),那么内核会直接返回已有的dentry
。 - 如果
dentry
不存在,则需要进行创建,并查找相应的 inode。
- 如果该文件路径的
-
获取 inode:
dentry
对象包含文件路径与文件内容(inode)的映射信息。如果该路径已经解析,内核会直接获取文件的inode
(即文件的元数据,如权限、大小、修改时间等)。- 如果
dentry
不存在,内核会通过文件路径从磁盘加载相应的 inode。
-
创建新的 dentry,并查找 inode:
- 如果
dentry
不存在,内核会通过路径解析,创建一个新的dentry
对象,并查找与该文件路径对应的 inode。如果 inode 不存在,还可能需要从磁盘读取 inode。
- 如果
-
将 inode 与 dentry 绑定:
- 将找到的 inode 与 dentry 对象绑定起来,完成路径到文件内容的映射。
-
创建进程的文件描述符:
- 内核会为当前进程创建一个新的
file
对象,file
结构体会包含文件的 inode、文件操作表(如f_op->read
、f_op->write
等)、文件描述符等信息。
- 内核会为当前进程创建一个新的
-
更新进程结构中的文件描述符表:
- 该
file
对象会被加入到进程的文件描述符表中(task_struct->files
)。进程的文件描述符表(fd[]
)记录着该进程打开的所有文件。
- 该
-
返回文件描述符给用户:
- 文件成功打开后,内核会返回一个文件描述符给用户。用户可以使用该文件描述符来进行文件操作(如读写文件)。
2.2 进程读取文件
当一个进程读取文件时,内核通过以下步骤将 file
、inode
和 dentry
数据结构与进程进行关联,确保文件的读取操作能够顺利执行:
- 用户空间发起读取请求,内核根据进程的文件描述符表获取相应的
file
对象。 file
对象包含了指向inode
的引用,进而通过inode
获取文件的元数据和数据块指针。- 内核使用
inode
查找文件的数据块,将文件内容加载到内存。 - 最终,数据返回给用户空间,完成文件读取。
进程读取文件的操作:
+-------------------------------+
| 用户调用 read() 系统调用 |
| (应用程序请求读取文件) |
+-------------------------------+
|
v
+-------------------------------+
| 系统调用进入内核,调用 sys_read()|
| (内核处理 read 系统调用) |
+-------------------------------+
|
v
+-------------------------------+
| 获取文件描述符对应的 file 对象 |
| (根据 fd 查找进程的 file 对象) |
+-------------------------------+
|
v
+-------------------------------+
| file 结构中包含 inode |
| (file->f_inode) |
+-------------------------------+
|
v
+-------------------------------+
| 获取文件的 inode |
| (inode 包含文件的元数据) |
+-------------------------------+
|
v
+-------------------------------+
| 查找文件数据块 |
| (通过 inode 查找文件的块) |
+-------------------------------+
|
v
+-------------------------------+
| 从磁盘或缓存读取数据块 |
| (加载文件内容到内存中) |
+-------------------------------+
|
v
+-------------------------------+
| 返回读取的数据到用户空间 |
| (返回给用户应用程序) |
+-------------------------------+
详细步骤解析:
-
应用程序调用
read()
系统调用:- 在用户空间,应用程序调用
read()
函数请求读取文件。
- 在用户空间,应用程序调用
-
系统调用进入内核,调用
sys_read()
:read()
调用触发了sys_read()
系统调用,内核开始处理读文件的请求。
-
获取文件描述符对应的文件对象
file
:- 内核根据当前进程的文件描述符表(
task_struct->files->fd[]
),找到对应的file
对象,file
中包含了该文件的inode
和文件操作结构(例如f_op->read
)。
- 内核根据当前进程的文件描述符表(
-
获取文件的
inode
:file
对象中包含对文件inode
的引用,inode
包含了文件的元数据,如文件的大小、权限、位置等。
-
通过
inode
查找文件的物理块:inode
结构中存储了文件的块指针(如直接块、间接块等),内核根据这些指针找到文件的数据块。
-
读取数据块到内存:
- 内核从磁盘或缓存中读取数据块,将数据加载到内存中,并返回给进程。
-
返回读取结果给用户空间:
- 内核将读取的数据复制到用户空间缓冲区,并返回给应用程序。
2.3 进程写入文件
进程写入文件的操作是一个涉及多个数据结构相互配合的复杂过程。在内核中,file
、inode
和 dentry
数据结构通过以下步骤共同作用:
- 应用程序通过
write()
系统调用发起文件写入请求。 - 内核通过进程的文件描述符表找到
file
对象,然后通过file
获取到文件的inode
。 - 如果需要,内核通过
dentry
查找文件的路径,并解析到文件的inode
。 - 文件的
inode
提供文件的元数据和数据块信息,内核使用这些信息将数据写入文件。 - 写入完成后,内核更新
inode
和dentry
中的元数据。
进程写入文件的操作:
+-------------------------------+
| 用户调用 write() 系统调用 |
| (应用程序请求写入文件) |
+-------------------------------+
|
v
+-------------------------------+
| 系统调用进入内核,调用 sys_write() |
| (内核开始处理写请求) |
+-------------------------------+
|
v
+-------------------------------+
| 查找文件描述符对应的 file 对象 |
| (根据 fd 查找进程的 file 对象) |
+-------------------------------+
|
v
+-------------------------------+
| file 结构中包含 inode |
| (file->f_inode) |
+-------------------------------+
|
v
+-------------------------------+
| 查找文件的 dentry |
| (路径解析到文件的 dentry) |
+-------------------------------+
|
v
+-------------------------------+
| 获取文件的 inode |
| (inode 包含文件元数据和块指针) |
+-------------------------------+
|
v
+-------------------------------+
| 通过 inode 查找数据块 |
| (定位文件数据存储位置) |
+-------------------------------+
|
v
+-------------------------------+
| 将数据写入文件的相应数据块 |
| (内核空间复制数据到文件) |
+-------------------------------+
|
v
+-------------------------------+
| 更新 inode 和 dentry |
| (更新元数据,如文件大小等) |
+-------------------------------+
|
v
+-------------------------------+
| 返回写入字节数到用户空间 |
| (应用程序收到写入结果) |
+-------------------------------+
详细步骤解析:
-
应用程序调用
write()
系统调用:- 应用程序通过调用
write()
系统调用来请求将数据写入文件。
- 应用程序通过调用
-
系统调用进入内核,调用
sys_write()
:write()
调用触发了sys_write()
系统调用,内核开始处理写文件的请求。
-
查找文件描述符对应的文件对象
file
:- 内核根据进程的文件描述符表(
task_struct->files->fd[]
)查找对应的file
对象,file
对象包含了该文件的inode
和文件操作结构。
- 内核根据进程的文件描述符表(
-
检查文件的
dentry
和路径:- 如果文件尚未打开,内核通过路径名查找文件的
dentry
,这将帮助解析文件路径并定位文件的inode
。dentry
结构存储了路径和文件的映射信息。
- 如果文件尚未打开,内核通过路径名查找文件的
-
获取文件的
inode
:- 一旦文件的路径解析完成,内核通过
dentry
获取到文件的inode
,inode
中包含文件的元数据(如权限、大小、最后修改时间等)和指向数据块的指针。
- 一旦文件的路径解析完成,内核通过
-
通过
inode
查找文件的数据块:- 内核通过
inode
查找文件的数据块。如果文件已经存在,内核将定位文件的物理存储位置。如果文件不存在,内核会创建一个新的空文件并分配空间。
- 内核通过
-
将数据写入文件:
- 内核从用户空间缓冲区复制数据到内核空间,并将数据写入文件的对应数据块。内核负责管理文件内容的修改和更新。
-
更新文件的
inode
和dentry
:- 写入操作完成后,内核更新
inode
中的元数据(如文件大小、最后修改时间等)。如果文件内容发生变化,dentry
也可能需要更新。
- 写入操作完成后,内核更新
-
返回结果到用户空间:
- 最终,内核将写入的字节数返回给应用程序,操作完成。