一:文献系统 1. 什么是文献系统? 操作系统中认真不断和存储文献信息的软件机构称为文献不断系统,简称文献系统。 时常文献系统是用于存储和组织文献的一种机制,便于对文献进行便捷的...
一:文献系统
1. 什么是文献系统?
操作系统中认真不断和存储文献信息的软件机构称为文献不断系统,简称文献系统。
时常文献系统是用于存储和组织文献的一种机制,便于对文献进行便捷的查找与探听。
文献系统是对文献存储开辟的空间进行组织和分拨,认真文献存储并对存入的文献进行保护和检索的系统。
它认真为用户诞生文献,存入、读出、修改、转储文献,抑遏文献的存取,当用户不再使用时取销文献等。
跟着文献种类的增多,扩增了更多的文献系统,为了对多样文献系统进行谐和的不断与组织。
2. Linux文献系统
Linux将文献系统分为了两层:VFS(假造文献系统)、具体文献系统,如下图所示:
VFS
VFS(Virtual Filesystem Switch)称为假造文献系统或假造文献系统调度,是一个内核软件层,在具体的文献系统之上详细的一层,用来处理与Posix文献系统关联的扫数调用,阐述为概况给多样文献系统提供一个通用的接口,使表层的应用按序概况使用通用的接口探听不同文献系统,同期也为不同文献系统的通讯提供了引子。
VFS并不是一种内容的文献系统,它只存在于内存中,不存在职何外存空间,VFS在系统启动时诞生,在系统关闭时祛除。
VFS由超等块、inode、dentry、vfsmount等结构来组成。
Linux系统中存在好多的文献系统,举例常见的**ext2,ext3,ext4,sysfs,rootfs,proc...**等等。
二 、VFS
1. VFS在linux架构中的位置
从用户的使用角度,Linux下的文献系统中宏观上主要分为三层:
1.表层的文献系统的系统调用(System-call ); 2.假造文献系统VFS(Virtual File System)层, 3.挂载到VFS中的多样内容文献系统。VFS在通盘Linux系统中的架构视图如下:
VFS
Linux系统的User使用GLIBC(POSIX标准、GUN C运行时库)四肢应用按序的运行时库,然后通过操作系统,将其调度为系统调用SCI(system-call interface),SCI是操作系统内核界说的系统调用接口,这层详细允许用户按序的I/O操作调度为内核的接口调用。
2. 用户如何透明的去向理文献?
咱们澄澈每个文献系统是孤独的,有我方的组织方法,操作方法。那么关于用户来说,不可能扫数的文献系统都了解,那么若何做到让用户透明的去向理文献呢?
举例:我想写文献,那就平直read就OK,岂论你是什么文献系统,具体若何去读!这里就需要引入假造文献系统。
是以假造文献系统等于:关于一个system,不错存在多个“内容的文献系统”,举例:ext2,ext3,fat32,ntfs...举例我面前有多个分区,关于每一个分区咱们澄澈不错是不同的“内容文献系统”。
举例面前三个磁盘分隔离别是:ext2,ext3,fat32,那么每个“内容的文献系统”的操作和数据结构详情不通常,那么,用户若何能透明使用它们呢?
这个时候就需要VFS四肢中间一层!用户平直和VFS打交道。
VFS是一种软件机制,只存在于内存中,每次系统驱动化时间Linux都会先在内存中构造一棵VFS的目次树(也等于源码中的namespace)。
VFS主要的作用是对表层应用屏蔽底层不同的调用方法,提供一套谐和的调用接口,二是便于对不同的文献系统进行组织不断。
VFS提供了一个详细层,将POSIX API接口与不同存储开辟的具体接口终了进行了分离,使得底层的文献系统类型、开辟类型对表层应用按序透明。
举例read,write,那么映射到VFS中等于sys_read,sys_write,那么VFS不错左证你操作的是哪个“内容文献系统”(哪个分区)来进行不同的内容的操作!这个本领亦然很熟习的“钩子结构”本领来处理的。
其实等于VFS中提供一个详细的struct结构体,然后关于每一个具体的文献系统要把我方的字段和函数填充进去,这么就科罚了异构问题(内核好多子系统都巨额使用了这种机制)。
三、Linux假造文献系统四大对象
为了对文献系统进行谐和的不断与组织,Linux创建了一个巨匠根目次和全局文献系统树。要探听一个文献系统中的文献,必须先将这个文献系统挂载在全局文献系统树的某个根目次下,这一挂载经过被称作文献系统的挂载,所挂载的目次称为挂载点。
传统的文献系统在磁盘上的布局如下:
由上图可知,文献系统的来源时常是由一个磁盘扇区所组成的教悔块,该部分的主要缱绻是用于对操作系统的教悔。一般只在启动操作系统时使用。
随后是超等块,超等块主要存放了该物理磁盘汉文献系统结构的关联信息,而况对各个部分的大小进行证明。
终末由i节点位图,逻辑块位图、i节点、逻辑块这几部分散播在物理磁盘上。
Linux为了对超等块,i节点,逻辑块这三部分进行高效的不断,Linux创建了几种不同的数据结构,分别是文献系统类型、inode、dentry等几种。
其中,文献系统类型章程了某种文献系统的行为,诳骗该数据结构不错构造某种文献系统类型的实例,另外,该实例也被称为超等块实例。
超等块则是反应了文献系统举座的抑遏信息。超等块概况以多种的阵势存在,关于基于磁盘的文献系统,它以特定的式样存在于磁盘的固定区域(取决于文献系统类型)上。在挂载文献系统时,该超等块中的内容被读入磁盘中,从而构建出位于内存中的新的超等块。
inode则反应了文献系统对象中的一般元数据信息。dentry则是反应出某个文献系统对象在全局文献系统树中的位置。
Linux对这四种数据结构进行了关联的关联。如下图:
结构体关系
1. 超等块(super block)
超等块:一个超等块对应一个文献系统(还是安设的文献系统类型如ext2,此处是内容的文献系统,不是VFS)。
之前咱们还是说了文献系统用于不断这些文献的数据式样和操作之类的,系统文献有系统文献我方的文献系统,同期关于不同的磁盘分区也有不错是不同的文献系统。那么一个超等块关于一个孤独的文献系统。保存文献系统的类型、大小、情状等等。
(“文献系统”和“文献系统类型”不通常!一个文献系统类型下不错包括好多文献系统即好多的super_block)
既然咱们澄澈关于不同的文献系统有不同的super_block,那么关于不同的super_block的操作详情亦然不同的,是以咱们不才面的super_block结构中不错看到上头说的详细的struct结构(举例底下的:struct super_operations):
1246 struct super_block { 1247 struct list_head s_list; /* Keep this first */ 1248 dev_t s_dev; /* search index; _not_ kdev_t */ 1249 unsigned char s_blocksize_bits; 1250 unsigned long s_blocksize; 1251 loff_t s_maxbytes; /* Max file size */ 1252 struct file_system_type *s_type; 1253 const struct super_operations *s_op; 1254 const struct dquot_operations *dq_op; 1255 const struct quotactl_ops *s_qcop; 1256 const struct export_operations *s_export_op; 1257 unsigned long s_flags; 1258 unsigned long s_magic; 1259 struct dentry *s_root; 1260 struct rw_semaphore s_umount; 1261 int s_count; 1262 atomic_t s_active; 1263 #ifdef CONFIG_SECURITY 1264 void *s_security; 1265 #endif 1266 const struct xattr_handler **s_xattr; 1267 1268 struct list_head s_inodes; /* all inodes */ 1269 struct hlist_bl_head s_anon; /* anonymous dentries for (nfs) exporting */ 1270 struct list_head s_mounts; /* list of mounts; _not_ for fs use */ 1271 struct block_device *s_bdev; 1272 struct backing_dev_info *s_bdi; 1273 struct mtd_info *s_mtd; 1274 struct hlist_node s_instances; 1275 struct quota_info s_dquot; /* Diskquota specific options */ 1276 1277 struct sb_writers s_writers; 1278 1279 char s_id[32]; /* Informational name */ 1280 u8 s_uuid[16]; /* UUID */ 1281 1282 void *s_fs_info; /* Filesystem private info */ 1283 unsigned int s_max_links; 1284 fmode_t s_mode; 1285 1286 /* Granularity of c/m/atime in ns. 1287 Cannot be worse than a second */ 1288 u32 s_time_gran; 1289 1290 /* 1291 * The next field is for VFS *only*. No filesystems have any business 1292 * even looking at it. You had been warned. 1293 */ 1294 struct mutex s_vfs_rename_mutex; /* Kludge */ 1295 1296 /* 1297 * Filesystem subtype. If non-empty the filesystem type field 1298 * in /proc/mounts will be "type.subtype" 1299 */ 1300 char *s_subtype; 1301 1302 /* 1303 * Saved mount options for lazy filesystems using 1304 * generic_show_options() 1305 */ 1306 char __rcu *s_options; 1307 const struct dentry_operations *s_d_op; /* default d_op for dentries */ 1308 1309 /* 1310 * Saved pool identifier for cleancache (-1 means none) 1311 */ 1312 int cleancache_poolid; 1313 1314 struct shrinker s_shrink; /* per-sb shrinker handle */ 1315 1316 /* Number of inodes with nlink == 0 but still referenced */ 1317 atomic_long_t s_remove_count; 1318 1319 /* Being remounted read-only */ 1320 int s_readonly_remount; 1321 1322 /* AIO completions deferred from interrupt context */ 1323 struct workqueue_struct *s_dio_done_wq; 1324 1325 /* 1326 * Keep the lru lists last in the structure so they always sit on their 1327 * own individual cachelines. 1328 */ 1329 struct list_lru s_dentry_lru ____cacheline_aligned_in_smp; 1330 struct list_lru s_inode_lru ____cacheline_aligned_in_smp; 1331 struct rcu_head rcu; 1332 };
评释字段:
字段 式样 s_list 指向超等块链表的指针,这个struct list_head是很熟习的结构了,里面其实等于用于取悦关系的prev和next字段。内核单独使用一个简单的结构体将扫数的super_block都蚁集起来。 s_dev 包含该具体文献系统的块开辟标记符。举例,关于 /dev/hda1,其开辟标记符为 0x301 s_blocksize_bits 上头的size大小占用位数,举例512字节等于9 bits s_blocksize 文献系统中数据块大小,以字节单元 s_maxbytes 允许的最大的文献大小(字节数) struct file_system_type *s_type 文献系统类型(也等于现时这个文献系统属于哪个类型?ext2如故fat32),要隔离“文献系统”和“文献系统类型”不通常!一个文献系统类型下不错包括好多文献系统即好多的super_block,后头会说! struct super_operations *s_op 指向某个特定的具体文献系统的用于超等块操作的函数蚁集 struct dquot_operations *dq_op 指向某个特定的具体文献系统用于名额操作的函数蚁集 struct quotactl_ops *s_qcop 用于建设磁盘名额的的方法,处理来私用户空间的央求 s_flags 安设标记 s_magic 区别于其他文献系统的标记 s_root 指向该具体文献系统安设目次的目次项 s_umount 对超等块读写时进行同步 s_count 对超等块的使用计数 s_active 援用计数
超等块方法
struct super_operations { //该函数在给定的超等块下创建并驱动化一个新的索引节点对象 struct inode *(*alloc_inode)(struct super_block *sb); //开释指定的索引结点 。 void (*destroy_inode)(struct inode *); //VFS在索引节点被修改时会调用此函数。 void (*dirty_inode) (struct inode *, int flags); // 将指定的inode写回磁盘。 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 *); //使文献系统的数据元素与磁盘上的文献系统同步,wait参数指定操作是否同步。 int (*sync_fs)(struct super_block *sb, int wait); int (*freeze_fs) (struct super_block *); int (*unfreeze_fs) (struct super_block *); //赢得文献系统情状。把文献系统关联的统计信息放在statfs中 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); #endif int (*bdev_try_to_free_page)(struct super_block*, struct page*, gfp_t); long (*nr_cached_objects)(struct super_block *, int); long (*free_cached_objects)(struct super_block *, long, int); };
2. 索引节点(inode)
索引节点inode:保存的其实是内容的数据的一些信息,这些信息称为“元数据”(也等于对文献属性的式样)。
举例:文献大小,开辟标记符,用户标记符,用户组标记符,文献模式,彭胀属性,文献读取或修改的时期戳,蚁集数目,指向存储该内容的磁盘区块的指针,文献分类等等。
( 看管数据分红:元数据+数据自己 )
同期看管:inode有两种,一种是VFS的inode,一种是具体文献系统的inode。前者在内存中,后者在磁盘中。是以每次其实是将磁盘中的inode调进填充内存中的inode,这么才是算使用了磁盘文献inode。
inode如何生成的?
每个inode节点的大小,一般是128字节或256字节。inode节点的总额,在式样化时就给定(当代OS不错动态变化),一般每2KB就诞生一个inode。
一般文献系统中很少有文献小于2KB的,是以预定按照2KB分,一般inode是用不完的。是以inode在文献系统安设的时候会有一个默许数目,后期会左证内容的需要发生变化。
看管inode号:inode号是唯一的,默示不同的文献。其实在Linux里面的时候,探听文献都是通过inode号来进行的,所谓文献名只是是给用户容易使用的。
当咱们掀开一个文献的时候,最初,系统找到这个文献名对应的inode号;然后,通过inode号,得到inode信息,终末,由inode找到文献数据场合的block,面前不错处理文献数据了。
inode和文献的关系?
当创建一个文献的时候,就给文献分拨了一个inode。一个inode只对应一个内容文献,一个文献也会唯唯一个inode。inodes最大数目等于文献的最大数目。
527 struct inode { 528 umode_t i_mode; /* 探听权限抑遏 */ 529 unsigned short i_opflags; 530 kuid_t i_uid; /* 使用者的id */ 531 kgid_t i_gid; /* 使用组id */ 532 unsigned int i_flags; /* 文献系统标志 */ 533 534 #ifdef CONFIG_FS_POSIX_ACL 535 struct posix_acl *i_acl; 536 struct posix_acl *i_default_acl; 537 #endif 538 539 const struct inode_operations *i_op; /*索引节点操作表*/ 540 struct super_block *i_sb; /* 关联的超等块 */ 541 struct address_space *i_mapping; /* 关联的地址映射 */ 542 543 #ifdef CONFIG_SECURITY 544 void *i_security; 545 #endif 546 547 /* Stat data, not accessed from path walking */ 548 unsigned long i_ino; /* 索引节点号 */ 549 /* 550 * Filesystems may only read i_nlink directly. They shall use the 551 * following functions for modification: 552 * 553 * (set|clear|inc|drop)_nlink 554 * inode_(inc|dec)_link_count 555 */ 556 union { 557 const unsigned int i_nlink; 558 unsigned int __i_nlink; /* 硬取悦数 */ 559 }; 560 dev_t i_rdev; /* 内容开辟标记标记 */ 561 loff_t i_size; 562 struct timespec i_atime; /* 终末探听时期 */ 563 struct timespec i_mtime; /* 终末修改时期 */ 564 struct timespec i_ctime; /* 终末篡改时期 */ 565 spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */ 566 unsigned short i_bytes; /* 使用的字节数 */ 567 unsigned int i_blkbits; 568 blkcnt_t i_blocks; /* 文献的块数 */ 569 570 #ifdef __NEED_I_SIZE_ORDERED 571 seqcount_t i_size_seqcount; 572 #endif 573 574 /* Misc */ 575 unsigned long i_state; 576 struct mutex i_mutex; 577 578 unsigned long dirtied_when; /* jiffies of first dirtying 初度修改时期*/ 579 580 struct hlist_node i_hash; /* hash值,提升查找成果 */ 581 struct list_head i_wb_list; /* backing dev IO list */ 582 struct list_head i_lru; /* inode LRU list 未使用的inode*/ 583 struct list_head i_sb_list; /* 蚁集一个文献系统中扫数inode的链表 */ 584 union { 585 struct hlist_head i_dentry; /* 目次项链表 */ 586 struct rcu_head i_rcu; 587 }; 588 u64 i_version; 589 atomic_t i_count; /* 援用计数 */ 590 atomic_t i_dio_count; 591 atomic_t i_writecount; /* 写者计数 */ 592 const struct file_operations *i_fop; /* former ->i_op->default_file_ops 文献操作*/ 593 struct file_lock *i_flock; /* 文献锁链表 */ 594 struct address_space i_data; /* 默示被inode读写的页面 */ 595 #ifdef CONFIG_QUOTA 596 struct dquot *i_dquot[MAXQUOTAS];/* 节点的磁盘名额 */ 597 #endif 598 struct list_head i_devices; /* 开辟链表(共用并吞个驱动按序的开辟造成的链表。) */ 599 union { 600 struct pipe_inode_info *i_pipe; /* 管道信息 */ 601 struct block_device *i_bdev; /* 块开辟驱动节点 */ 602 struct cdev *i_cdev; /* 字符开辟驱动节点 */ 603 }; 604 605 __u32 i_generation; /* 索引节点版块号 */ 606 607 #ifdef CONFIG_FSNOTIFY 608 __u32 i_fsnotify_mask; /* all events this inode cares about */ 609 struct hlist_head i_fsnotify_marks; 610 #endif 611 612 #ifdef CONFIG_IMA 613 atomic_t i_readcount; /* struct files open RO */ 614 #endif 615 void *i_private; /* fs or device private pointer 用户特罕有据*/ 616 };
看管不断inode的四个链表:
static struct hlist_head *inode_hashtable __read_mostly;
节点方法
struct inode_operations { struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int); void * (*follow_link) (struct dentry *, struct nameidata *); int (*permission) (struct inode *, int); struct posix_acl * (*get_acl)(struct inode *, int); int (*readlink) (struct dentry *, char __user *,int); void (*put_link) (struct dentry *, struct nameidata *, 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 (*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;
对其中一些蹙迫的终局进行分析:
方法 含义 create() 要是该inode式样一个目次文献,那么当在该目次下创建或掀开一个文献时,内核必须为这个文献创建一个inode。VFS通过调用该inode的i_op->create()函数来完成上述新inode的创建。该函数的第一个参数为该目次的 inode,第二个参数为要掀开新文献的dentry,第三个参数是对该文献的探听权限。要是该inode式样的是一个凡俗文献,那么该inode始终都不会调用这个create函数; lookup() 查找指定文献的dentry; link() 用于在指定目次下创建一个硬蚁集。这个link函数最终会被系统调用link()调用。该函数的第一个参数是原始文献的dentry,第二个参数即为上述指定目次的inode,第三个参数是蚁集文献的dentry。 unlink () 在某个目次下删除指定的硬蚁集。这个unlink函数最终会被系统调用unlink()调用。第一个参数即为上述硬蚁集场合目次的inode,第二个参数为要删除文献的dentry。 symlink () 在某个目次下新建 mkdir() 在指定的目次下创建一个子目次,现时目次的inode会调用i_op->mkdir()。该函数会被系统调用mkdir()调用。第一个参数即为指定目次的inode,第二个参数为子目次的dentry,第三个参数为子目次权限; rmdir () 从inode所式样的目次中删除一个指定的子目次时,该函数会被系统调用rmdir()最终调用; mknod() 在指定的目次下创建一个出奇文献,比如管道、开辟文献或套接字等。3)目次项(dentry)
目次项是式样文献的逻辑属性,只存在于内存中,并莫得内容对应的磁盘上的式样,更确切的说是存在于内存的目次项缓存,为了提升查找性能而遐想。
看管岂论是文献夹如故最终的文献,都是属于目次项,扫数的目次项在沿路组成一颗巨大的目次树。
举例:open一个文献/home/xxx/yyy.txt,那么/、home、xxx、yyy.txt都是一个目次项,VFS在查找的时候,左证一层一层的目次项找到对应的每个目次项的inode,那么沿着目次项进行操作就不错找到最终的文献。
看管:目次亦然一种文献(是以也存在对应的inode)。掀开目次,内容上等于掀开目次文献。
108 struct dentry { 109 /* RCU lookup touched fields */ 110 unsigned int d_flags; /* protected by d_lock */ 111 seqcount_t d_seq; /* per dentry seqlock */ 112 struct hlist_bl_node d_hash; /* lookup hash list */ 113 struct dentry *d_parent; /* parent directory 父目次*/ 114 struct qstr d_name; 115 struct inode *d_inode; /* Where the name belongs to - NULL is 116 * negative 与该目次项关联的inode*/ 117 unsigned char d_iname[DNAME_INLINE_LEN]; /* small names 漫笔件名*/ 118 119 /* Ref lookup also touches following */ 120 struct lockref d_lockref; /* per-dentry lock and refcount */ 121 const struct dentry_operations *d_op; /* 目次项操作 */ 122 struct super_block *d_sb; /* The root of the dentry tree 这个目次项所属的文献系统的超等块(目次项树的根)*/ 123 unsigned long d_time; /* used by d_revalidate 从头见效时期*/ 124 void *d_fsdata; /* fs-specific data 具体文献系统的数据 */ 125 126 struct list_head d_lru; /* LRU list 未使用目次以LRU 算法蚁集的链表 */ 127 /* 128 * d_child and d_rcu can share memory 129 */ 130 union { 131 struct list_head d_child; /* child of parent list 目次项通过这个加入到父目次的d_subdirs中*/ 132 struct rcu_head d_rcu; 133 } d_u; 134 struct list_head d_subdirs; /* our children 本目次的扫数孩子目次链表头 */ 135 struct hlist_node d_alias; /* inode alias list 索引节点笔名链表*/ 136 };
一个有用的dentry结构必定有一个inode结构,这是因为一个目次项要么代表着一个文献,要么代表着一个目次,而目次内容上亦然文献。是以,只须dentry结构是有用的,则其指针d_inode必定指向一个inode结构。然而inode却不错对应多个。
通盘结构其实等于一棵树,要是看过我的开辟模子kobject就能澄澈,目次其实等于文献(kobject、inode)再加上一层封装,这里所谓的封装主要等于加多两个指针,一个是指向父目次,一个是指向该目次所包含的扫数文献(凡俗文献和目次)的链表头。
这么身手有咱们的目次操作(比如回到前次目次,只需要一个指针设施【..】,而插足子目次需要链表索引需要多个设施)
dentry关联的操作(inode里面还是包含了mkdir,rmdir,mknod之类的操作了)
struct dentry_operations { /* 该函数判断目次对象是否有用。VFS准备从dcache中使用一个目次项时,会调用该函数. */ int (*d_revalidate)(struct dentry *, unsigned int); int (*d_weak_revalidate)(struct dentry *, unsigned int); /* 该目次生成散列值,当目次项要加入到散列表时,VFS要调用此函数。 */ int (*d_hash)(const struct dentry *, struct qstr *); /* 该函数来比拟name1和name2这两个文献名。使用该函数要加dcache_lock锁。 */ int (*d_compare)(const struct dentry *, const struct dentry *, unsigned int, const char *, const struct qstr *); /* 当d_count=0时,VFS调用次函数。使用该函数要叫 dcache_lock锁。 */ int (*d_delete)(const struct dentry *); /* 当该目次对象将要被开释时,VFS调用该函数。 */ void (*d_release)(struct dentry *); void (*d_prune)(struct dentry *); /* 当一个目次项丢失了其索引节点时,VFS就掉用该函数。 */ void (*d_iput)(struct dentry *, struct inode *); char *(*d_dname)(struct dentry *, char *, int); struct vfsmount *(*d_automount)(struct path *); int (*d_manage)(struct dentry *, bool); } ____cacheline_aligned;
4)文献对象(file)
文献对象式样的是程度还是掀开的文献。因为一个文献不错被多个程度掀开,是以一个文献不错存在多个文献对象。然而由于文献是唯一的,那么inode等于唯一的,目次项亦然定的!
程度其实是通过文献式样符来操作文献的,每个文献都有一个32位的数字来默示下一个读写的字节位置,这个数字叫做文献位置。
一般情况下掀开文献后,掀开位置都是从0动手,除非一些出奇情况。Linux用file结构体来保存掀开的文献的位置,是以file称为掀开的文献式样。file结构造成一个双链表,称为系统掀开文献表。
file
775 struct file { 776 union { 777 struct llist_node fu_llist; /* 每个文献系统中被掀开的文献都会造成一个双链表 */ 778 struct rcu_head fu_rcuhead; 779 } f_u; 780 struct path f_path; 781 #define f_dentry f_path.dentry 782 struct inode *f_inode; /* cached value */ 783 const struct file_operations *f_op; /* 指向文献操作表的指针 */ 784 785 /* 786 * Protects f_ep_links, f_flags. 787 * Must not be taken from IRQ context. 788 */ 789 spinlock_t f_lock; 790 atomic_long_t f_count; /* 文献对象的使用计数 */ 791 unsigned int f_flags; /* 掀开文献时所指定的标志 */ 792 fmode_t f_mode; /* 文献的探听模式(权限等) */ 793 struct mutex f_pos_lock; 794 loff_t f_pos; /* 文献现时的位移量 */ 795 struct fown_struct f_owner; 796 const struct cred *f_cred; 797 struct file_ra_state f_ra; /* 预读情状 */ 798 799 u64 f_version; /* 版块号 */ 800 #ifdef CONFIG_SECURITY 801 void *f_security; /* 安全模块 */ 802 #endif 803 /* needed for tty driver, and maybe others */ 804 void *private_data; /* 特罕有据 */ 805 806 #ifdef CONFIG_EPOLL 807 /* Used by fs/eventpoll.c to link all the hooks to this file */ 808 struct list_head f_ep_links; 809 struct list_head f_tfile_llink; 810 #endif /* #ifdef CONFIG_EPOLL */ 811 struct address_space *f_mapping;/* 页缓存映射 */ 812 #ifdef CONFIG_DEBUG_WRITECOUNT 813 unsigned long f_mnt_write_state; 814 #endif 815 } __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */
重心评释一些蹙迫字段:
最初,f_flags、f_mode和f_pos代表的是这个程度现时操作这个文献的抑遏信息。这个特地蹙迫,因为关于一个文献,不错被多个程度同期掀开,那么关于每个程度来说,操作这个文献是异步的,是以这个三个字段就很蹙迫了。
关于援用计数f_count,当咱们关闭一个程度的某一个文献式样符时候,其实并不是委果的关闭文献,只是是将f_count减一,当f_count=0时候,才会果真去关闭它。关于dup,fork这些操作来说,都会使得f_count加多,具体的细节,以后再说。
f_op亦然很蹙迫的!是触及到扫数的文献的操作结构体。举例:用户使用read,最终都会调用file_operations中的读操作,而file_operations结构体是关于不同的文献系统不一定换取。里面一个蹙迫的操作函数式release函数,当用户实施close时候,其实在内核中是实施release函数,这个函数只是将f_count减一,这也就评释了上头说的,用户close一个文献其实是将f_count减一。唯独援用计数减到0才关闭文献。
看管:关于“正在使用”和“未使用”的文献对象分别使用一个双向链表进行不断。
files_struct
上头的file只是对一个文献而言,关于一个程度(用户)来说,不错同期处理多个文献,是以需要另一个结构来不断扫数的files!
即:用户掀开文献表--->files_struct
172 struct files_struct { 173 atomic_t count; 174 rwlock_t file_lock; /* Protects all the below members. Nests inside tsk->alloc_lock */ 175 int max_fds; 176 int max_fdset; 177 int next_fd; 178 struct file ** fd; /* current fd array */ 179 fd_set *close_on_exec; 180 fd_set *open_fds; 181 fd_set close_on_exec_init; 182 fd_set open_fds_init; 183 struct file * fd_array[NR_OPEN_DEFAULT]; 184 };
评释一些字段:
字段 式样 count 援用计数 file_lock 锁,保护底下的字段 max_fds 现时文献对象的最大的数目 max_fdset 文献式样符最大数 next_fd 已分拨的最大的文献式样符+1 fd 指向文献对象指针数组的指针,一般等于指向终末一个字段fd_arrray,当文献数跨越NR_OPEN_DEFAULT时候,就会从头分拨一个数组,然后指向这个新的数组指针! close_on_exec 实施exec()时候需要关闭的文献式样符 open_fds 指向掀开的文献式样符的指针 close_on_exec_init 实施exec()时候需要关闭的文献式样符驱动化值 open_fds_init 文献式样符初值蚁集 fd_array 文献对象指针的驱动化数组fs_struct
上头的file和files_struct记载的是与程度关联的文献的信息,然而关于程度自己来说,自身的一些信息用什么默示,这里就触及到fs_struct结构体。
5 struct fs_struct { 6 atomic_t count; 7 rwlock_t lock; 8 int umask; 9 struct dentry * root, * pwd, * altroot; 10 struct vfsmount * rootmnt, * pwdmnt, * altrootmnt; 11 };
评释一些字段:
字段 式样 count 援用计数 lock 保护锁 umask 掀开文献时候默许的文献探听权限 root 程度的根目次 pwd 程度现时的实施目次 altroot 用户诞生的替换根目次看管:内容运行时,这三个目次不一建都在并吞个文献系统中。举例,程度的根目次时常是安设于“/”节点上的ext文献系统,而现时责任目次可能是安设于/etc的一个文献系统,替换根目次也不错不同文献系统中。rootmnt,pwdmnt,altrootmnt:对应于上头三个的安设点。
文献方法(操作)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 (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, 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 **); long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len); int (*show_fdinfo)(struct seq_file *m, struct file *f); };
上头这个对咱们驱动开发人员应该是最熟习的,亦然必须足下的了。
字段 式样 owner 用于指定领有这个文献操作结构体的模块,时常取THIS_MODULE; llseek 用于诞生文献的偏移量。第一个参数指明要操作的文献,第二个参数为偏移量,第三个参数为动手偏移的位置(可取SEEK_SET,SEEK_CUR和SEEK_END之一)。 read 从文献中读数据。第一个参数为源文献,第二个参数为缱绻字符串,第三个参数指明欲读数据的总字节数,第四个参数指明从源文献的某个偏移量处动手读数据。由系统调用read()调用; write 往文献里写数据。第一个参数为缱绻文献,第二个参数源字符串,第三个参数指明欲写数据的总字节数,第四个参数指明从缱绻文献的某个偏移量出动手写数据。由系统调用write()调用; mmap 将指定文献映射到指定的地址空间上。由系统调用mmap()调用; open 掀开指定文献,而况将这个文献和指定的索引结点关联起来。由系统调用open()调用; release 开释以掀开的文献,当掀开文献的援用计数(f_count)为0时,该函数被调用; fsync 文献在缓冲的数据写回磁盘;四、程度与这四者之间的关系
内核顶用于不断程度的结构体是task_struct。程度掀开文献就触及到上述4个蹙迫的数据结构:
file fs_struct files_struct namespace
每个程度都有我方的namespace。
fs_struct用于默示程度与文献系统之间的结构关系,比如现时的责任目次,程度的根目次等等。
files_struct 用于默示现时景度掀开的文献。
而关于每一个掀开的文献,由file对象来默示。
Linux中,时常用文献式样符(file descriptor)来默示一个掀开的文献,这个式样符的值经常是一个大于或等于0的整数。而这个整数,其实等于在files_struct中file数组fd的下标。关于扫数掀开的文献, 这些文献式样符会存储在open_fds的位图中。
程度与超等块、文献、索引结点、目次项的关系
从图中可知:
程度通过task_struct中的一个域files->files_struct 来了解它现时所掀开的文献对象;而咱们时常所说的文献式样符其实是程度掀开的文献对象数组的索引值。 文献对象通过域f_dentry找到它对应的dentry对象,再由dentry对象的域d_inode找到它对应的索引节点(通过索引节点又不错得到超等块的信息,也就不错得到最终操作文献的方法,在open文献的时候等于使用这么一个经过),这么就诞生了文献对象与内容的物理文献的关联。 文献对象所对应的文献操作函数列表是通过索引节点的域i_fop得到的,而i_fop最终又是通过struct super_operations *s_op来驱动化的。VFS文献系统中的inode和dentry与内容文献系统的inode和dentry有一定的关系,但不可等同。
真实磁盘文献的inode和dentry是存在于物理外存上的,但VFS中的inode和dentry是存在于内存中的,系统读取外存中的inode和dentry信息进行一定加工后,生成内存中的inode和dentry。
假造的文献系统也具有inode和dentry结构,只是这是系统左证相应的限定生成的,不存在于内容外存中。
五、磁盘与文献系统
假定一块磁盘被分为好几个分区,每个分区都是不同的文献系统。
磁盘与文献系统
参考著作:csdn-小刀刀 侵权删
https://blog.csdn.net/qq_16777851/article/details/82914514
http://www.360doc.com/content/12/0503/19/9555338_208437965.shtml
https://www.sohu.com/a/326449573_467784
本文转载自微信公众号「一口Linux」,不错通过以下二维码心思。转载本文请估计一口Linux公众号。