索引节点和inode的指针结构浅析

本文详细介绍了索引节点(inode)的概念,它是文件系统中存储文件元数据的数据结构。inode包含文件的属性信息和数据块位置。在Unix和Linux文件系统中,如EXT3,使用inode来组织文件的数据块。同时,文章也探讨了struct file结构,它代表了操作系统中打开的文件,包含了文件的偏移量、模式、权限等信息,以及与之相关的操作函数和预读缓存等特性。

本博文参考维基百科:

索引节点是一个数据结构用来代表一个文件系统对象,其可以是各种事情,包括一个文件或一个目录。每个inode存储文件系统对象的数据的属性和磁盘块位置(多个)。的文件系统对象的属性可包括操纵元数据(例如,修改,JVSANTEN的访问,修改时间),以及雇主和权限数据(例如组ID,用户ID,权限)。

目录是分配给索引节点名称的列表。该目录包含自己,其母公司,及其每个孩子的条目。

详细信息
文件描述符,文件表和inode表中的Unix
文件系统依赖于数据结构有关的文件,文件内容旁边。前者被称为元数据描述数据-data。每个文件与相关联的索引节点,它是由一个整数,通常被称为所识别的i编号或节点号码。
索引节点存储有关的文件和目录(文件夹),如文件所有权,访问模式(读,写,执行权限),以及文件类型的信息。在许多类型的文件系统实现的,索引节点的最大数目被固定在创建文件系统,限制文件的文件系统能够容纳的最大数量。在文件系统中的inode一个典型的分配方案是总规模的百分之一。
inode号指标在设备上的已知位置的inode表。从节点号码,内核的文件系统驱动程序都可以访问的索引节点的内容,其中包括文件​​的位置 - 从而允许访问该文件。
一个文件的inode编号可以使用发现LS -i命令。该LS -i命令打印在报告的第一列的i节点号。
一些Unix风格文件系统,如ReiserFS的省略一节点表,但必须存储以便提供等效的功能等效的数据。该数据可以被称为统计数据,在参照统计 系统调用提供数据给节目。


文件名和目录的含义:
i节点不包含文件名,唯一的其他文件的元数据。
Unix目录是缔合结构,其每一个包含一个文件名,一个节点号码的列表。
文件系统驱动程序必须搜索寻找一个特定的文件名,目录,然后把文件名转化为正确的对应索引节点号。
该数据的操作系统内核的内存中表示称为结构的inode中的Linux。源自系统BSD使用术语v节点,随着v的v节点指的是内核的虚拟文件系统层。


POSIX的inode描述
该POSIX是强烈影响的传统标准要求的文件系统行为的UNIX文件系统。常规文件必须具有以下属性:
在该文件的大小的字节。
设备ID(本标识包含文件的设备)。
该用户ID文件的所有者。
该组ID的文件。
该文件模式,它决定了文件类型以及文件的所有者,其组以及其他人可以访问该文件。
其他系统和用户的标志,以进一步保护文件(限制其使用和修改)。
时间戳告诉当inode中的最后修改(的ctime,索引节点更改时间),该文件内容的最后一次修改(修改时间,修改时间),并最后访问(的atime,访问时间)。
一个链接数,告诉许多如何硬链接指向的inode。
指向存储文件内容的磁盘块(见inode的指针结构)。

该统计系统调用检索文件的inode编号和一些在inode信息。


内联
它可以使感非常小的文件存储在inode本身节省空间(无需数据块)和查找时间(不需要进一步的接盘)。此文件系统功能称为内联。索引节点和文件数据的严格分离从而可以利用现代文件系统时不再承担。


如果一个文件的数据中分配给指针的数据空间配合,可以方便地使用此空间。例如,EXT2及其后继存储符号链接(通常是文件名 ​​)在本方式的数据,如果数据是不超过60个字节(“快速符号链接”)。[8]


EXT4有一个名为inline_data一个文件系统选项,当文件系统创建过程中启用,将允许EXT4执行内联。作为一个inode的大小是有限的,这仅适用于非常小的文件。



inode的指针结构

i节点指针结构是由所采用的一种结构的inode在一个文件中的Unix文件系统(UFS)列出一个文件的的地址的数据块。它也被许多相关的文件系统,包括采用EXT3文件系统,颇受Linux用户。


结构
在过去,该结构可能是由十三岁指针,但大多数现代的文件系统使用15指针。这些指针包括(在inode假设15球)的:
十二指针直接指向该文件的数据块(直接指针)
一个单独的间接指针(指向指针的块,然后指向文件的数据块的指针)
一个双重间接指针(指向指向指针的其他块的然后指向该文件的数据块的指针块的指针)

一个三重间接指针(指向指向指向指针的其他块的然后指向该文件的数据块的指针的其他块的指针块的指针)


主要特点
固定的逻辑块大小

该结构本文附带图中所示部分。该结构允许的inode与固定的逻辑块大小描述文件系统非常大的文件。中央的机制是,地址(也称为块间接块)根据需要仅分配。例如,一个12块文件将被仅使用索引节点,因为它的块装配到可用的直接指针的数量描述。然而,13块文件需要一个间接块包含第十三地址。


位置数据易于
i节点指针结构不仅允许文件容易地分配给非连续块,它也允许在一个文件中的特定位置的数据可以很容易地定位。因为逻辑块大小是固定的,这是可能的。例如,如果每个块为8KB,文件数据,在120至128 kB的将被指向的第一间接块的第四指针(假定12直接指针在inode指针结构)。


间接块

不像i节点,其被固定在数量和在文件系统中的一个特殊的部分分配,所述间接块可以是任何数目的,并在文件系统中作为数据块的相同的部分被分配。在间接块的指针的数目是依赖于块的指针的块大小和尺寸。例如:用一个512字节的块大小,和4个字节的块的指针,每个间接块可以由128(512/4)的指针。




关联到文件的目录入口( dentry )结构. 设备驱动编写者正常地不需要关心 dentry 结构, 除了作为 filp->f_dentry->d_inode 存取 inode 结构.
file 结构如下所示:
  struct file {
  union {
  struct list_head fu_list; 文件对象链表指针linux/include/linux/list.h
  struct rcu_head fu_rcuhead; RCU(Read-Copy Update)是Linux 2.6内核中新的锁机制
  } f_u;
  struct path f_path; 包含dentry和mnt两个成员,用于确定文件路径
  #define f_dentry f_path.dentry f_path的成员之一,当前文件的dentry结构
  #define f_vfsmnt f_path.mnt 表示当前文件所在文件系统的挂载根目录
  const struct file_operations *f_op; 与该文件相关联的操作函数
  atomic_t f_count; 文件的引用计数(有多少进程打开该文件)
  unsigned int f_flags; 对应于open时指定的flag
  mode_t f_mode; 读写模式:open的mod_t mode参数
  off_t f_pos; 该文件在当前进程中的文件偏移量
  struct fown_struct f_owner; 该结构的作用是通过信号进行I/O时间通知的数据。
  unsigned int f_uid, f_gid; 文件所有者id,所有者组id
  struct file_ra_state f_ra; 在linux/include/linux/fs.h中定义,文件预读相关
  unsigned long f_version;
  #ifdef CONFIG_SECURITY
  void *f_security;
  #endif
  
  void *private_data;
  #ifdef CONFIG_EPOLL
  
  struct list_head f_ep_links;
  spinlock_t f_ep_lock;
  #endif
  struct address_space *f_mapping;
  };


文件结构体代表一个打开的文件,系统中的每个打开的文件在内核空间都有一个关联的struct file。它由内核在打开文件时创建,并传递给在文件上进行操作的任何函数。在文件的所有实例都关闭后,内核释放这个数据结构。在内核创建和驱动源码中,struct file的指针通常被命名为file或filp。



接下来,内核中用inode结构表示具体的文件,而用file结构表示打开的文件描述符。Linux2.6.27内核中,inode结构体具体定义如下:

struct inode {
struct hlist_node    i_hash;
struct list_head    i_list;
struct list_head    i_sb_list;
struct list_head    i_dentry;
unsigned long        i_ino;
atomic_t        i_count;
unsigned int        i_nlink;
uid_t            i_uid;
gid_t            i_gid;
 dev_t            i_rdev;   //该成员表示设备文件的inode结构,它包含了真正的设备编号。
u64            i_version;
loff_t            i_size;
#ifdef __NEED_I_SIZE_ORDERED
seqcount_t        i_size_seqcount;
#endif
struct timespec        i_atime;
struct timespec        i_mtime;
struct timespec        i_ctime;
unsigned int        i_blkbits;
blkcnt_t        i_blocks;
unsigned short          i_bytes;
umode_t            i_mode;
spinlock_t        i_lock;    
struct mutex        i_mutex;
struct rw_semaphore    i_alloc_sem;
const struct inode_operations    *i_op;
const struct file_operations    *i_fop;    
struct super_block    *i_sb;
struct file_lock    *i_flock;
struct address_space    *i_mapping;
struct address_space    i_data;
#ifdef CONFIG_QUOTA
struct dquot        *i_dquot[MAXQUOTAS];
#endif
struct list_head    i_devices;
union {
struct pipe_inode_info    *i_pipe;
struct block_device    *i_bdev;
 struct cdev        *i_cdev; //该成员表示字符设备的内核的 内部结构。当inode指向一个字符设备文件时,该成员包含了指向struct cdev结构的指针,其中cdev结构是字符设备结构体。
};
int            i_cindex;


__u32            i_generation;


#ifdef CONFIG_DNOTIFY
unsigned long        i_dnotify_mask;
struct dnotify_struct    *i_dnotify;
#endif


#ifdef CONFIG_INOTIFY
struct list_head    inotify_watches;
struct mutex        inotify_mutex;    
#endif


unsigned long        i_state;
unsigned long        dirtied_when;    


unsigned int        i_flags;


atomic_t        i_writecount;
#ifdef CONFIG_SECURITY
void            *i_security;
#endif
void            *i_private;
};

在前面的课程里,我你多次提到过etcd数据存储在boltdb。那么boltdb是如何组织你的key-value数据的呢?当你读写一个key时,boltdb是如何工作的?今天我将通过一个写请求在boltdb中执行的简要流程,分析其背后的boltdb的磁盘文件布局,帮助你了解page、node、bucket等核心数据结构的原理与作用,搞懂boltdb基于B+ tree、各类page实现查找、更新、事务提交的原理,让你明白etcd为什么适合读多写少的场景。boltdb磁盘布局在介绍一个put写请求在boltdb中执行原理前,我先给你从整体上介绍下平时你所看到的etcd db文件的磁盘布局,让你了解下db文件的物理存储结构。boltdb文件指的是你etcd数据目录下的member/snap/db的文件, etcd的key-value、lease、meta、member、cluster、auth等所有数据存储在其中。etcd启动的时候,会通过mmap机制将db文件映射到内存,后续可从内存中快速读取文件中的数据。写请求通过fwritefdatasync来写入、持久化数据到磁盘。上图是我给你画的db文件磁盘布局,从图中的左边部分你可以看到,文件的内容由若干个page组成,一般情况下page size为4KB。page按照功能可分为元数据页(meta page)、B+ tree索引节点页(branch page)、B+ tree 叶子节点页(leaf page)、空闲页管理页(freelist page)、空闲页(free page)。文件最开头的两个page是固定的db元数据meta page,空闲页管理页记录了db中哪些页是空闲、可使用的。索引节点页保存了B+ tree的内部节点,如图中的右边部分所示,它们记录了key值,叶子节点页记录了B+ tree中的key-valuebucket数据。boltdb逻辑上通过B+ tree来管理branch/leaf page, 实现快速查找、写入key-value数据。boltdb API了解完boltdb的磁盘布局后,那么如果要在etcd中执行一个put请求,boltdb中是如何执行的呢? boltdb作为一个库,提供了什么API给client访问写入数据?boltdb提供了非常简单的API给上层业务使用,当我们执行一个put hello为world命令时,boltdb实际写入的key是版本号,value为mvccpb.KeyValue结构体。这里我们简化下,假设往key bucket写入一个key为r94,value为world的字符串,其核心代码如下:// 打开boltdb文件,获取db对象db,err := bolt.Open("db", 0600, nil)if err != nil { log.Fatal(err)}defer db.Close()// 参数true表示创建一个写事务,false读事务tx,err := db.Begin(true)if err != nil { return err}defer tx.Rollback()// 使用事务对象创建key bucketb,err := tx.CreatebucketIfNotExists([]byte("key"))if err != nil { return err}// 使用bucket对象更新一个keyif err := b.Put([]byte("r94"),[]byte("world")); err != nil { return err}// 提交事务if err := tx.Commit(); err != nil { return err}12345678910111213141516171819202122232425如上所示,通过boltdb的Open API,我们获取到boltdb的核心对象db实例后,然后通过db的Begin API开启写事务,获得写事务对象tx。通过写事务对象tx, 你可以创建bucket。这里我们创建了一个名为key的bucket(如果不存在),并使用bucket API往其中更新了一个key为r94,value为world的数据。最后我们使用写事务的Commit接口提交整个事务,完成bucket创建key-value数据写入。看起来是不是非常简单,神秘的boltdb,并未有我们想象的那么难。然而其API简单的背后却是boltdb的一系列巧妙的设计实现。一个key-value数据如何知道该存储在db在哪个page?如何快速找到你的key-value数据?事务提交的原理又是怎样的呢?接下来我就浅析boltdb背后的奥秘。核心数据结构介绍上面我们介绍boltdb的磁盘布局时提到,boltdb整个文件由一个个page组成。最开头的两个page描述db元数据信息,而它正是在client调用boltdb Open API时被填充的。那么描述磁盘页面的page数据结构是怎样的呢?元数据页又含有哪些核心数据结构?boltdb本身自带了一个工具bbolt,它可以按页打印出db文件的十六进制的内容,下面我们就使用此工具来揭开db文件的神秘面纱。下图左边的十六进制是执行如下bbolt dump命令,所打印的boltdb第0页的数据,图的右边是对应的page磁盘页结构meta page的数据结构。$ ./bbolt dump ./infra1.etcd/member/snap/db 01一看上图中的十六进制数据,你可能很懵,没关系,在你了解page磁盘页结构、meta page数据结构后,你就能读懂其含义了。page磁盘页结构我们先了解下page磁盘页结构,如上图所示,它由页ID(id)、页类型(flags)、数量(count)、溢出页数量(overflow)、页面数据起始位置(ptr)字段组成。页类型目前有如下四种:0x01表示branch page,0x02表示leaf page,0x04表示meta page,0x10表示freelist page。数量字段仅在页类型为leafbranch时生效,溢出页数量是指当前页面数据存放不下,需要向后再申请overflow个连续页面使用,页面数据起始位置指向page的载体数据,比如meta page、branch/leaf等page的内容。meta page数据结构第0、1页我们知道它是固定存储db元数据的页(meta page),那么meta page它为了管理整个boltdb含有哪些信息呢?如上图中的meta page数据结构所示,你可以看到它由boltdb的文件标识(magic)、版本号(version)、页大小(pagesize)、boltdb的根bucket信息(root bucket)、freelist页面ID(freelist)、总的页面数量(pgid)、上一次写事务ID(txid)、校验码(checksum)组成。meta page十六进制分析了解完page磁盘页结构meta page数据结构后,我再结合图左边的十六进数据你简要分析下其含义。上图中十六进制输出的是db文件的page 0页结构,左边第一列表示此行十六进制内容对应的文件起始地址,每行16个字节。结合page磁盘页meta page数据结构我们可知,第一行前8个字节描述pgid(忽略第一列)是0。接下来2个字节描述的页类型, 其值为0x04表示meta page, 说明此页的数据存储的是meta page内容,因此ptr开始的数据存储的是meta page内容。正如你下图中所看到的,第二行首先含有一个4字节的magic number(0xED0CDAED),通过它来识别当前文件是否boltdb,接下来是两个字节描述boltdb的版本号0x2, 然后是四个字节的page size大小,0x1000表示4096个字节,四个字节的flags为0。第三行对应的就是meta page的root bucket结构(16个字节),它描述了boltdb的root bucket信息,比如一个db中有哪些bucket, bucket里面的数据存储在哪里。第四行中前面的8个字节,0x3表示freelist页面ID,此页面记录了db当前哪些页面是空闲的。后面8个字节,0x6表示当前db总的页面数。第五行前面的8个字节,0x1a表示上一次的写事务ID,后面的8个字节表示校验码,用于检测文件是否损坏。了解完db元数据页面原理后,那么boltdb是如何根据元数据页面信息快速找到你的bucketkey-value数据呢?这就涉及到了元数据页面中的root bucket,它是个至关重要的数据结构。下面我们看看它是如何管理一系列bucket、帮助我们查找、写入key-value数据到boltdb中。bucket数据结构如下命令所示,你可以使用bbolt buckets命令,输出一个db文件的bucket列表。执行完此命令后,我们可以看到之前介绍过的auth/lease/meta等熟悉的bucket,它们都是etcd默认创建的。那么boltdb是如何存储、管理bucket的呢?$ ./bbolt buckets ./infra1.etcd/member/snap/dbalarmauthauthRolesauthUsersclusterkeyleasemembersmembers_removedmeta123456789101112在上面我们提到过meta page中的,有一个名为root、类型bucket的重要数据结构,如下所示,bucket由rootsequence两个字段组成,root表示该bucket根节点的page id。注意meta page中的bucket.root字段,存储的是db的root bucket页面信息,你所看到的key/lease/auth等bucket都是root bucket的子bucket。type bucket struct { root pgid // page id of the bucket's root-level page sequence uint64 // monotonically incrementing, used by NextSequence()}1234上面meta page十六进制图中,第三行的16个字节就是描述的root bucket信息。root bucket指向的page id为4,page id为4的页面是什么类型呢? 我们可以通过如下bbolt pages命令看看各个page类型元素数量,从下图结果可知,4号页面为leaf page。$ ./bbolt pages ./infra1.etcd/member/snap/dbID TYPE ITEMS OVRFLW======== ========== ====== ======0 meta 01 meta 02 free3 freelist 24 leaf 105 free123456789通过上面的分析可知,当bucket比较少时,我们子bucket数据可直接从meta page里指向的leaf page中找到。leaf pagemeta page的root bucket直接指向的是page id为4的leaf page, page flag为0x02, leaf page它的磁盘布局如下图所示,前半部分是leafPageElement数组,后半部分是key-value数组。leafPageElement包含leaf page的类型flags, 通过它可以区分存储的是bucket名称还是key-value数据。当flag为bucketLeafFlag(0x01)时,表示存储的是bucket数据,否则存储的是key-value数据,leafPageElement它还含有key-value的读取偏移量,key-value大小,根据偏移量key-value大小,我们就可以方便地从leaf page中解析出所有key-value对。当存储的是bucket数据的时候,key是bucket名称,value则是bucket结构信息。bucket结构信息含有root page信息,通过root page(基于B+ tree查找算法),你可以快速找到你存储在这个bucket下面的key-value数据所在页面。从上面分析你可以看到,每个子bucket至少需要一个page来存储其下面的key-value数据,如果子bucket数据量很少,就会造成磁盘空间的浪费。实际上boltdb实现了inline bucket,在满足一些条件限制的情况下,可以将小的子bucket内嵌在它的父亲叶子节点上,友好的支持了大量小bucket。为了方便大家快速理解核心原理,本节我们讨论的bucket是假设都是非inline bucket。那么boltdb是如何管理大量bucket、key-value的呢?branch pageboltdb使用了B+ tree来高效管理所有子bucketkey-value数据,因此它可以支持大量的bucketkey-value,只不过B+ tree的根节点不再直接指向leaf page,而是branch page索引节点页。branch page flags为0x01。它的磁盘布局如下图所示,前半部分是branchPageElement数组,后半部分是key数组。branchPageElement包含key的读取偏移量、key大小、子节点的page id。根据偏移量key大小,我们就可以方便地从branch page中解析出所有key,然后二分搜索匹配key,获取其子节点page id,递归搜索,直至从bucketLeafFlag类型的leaf page中找到目的bucket name。注意,boltdb在内存中使用了一个名为node的数据结构,来保存page反序列化的结果。下面我给出了一个boltdb读取page到node的代码片段,你可以直观感受下。func (n *node) read(p *page) { n.pgid = p.id n.isLeaf = ((p.flags & leafPageFlag) != 0) n.inodes = make(inodes, int(p.count)) for i := 0; i < int(p.count); i++ { inode := &n.inodes[i] if n.isLeaf { elem := p.leafPageElement(uint16(i)) inode.flags = elem.flags inode.key = elem.key() inode.value = elem.value() } else { elem := p.branchPageElement(uint16(i)) inode.pgid = elem.pgid inode.key = elem.key() } }12345678910111213141516171819从上面分析过程中你会发现,boltdb存储bucketkey-value原理是类似的,将page划分成branch page、leaf page,通过B+ tree来管理实现。boltdb为了区分leaf page存储的数据类型是bucket还是key-value,增加了标识字段(leafPageElement.flags),因此key-value的数据存储过程我就不再重复分析了。freelist介绍完bucket、key-value存储原理后,我们再看meta page中的另外一个核心字段freelist,它的作用是什么呢?我们知道boltdb将db划分成若干个page,那么它是如何知道哪些page在使用中,哪些page未使用呢?答案是boltdb通过meta page中的freelist来管理页面的分配,freelist page中记录了哪些页是空闲的。当你在boltdb中删除大量数据的时候,其对应的page就会被释放,页ID存储到freelist所指向的空闲页中。当你写入数据的时候,就可直接从空闲页中申请页面使用。下面meta page十六进制图中,第四行的前8个字节就是描述的freelist信息,page id为3。我们可以通过bbolt page命令查看3号page内容,如下所示,它记录了25为空闲页,与我们上面通过bbolt pages命令所看到的信息一致。$ ./bbolt page ./infra1.etcd/member/snap/db 3page ID: 3page Type: freelistTotal Size: 4096 bytesItem Count: 2Overflow: 025123456789下图是freelist page存储结构,pageflags为0x10,表示freelist类型的页,ptr指向空闲页id数组。注意在boltdb中支持通过多种数据结构(数组hashmap)来管理free page,这里我介绍的是数组。Open原理了解完核心数据结构后,我们就很容易搞懂boltdb Open API的原理了。首先它会打开db文件并对其增加文件锁,目的是防止其他进程也以读写模式打开它后,操作metafree page,导致db文件损坏。其次boltdb通过mmap机制将db文件映射到内存中,并读取两个meta page到db对象实例中,然后校验meta page的magic、version、checksum是否有效,若两个meta page都无效,那么db文件就出现了严重损坏,导致异常退出。Put原理那么成功获取db对象实例后,通过bucket API创建一个bucket、发起一个Put请求更新数据时,boltdb是如何工作的呢?根据我们上面介绍的bucket的核心原理,它首先是根据meta page中记录root bucket的root page,按照B+ tree的查找算法,从root page递归搜索到对应的叶子节点page面,返回key名称、leaf类型。如果leaf类型为bucketLeafFlag,且key相等,那么说明已经创建过,不允许bucket重复创建,结束请求。否则往B+ tree中添加一个flag为bucketLeafFlag的key,key名称为bucket name,value为bucket的结构。创建完bucket后,你就可以通过bucket的Put API发起一个Put请求更新数据。它的核心原理跟bucket类似,根据子bucket的root page,从root page递归搜索此key到leaf page,如果没有找到,则在返回的位置处插入新keyvalue。为了方便你理解B+ tree查找、插入一个key原理,我给你构造了的一个max degree为5的B+ tree,下图是key r94的查找流程图。那么如何确定这个key的插入位置呢?首先从boltdb的key bucket的root page里,二分查找大于等于r94的key所在page,最终找到key r9指向的page(流程1)。r9指向的page是个leaf page,B+ tree需要确保叶子节点key的有序性,因此同样二分查找其插入位置,将key r94插入到相关位置(流程二)。在核心数据结构介绍中,我你提到boltdb在内存中通过node数据结构来存储page磁盘页内容,它记录了key-value数据、page id、parent及children的node、B+ tree是否需要进行重平衡分裂操作等信息。因此,当我们执行完一个put请求时,它只是将值更新到boltdb的内存node数据结构里,并未持久化到磁盘中。事务提交原理那么boltdb何时将数据持久化到db文件中呢?当你的代码执行tx.Commit API时,它才会将我们上面保存到node内存数据结构中的数据,持久化到boltdb中。下图我给出了一个事务提交的流程图,接下来我就分别你简要分析下各个核心步骤。首先从上面put案例中我们可以看到,插入了一个新的元素在B+ tree的叶子节点,它可能已不满足B+ tree的特性,因此事务提交时,第一步首先要调整B+ tree,进行重平衡、分裂操作,使其满足B+ tree树的特性。上面案例里插入一个key r94后,经过重平衡、分裂操作后的B+ tree如下图所示。在重平衡、分裂过程中可能会申请、释放free page,freelist所管理的free page也发生了变化。因此事务提交的第二步,就是持久化freelist。注意,在etcd v3.4.9中,为了优化写性能等,freelist持久化功能是关闭的。etcd启动获取boltdb db对象的时候,boltdb会遍历所有page,构建空闲页列表。事务提交的第三步就是将client更新操作产生的dirty page通过fdatasync系统调用,持久化存储到磁盘中。最后,在执行写事务过程中,meta page的txid、freelist等字段会发生变化,因此事务的最后一步就是持久化meta page。通过以上四大步骤,我们就完成了事务提交的工作,成功将数据持久化到了磁盘文件中,安全地完成了一个put操作。小结最后我们来小结下今天的内容。首先我通过一幅boltdb磁盘布局图bbolt工具,为你解密了db文件的本质。db文件由meta page、freelist page、branch page、leaf page、free page组成。随后我结合bbolt工具,你深入介绍了meta page、branch page、leaf page、freelist page的数据结构,帮助你了解key、value数据是如何存储到文件中的。然后我通过分析一个put请求在boltdb中如何执行的。我从Open API获取db对象说起,介绍了其通过mmap将db文件映射到内存,构建meta page,校验meta page的有效性,再到创建bucket,通过bucket API往boltdb添加key-value数据。添加bucketkey-value操作本质,是从B+ tree管理的page中找到插入的页位置,并将数据更新到page的内存node数据结构中。真正持久化数据到磁盘是通过事务提交执行的。它首先需要通过一系列重平衡、分裂操作,确保boltdb维护的B+ tree满足相关特性,其次需要持久化freelist page,并将用户更新操作产生的dirty page数据持久化到磁盘中,最后则是持久化meta page。
04-06
<think>我们正在讨论磁盘索引节点Inode)的概念。用户询问的是“磁盘索引节点inode节点”的区别。实际上,根据引用的资料,这两个术语指的是同一个概念,即索引节点Inode)。但是,为了确保回答的准确性,我们需要仔细分析引用内容。 引用[1]提到:“Linux中的索引节点Inode)是文件系统中用于存储文件的元信息的数据结构。” 这里明确使用了“索引节点Inode)”,表明Inode就是索引节点的英文名称。 引用[2]中,同样提到了“索引节点inode”,并且将属性存储在inode中。 引用[3]的标题就是“磁盘索引节点-Inode”,并且在文中说明:“这种储存文件元信息的区域就叫做inode索引节点)”。 因此,我们可以得出结论:磁盘索引节点inode节点是同一个概念的不同表述。其中,“磁盘索引节点”强调了该节点存储在磁盘上,而“inode”是“index node”(索引节点)的缩写。在Linux文件系统中,它们是同义词。 但是,为了严谨,我们还需要注意:在文件系统的上下文中,inode通常指的是磁盘上的数据结构,即存储在磁盘上的索引节点。而有时在内存中也会有一个对应的内存inode(用于文件打开时的操作),但用户问题中明确提到了“磁盘索引节点”,所以这里我们只关注磁盘上的结构。 因此,回答应该是:磁盘索引节点inode节点没有区别,它们指的是同一个概念。 然而,用户可能看到不同的术语而产生了疑惑,我们需要解释清楚这两个术语的来源关系。 回答结构: 1. 明确回答:磁盘索引节点inode节点是同一个概念。 2. 解释术语:inode索引节点(index node)的缩写。 3. 详细说明:在磁盘上,每个文件都有一个对应的inode节点,存储文件的元信息数据块指针。 4. 补充说明:有时在内存中也会使用inode结构,但磁盘索引节点特指磁盘上的存储结构。 同时,按照要求,我们需要在回答中引用参考的段落,并在末尾添加引用标识。 最后,生成相关问题。 注意:在回答中,行内数学表达式使用$...$,独立公式使用$$...$$。但这个问题可能不需要数学公式,所以根据实际情况处理。 另外,注意使用中文回答。 最终回答: </think> ### 磁盘索引节点inode节点的区别 磁盘索引节点(Disk Inode)与inode节点本质上是同一个概念的不同表述。通过分析引用资料,可以明确以下核心结论: 1. **术语统一性** - "inode" 是 **索引节点(index node)** 的缩写,而"磁盘索引节点"是强调其物理存储位置的描述性名称。 - 引用[3]明确指出:*"储存文件元信息的区域就叫做inode索引节点)"*[^3]。 - 引用[1][2]同样将二者视为同一概念,描述为文件属性的存储结构[^1][^2]。 2. **功能与存储位置** - **磁盘索引节点**:特指**存储在磁盘上**的索引节点结构,包含文件的元数据(如权限、大小、时间戳)指向数据块的指针。 如引用[3]所述:*"文件存储在扇区中...存储文件元信息的区域就叫做inode"*,每个节点占用固定空间(通常128字节)。 - **内存inode**:当文件被打开时,系统会将磁盘索引节点加载到内存中,加入动态信息(如引用计数)。但用户问题中的"inode节点"通常默认指磁盘版本。 3. **关键区分点总结** | **特性** | 磁盘索引节点 | 内存inode | |------------------|--------------------------|------------------------| | **存储位置** | 磁盘 | 内存 | | **内容** | 静态元数据+数据块指针 | 磁盘inode副本+动态状态 | | **生命周期** | 持久化(文件存在即存在) | 临时(文件关闭后释放) | | **用户查询对象** | 通过`df -i`或`stat`命令查看 | 内核管理,用户不可直接访问 | 4. **操作示例** 引用[4]展示了检查磁盘索引节点使用率的命令[^4]: ```bash echo "Detailed Inode usage: $(pwd)"; find -maxdepth 1 -type d | cut -d\/ -f2 | grep -xv . | sort | while read d; do c=$(find "$d" | wc -l); echo "$c - $d"; done ``` ### 结论 **磁盘索引节点inode节点是同一概念的两种表述**:前者强调物理存储位置(磁盘),后者是通用术语。在Linux文件系统中,二者均指存储文件元数据(非内容)的核心数据结构。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值