Linux 内核inode VS file
在linux 文件系统中inode 和file是两个比较重要的结构,明确这两个结构对理解文件系统非常重要。
inode节点
在linux文件系统中,每个文件都有一个inode,即所谓的索引节点,该节点是从存储介质的角度来描述一个文件,是linux文件系统的重要组成部分,每个文件在存储介质中都有一个唯一的inode节点,来描述其信息:
The inode is an essential component of a UNIX file system and, at the same time, an important component of VFS. An inode is a metadata (it has information about information). An inode uniquely identifies a file on disk and holds information about it (uid, gid, access rights, access times, pointers to data blocks, etc.). An important aspect is that an inode does not have information about the file name (it is retained by the associated struct dentry structure).
在linux文件系统中,一个文件只有一个对应的inode索引节点。当要访问一个文件时,一定要通过它的索引节点知道这个文件是什么类型的文件(比如设备文件等)、是怎样组织的、文件中存储着多少数据,这些数据在什么地方以及其下层的驱动程序在那额等必要的信息。inode索引节点并不会随着文件打开的进行越多而增加,一个文件只对应一个索引节点。
struct inode
inode结构定义在linux/linux/fs.h文件中,在5.8.11内核版本中,主要定义如下:
struct inode {
umode_t i_mode;
unsigned short i_opflags;
kuid_t i_uid;
kgid_t i_gid;
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;
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;
#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 block_device *i_bdev;
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;
inode节点数据成员比较多,主要记录文件权限,大小,日期等等信息。
Linux内核源码分析学习地址:https://ke.qq.com/course/4032547?flowToken=1041043
typedef unsigned short umode_t
- kuid_t i_uid,i_gid:每个文件都有一个“文件主”,最初是创建这个文件的用户,但是可以改变。系统的每个用户都有一个用户号即i_uid,并且都属于某个用户“组”,所以又有个组号gid,kuid_t定义在include/linux/uidgid.h文件中:
typedef struct {
uid_t val;
} kuid_t;
unsigned short i_opflags:记录该节点操作标志位,目前支持的标志位主要如下:
#define IOP_FASTPERM 0x0001
#define IOP_LOOKUP 0x0002
#define IOP_NOFOLLOW 0x0004
#define IOP_XATTR 0x0008
#define IOP_DEFAULT_READLINK 0x0010
- unsigned int i_flags: 该文件 标志位,用户层可以通过ioctl_iflags 进行操作及获取
- const struct inode_operations *i_op:inode节点的操作包括文件打开、修改等等,每个文件系统都有自己的具体操作实现。
- struct super_block *i_sb:超级块。在文件系统中都有一个根目录“/",而每个文件系统相当于格式化成某种文件系统的存储设备上都有一个根目录,比如sysfs文件系统的根目录为/sys而该根目录极为超级块,根目录的位置以及文件系统的其他一些参数就记录在该超级块中,超级块在设备上的逻辑位置都是固定的。对于一个特定的文件系统,超级快的格式都是固定的。系统在初始化时要将一个存储设备作为系统的根设备,它的根目录就成为整个文件系统的"总根”,就是“、”,即相当于把根设备的根目录安装在文件系统的总根“/“节点上。有个根设备以后,就可以i将其他文件系统从存储设备上读入超级块,并在内存中建立起一个super_block结构。
- unsigned long i_ino:inode节点号。在同一个文件系统中每个i节点号都是唯一的,内核中有时会根据i节点号的杂凑值寻找其他inode结构。
- i_nlink:inode节点并没有描述文件名,只有节点号的方式来查询访问文件,这是因为一个文件可能有多个文件名,即可以将一个已经创建的文件”连接“到另外一个文件名,这样同一个inode节点可能有多个文件名,i_nlink计数器用来记住这个文件有多少个这样的连接。
- dev_t i_rdev:如果该索节点是个驱动设备文件,则表示为对应的驱动设备号,即使用mknod命令挂载的索引节点对应的设备号。
- loff_t i_size:文件大小,其定义在include/linux/types.h文件中,
#if defined(__GNUC__)
typedef __kernel_loff_t loff_t;
#endif
__kernel_loff_t 定义如下:
typedef long long __kernel_loff_t;
- struct timespec64 i_atime、 i_mtime、 i_ctime;:最后一次访问该文件的时间、修改文件的时间以及最初创建该文件的时间。
- spinlock_t i_lock:inode节点锁,当对inode操作时需要使用该锁。
- blkcnt_t i_blocks:该文件实际占用了多少块(存储介质),只在quota中使用。
- atomic64_t i_version: inode version
- atomic_t i_count: inode 共享计数,用来防止其他程序来释放掉该文件的cache
- void *i_private:具体的文件系统的私有数据。
其他部分后面再详细讲解。inode结构是VSF文件系统的重要组成成分,它是具体的文件系统节点的公共部分,对上来屏蔽文件系统差异,对一些通用操作进行封装,来实现与具体文件系统无关层。inode节点数据可以分为动态数据和静态数据,比如i_count等随着程序会变化,而文件大小、时间等一些信息与具体文件有关被看成静态数据,静态数据部分最终要通过文件系统写入到存储介质中。
inode_operations
inode为了屏蔽不同文件系统,将针对所有的节点操作全部抽象成接口为inode_operations 索引节点操作,提供了通用的inode节点通用操作以供上层使用,具体的文件系统需要实现这一系列的索引节点操作,封装的操作如下:
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 inode *, int);
struct posix_acl * (*get_acl)(struct inode *, int);
int (*readlink) (struct dentry *, char __user *,int);
int (*create) (struct inode *,struct dentry *, umode_t, bool);
int (*link) (struct dentry *,struct inode *,struct dentry *);
int (*unlink) (struct inode *,struct dentry *);
int (*symlink) (struct inode *,struct dentry *,const char *);
int (*mkdir) (struct inode *,struct dentry *,umode_t);
int (*rmdir) (struct inode *,struct dentry *);
int (*mknod) (struct inode *,struct dentry *,umode_t,dev_t);
int (*rename) (struct inode *, struct dentry *,
struct inode *, struct dentry *, unsigned int);
int (*setattr) (struct dentry *, struct iattr *);
int (*getattr) (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 *, struct timespec64 *, int);
int (*atomic_open)(struct inode *, struct dentry *,
struct file *, unsigned open_flag,
umode_t create_mode);
int (*tmpfile) (struct inode *, struct dentry *, umode_t);
int (*set_acl)(struct inode *, struct posix_acl *, int);
} ____cacheline_aligned;
file
与inode不同的是 内核中的struct file是一个进程级的文件管理结构,这就意味这一个inode节点被多个进程打开时,就会分别在各自的进程内创建一个struct file结构,而且在同一个进程内如果同一个文件同时打开多次也会有多个file,这是inode和file两个不同结构的显著差异。
The inode refers to a file on the disk. To refer an open file (associated with a file descriptor within a process), the struct file structure is used. An inode can have any number of (zero or more) file structures associated (multiple processes can open the same file, or a process can open the same file several times).
struct file
struct file 结构定义在linux/linux/fs.h文件中,在5.8.11内核版本中,主要定义如下:
struct file {
union {
struct llist_node fu_llist;
struct rcu_head fu_rcuhead;
} f_u;
struct path f_path;
struct inode *f_inode; /* cached value */
const struct file_operations *f_op;
/*
* Protects f_ep_links, f_flags.
* Must not be taken from IRQ context.
*/
spinlock_t f_lock;
enum rw_hint f_write_hint;
atomic_long_t f_count;
unsigned int f_flags;
fmode_t f_mode;
struct mutex f_pos_lock;
loff_t f_pos;
struct fown_struct f_owner;
const struct cred *f_cred;
struct file_ra_state f_ra;
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 list_head f_ep_links;
struct list_head f_tfile_llink;
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;
errseq_t f_wb_err;
errseq_t f_sb_err; /* for syncfs */
} __randomize_layout
- f_u: file 组织结构list, 当为RCU时则采用RCU链表
- struct path f_path:该文件所挂载的路径, path结构定义在include/linux/path.h文件中,结构如下:
struct path {
struct vfsmount *mnt;
struct dentry *dentry;
} __randomize_layout;
- struct inode *f_inode: 该文件所指向的inode节点。
- const struct file_operations *f_op:file 文件操作,类似与inode_operations节点操作。
- spinlock_t f_lock:文件锁,对该文件进行操作时需要加锁。
- atomic_long_t f_count:文件引用计数,当系统调用dup()和for()时 由于创建的子进程刚开始会几次父进程的一切,包括文件描述符和所有打开的file,此时子进程并没有创建新的file结构,为了提高fork效率,直接引用了父进程的file结构。调用了fotk预示着该结构与两个进程进行共享,f_count代表的是共享引用计数。
- unsigned int f_flags:文件flags,在文件主要支持的如下:
#define O_CREAT 01000 /* not fcntl */
#define O_TRUNC 02000 /* not fcntl */
#define O_EXCL 04000 /* not fcntl */
#define O_NOCTTY 010000 /* not fcntl */
#define O_NONBLOCK 00004
#define O_APPEND 00010
#define O_DSYNC 040000 /* used to be O_SYNC, see below */
#define O_DIRECTORY 0100000 /* must be a directory */
#define O_NOFOLLOW 0200000 /* don't follow links */
#define O_LARGEFILE 0400000 /* will be set by the kernel on every open */
#define O_DIRECT 02000000 /* direct disk access - should check with OSF/1 */
#define O_NOATIME 04000000
#define O_CLOEXEC 010000000 /* set close_on_exec */
#define __O_SYNC 020000000
#define O_SYNC (__O_SYNC|O_DSYNC)
#define O_PATH 040000000
fmode_t f_mode:文件模式,主要支持的如下,在文件linux/linux/fs.h中定义:
/* file is open for reading */
#define FMODE_READ ((__force fmode_t)0x1)
/* file is open for writing */
#define FMODE_WRITE ((__force fmode_t)0x2)
/* file is seekable */
#define FMODE_LSEEK ((__force fmode_t)0x4)
/* file can be accessed using pread */
#define FMODE_PREAD ((__force fmode_t)0x8)
/* file can be accessed using pwrite */
#define FMODE_PWRITE ((__force fmode_t)0x10)
/* File is opened for execution with sys_execve / sys_uselib */
#define FMODE_EXEC ((__force fmode_t)0x20)
/* File is opened with O_NDELAY (only set for block devices) */
#define FMODE_NDELAY ((__force fmode_t)0x40)
/* File is opened with O_EXCL (only set for block devices) */
#define FMODE_EXCL ((__force fmode_t)0x80)
/* File is opened using open(.., 3, ..) and is writeable only for ioctls
(specialy hack for floppy.c) */
#define FMODE_WRITE_IOCTL ((__force fmode_t)0x100)
/* 32bit hashes as llseek() offset (for directories) */
#define FMODE_32BITHASH ((__force fmode_t)0x200)
/* 64bit hashes as llseek() offset (for directories) */
#define FMODE_64BITHASH ((__force fmode_t)0x400)
- loff_t f_pos: 在文件中偏移位置。
struct file_operations
struct file_operations为文件操作结构,当文件为设备驱动文件时会调用到具体的驱动实现,当为文件时将会调用到具体的文件系统实现:
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, bool spin);
int (*iterate) (struct file *, struct dir_context *);
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 *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
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);
int (*setlease)(struct file *, long, 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);
file descriptor文件描述符
既然file是一种进程级别的资源,当一个进行打开多个文件时就需要对file进行管理,而linux内核中使用文件描述符来管理file资源,在linux内核中文件描述符为一个无符号整型,其管理采用数组方式,方便进行查询:
其中0,1和2进行了保留:
- 0: 标准输出standard input
- 1: 标准输入Output standard output correctly
- 2: 出错输出error output
inode 、file、file descript
在一个进程中inode 、file以及file descript三者之间的组织关系可以用如下图来表示:
file是一种进程资源,故在相同的文件不同的进程打开 有不同的file结构以及不同的文件描述符 ,而与存储介质打加到的inode节点,系统启动的过程中从super block中读取该文件系统的信息,并在内存中建立相应的inode节点,一个文件只能有一个inode节点但是可以有多个file结构。dentry_cahce是一种描述整个文件系统的目录组织形式,以方便能够快速进行查找文件等操作。
:https://zhuanlan.zhihu.com/p/578285171