admin管理员组

文章数量:1530517

参考链接
Inode详解

一、inode简介

Linux内核文件系统最重要的数据结构为inode,则一个inode就代表一个文件,inode结构体保存文件的大小、文件的块大小、创建时间等各种参数。一个文件的inode只有唯一一个。
Linux内核inode源码如下:include\linux\fs.h

/*
 * Keep mostly read-only and often accessed (especially for
 * the RCU path lookup and 'stat' data) fields at the beginning
 * of the 'struct inode'
 */
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 timespec		i_atime;
	struct timespec		i_mtime;
	struct timespec		i_ctime;
	spinlock_t		i_lock;	/* i_blocks, i_bytes, maybe i_size */
	unsigned short          i_bytes;
	unsigned int		i_blkbits;
	enum rw_hint		i_write_hint;
	blkcnt_t		i_blocks;

#ifdef __NEED_I_SIZE_ORDERED
	seqcount_t		i_size_seqcount;
#endif

	/* Misc */
	unsigned long		i_state;
	struct mutex		i_mutex;

	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;
	union {
		struct hlist_head	i_dentry;
		struct rcu_head		i_rcu;
	};
	u64			i_version;
	atomic_t		i_count;
	atomic_t		i_dio_count;
	atomic_t		i_writecount;
#ifdef CONFIG_IMA
	atomic_t		i_readcount; /* struct files open RO */
#endif
	const struct file_operations	*i_fop;	/* former ->i_op->default_file_ops */
	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;
	};

	__u32			i_generation;

#ifdef CONFIG_FSNOTIFY
	__u32			i_fsnotify_mask; /* all events this inode cares about */
	struct hlist_head	i_fsnotify_marks;
#endif

#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
	struct fscrypt_info	*i_crypt_info;
#endif
	void			*i_private; /* fs or device private pointer */
};

文件数据存储在"块"中,很显然我们必须要找到一个地方存储此文件"元信息"(文件大小,文件作者,文件创建日期),存储文件元信息的区域就叫做inode(索引节点)
inode也会占用硬盘空间,所以我们在硬盘格式化时,操作系统自动将硬盘分为两个区域:
1.一个是数据区,存放数据;
2.另一个为inode区,存放所包含的信息。每个inode节点的大小一般为128字节或者256字节。
我们可以通过df命令来查看硬盘分区的inode总数和已经使用的数量。

li@li:~$ df -i
文件系统         Inode 已用(I) 可用(I) 已用(I)% 挂载点
udev            493944     480  493464       1% /dev
tmpfs           500212    1063  499149       1% /run
/dev/sda1      3276800  550576 2726224      17% /
tmpfs           500212       1  500211       1% /dev/shm
tmpfs           500212       6  500206       1% /run/lock
tmpfs           500212      18  500194       1% /sys/fs/cgroup
/dev/loop0       65095   65095       0     100% /snap/gtk-common-themes/1519
/dev/loop1       11736   11736       0     100% /snap/core20/1242
/dev/loop2        1388    1388       0     100% /snap/gnome-calculator/884
/dev/loop4         872     872       0     100% /snap/gnome-system-monitor/157
/dev/loop3       17441   17441       0     100% /snap/gnome-3-38-2004/87
/dev/loop7       64986   64986       0     100% /snap/gtk-common-themes/1515
/dev/loop8          29      29       0     100% /snap/bare/5
/dev/loop11        305     305       0     100% /snap/gnome-characters/761
/dev/loop10      18508   18508       0     100% /snap/gnome-3-34-1804/66
/dev/loop13        479     479       0     100% /snap/snapd/14066
/dev/loop14        938     938       0     100% /snap/gnome-system-monitor/174
/dev/loop15      10836   10836       0     100% /snap/core18/2253
/dev/loop16        401     401       0     100% /snap/gnome-logs/106
/dev/loop17        401     401       0     100% /snap/gnome-logs/103
/dev/loop18      18500   18500       0     100% /snap/gnome-3-34-1804/77
vmhgfs-fuse          0       0       0        - /mnt/hgfs
tmpfs           500212      25  500187       1% /run/user/121
tmpfs           500212      34  500178       1% /run/user/1000
/dev/loop19      10847   10847       0     100% /snap/core18/2284
/dev/loop20      11777   11777       0     100% /snap/core20/1328
/dev/loop21        482     482       0     100% /snap/snapd/14549
/dev/loop6         305     305       0     100% /snap/gnome-characters/741
/dev/loop9        1386    1386       0     100% /snap/gnome-calculator/920

inode的成员可能分为两类:
1.描述文件状态的元数据(比如访问权限、文件大小等);
2.保存实际文件内容的数据段(指向数据的指针,文本文件用于保存文本)。

二、inode是什么

理解inode,要从文件储存说起。
文件储存在硬盘上,硬盘的最小存储单位是扇区(Sector)。每个扇区储存512字节。
操作系统读取硬盘的时候,不会一个一个扇区的读取,而是一次性连续读取多个扇区,即一次性读取一个块(block),这种由多个扇区组成的块是文件的最小单位。块的大小最常见的是4kb,即连续8个sector组成一个block。
文件数据都储存在块中,那么很显然,我们还必须找到一个地方储存文件的元信息,比如文件的创建者、文件的的创建日期、文件的大小等,这种储存文件元信息的区域就叫做inode,中文译为索引节点。

三、inode编号

系统中所有的inode都有一个特定的编号,用于唯一标识各个inode,文件名称和inode之间的关联即通过该编号直接建立。
每个inode都有一个号码,操作系统用inode号码来识别不同的文件。
这里重复一遍,Unix/Linux系统内部不使用文件名,而使用inode号码来识别文件,对于系统来说,文件名只是inode号码便于识别的别称或者绰号。
用户通过文件名打开文件,实际上在系统中这个过程分成三步;
1.首先,系统找到这个文件名对应的inode号码;
2.其次,通过inode号码,获取inode信息;
3.最后,根据inode信息找到文件数据所在的block,读出数据。
使用ls -i命令,可以看到文件名对应的inode号码。

li@li:~$ ls -i
 538889 a.out              526533 git       944994 linux_driver   526543 tool                                  656723 vsCode      524358 模板   524357 下载
 539052 c++                539079 lcl       656639 linux_src      539312 tools                                 538400 work        524363 视频   524361 音乐
 944995 code               541014 lcl_git   541029 misc.c         524424 VMwareTools-10.3.22-15902021          541030 work_test   524362 图片   524356 桌面
 524291 examples.desktop   524410 linux    1049036 test           524418 VMwareTools-10.3.22-15902021.tar.gz   524359 公共的      524360 文档
li@li:~$

四、inode源码注释

linux 内核inode VS file

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 timespec		i_atime;     //上一次访问文件的时间
	struct timespec		i_mtime;     //上一次修改文件数据的时间
	struct timespec		i_ctime;     //上一次修改文件索引节点的时间
	spinlock_t		i_lock;	/* i_blocks, i_bytes, maybe i_size */
	unsigned short          i_bytes;  //文件长度除以块长度的余数
	unsigned int		i_blkbits;    //块长度以2为底的对数
	enum rw_hint		i_write_hint; 
	blkcnt_t		i_blocks;   //文件的块数

#ifdef __NEED_I_SIZE_ORDERED
	seqcount_t		i_size_seqcount;
#endif

	/* Misc */
	unsigned long		i_state;
	struct mutex		i_mutex;

	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;
	union {
		struct hlist_head	i_dentry;
		struct rcu_head		i_rcu;
	};
	u64			i_version;
	atomic_t		i_count;       //索引节点的引用计数
	atomic_t		i_dio_count;
	atomic_t		i_writecount;
#ifdef CONFIG_IMA
	atomic_t		i_readcount; /* struct files open RO */
#endif
	const struct file_operations	*i_fop;	/* former ->i_op->default_file_ops */
	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;
	};

	__u32			i_generation;

#ifdef CONFIG_FSNOTIFY
	__u32			i_fsnotify_mask; /* all events this inode cares about */
	struct hlist_head	i_fsnotify_marks;
#endif

#if IS_ENABLED(CONFIG_FS_ENCRYPTION)
	struct fscrypt_info	*i_crypt_info;
#endif
	void			*i_private; /* fs or device private pointer */
};

超级块,i节点,数据块,目录块,间接块

将磁盘块分为三个部分:
1)超级块文件系统中第一个块被称为超级块。这个块存放文件系统本身的结构信息。比如,超级块记录了每个区域的大小,超级块也存放未被使用的磁盘块的信息。
2) i-节点表。超级块的下一个部分就是i-节点表,每个文件都有一些属性,如文件的大小、文件所有者、和创建时间等,这些性质被记录在一个称为i-节点的结构中。所有i-节点都有相同的大小,并且i-节点表是这些结构的一个列表,文件系统中每个文件在该表中都有一个i-节点。
3)数据区。文件系统的第3个部分是数据区。文件的内容保存在这个区域。磁盘上所有块的大小都一样。如果文件包含了超过一个块的内容,则文件内容会存放在多个磁盘块中。一个较大的文件很容易分布上千个独立的磁盘块中。

五、文件分类

普通文件、目录、字符设备文件、块设备文件、命名管道(FIFO)、套接字(socket)。
字符设备文件、块设备文件、命名管道(FIFO)、套接字(socket)这4种是特殊文件,这些文件只有索引节点,没有数据,字符设备文件和块设备文件用来存储设备号、直接把设备号存储在索引节点中

六、链接

在linux系统中有种文件是链接文件,可以解决文件的共享使用。链接分为两种:一种是硬链接,另一种是软连接或者也称为符号链接
硬链接:相当于一个文件取了多个名称,多个文件名称对应一个索引节点,索引节点成员i_nlink是硬链接计数,删除文件只需将相应的目录项删除,该文件的链接数减1,如果删除目录项后该文件的链接数为零,这时系统才把真正的文件从磁盘上删除。;
软连接:这种文件的数据是另一个文件的路径,可对一个文件或目录创建软连接。
inode(索引节点)的成员i_op指向索引节点操作命令inode_operations,i_fop指向文件操作集合file_operations。两者间的区别:inode_operations用来操作目录和文件属性。file_operations用于访问文件的数据。
索引节点操作集合的数据结构是结构体inode_operations,具体源码如下:

struct inode_operations {
	struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
	const char * (*follow_link) (struct dentry *, void **);
	int (*permission) (struct inode *, int);
	int (*permission2) (struct vfsmount *, struct inode *, int);
	struct posix_acl * (*get_acl)(struct inode *, int);

	int (*readlink) (struct dentry *, char __user *,int);
	void (*put_link) (struct inode *, void *);

	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 *);
	int (*rename2) (struct inode *, struct dentry *,
			struct inode *, struct dentry *, unsigned int);
	int (*setattr) (struct dentry *, struct iattr *);
	int (*setattr2) (struct vfsmount *, struct dentry *, struct iattr *);
	int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
	int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
	ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
	ssize_t (*listxattr) (struct dentry *, char *, size_t);
	int (*removexattr) (struct dentry *, const char *);
	int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
		      u64 len);
	int (*update_time)(struct inode *, struct timespec *, int);
	int (*atomic_open)(struct inode *, struct dentry *,
			   struct file *, unsigned open_flag,
			   umode_t create_mode, int *opened);
	int (*tmpfile) (struct inode *, struct dentry *, umode_t);
	int (*set_acl)(struct inode *, struct posix_acl *, int);
} ____cacheline_aligned;

文件操作集合的数据结构是结构体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 (*iterate) (struct file *, struct dir_context *);
	unsigned int (*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 *);
	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 (*aio_fsync) (struct kiocb *, 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
};

硬链接:通过索引节点来进行链接,在linux文件系统中,保存在磁盘分区中的文件不管是什么类型都会给它分配一个编号,这个编号被称为索引节点(inode index)。硬链接的作用之一是允许一个文件拥有多个有效路径名称,这样用户就可以建立硬链接到重要文件,以防止"误删除"源数据。
软连接:是存放另一个文件的路径的形式存在。
Linux建立软链接、硬链接
软硬链接的特点:
1、软连接可以跨文件系统,硬链接不可以;
2、软连接可以对目录进行链接,硬链接不可以;

本文标签: 模型链接文件VFSinode