百篇博客系列篇.本篇为:
v64.xx 鸿蒙内核源码分析(索引节点篇) | 谁是文件系统最重要的概念 | 51 .c .h .o
文件系统相关篇为:
- v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么说一切皆是文件 | 51 .c .h .o
- v63.xx 鸿蒙内核源码分析(文件系统篇) | 用图书管理说文件系统 | 51 .c .h .o
读懂鸿蒙内核的关键线索是
LOS_DL_LIST
(双向链表),它是系列篇开篇的内容.
而读懂文件系统的关键线索是
vnode
(索引节点),
vnode
在文件系统中起承上启下的关键点.
vnode
是
BSD
的叫法,鸿蒙沿用了
BSD
的称呼,
linux
的叫法是
inode
,关于
vnode
有翻译成虚拟节点,但系列篇还是统一翻译成索引节点.
什么是 vnode
先看大佬们对其的定义
OpenBSD 定义
A vnode is an object in kernel memory that speaks the UNIX file interface (open, read, write, close, readdir, etc.). Vnodes can represent files, directories, FIFOs, domain sockets, block devices, character devices.
vnode 是内核内存中的一个对象,它使用 UNIX 文件接口(打开、读取、写入、关闭、readdir 等)。 Vnodes 可以代表文件、目录、FIFO、域套接字、块设备、字符设备。
freeBSD 定义
vnode – internal representation of a file or directory . The vnode is the focus of all file activity in UNIX. A vnode is described by struct vnode. There is a unique vnode allocated for each active file, each current directory, each mounted-on file, text file, and the root.
vnode – 文件或目录的内部表示. vnode 是 UNIX 中所有文件活动的焦点。 vnode 由 struct vnode 描述。 为每个活动文件、每个当前目录、每个挂载文件、文本文件和根分配了一个唯一的 vnode。
linux 定义
The inode (index node) is a data structure in a Unix-style file system that describes a file-system object such as a file or a directory. Each inode stores the attributes and disk block locations of the object’s data.[1] File-system object attributes may include metadata (times of last change, access, modification), as well as owner and permission data.
inode(索引节点)是 Unix 风格的文件系统中的一种数据结构,用于描述文件系统对象,例如文件或目录。 每个 inode 存储对象数据的属性和磁盘块位置。 文件系统对象属性可能包括元数据(上次更改、访问、修改的时间),以及所有者和权限数据。
综合所述,发现木有,这说的可不就是 v63.xx 鸿蒙内核源码分析(文件系统篇) | 用图书管理说文件系统 | 51 .c .h .o 中的索引页吗? 没读过的建议先阅读后再继续. 对于在硬盘中的
vnode
,在系统启动后
vnode
会被加载到内存统一管理.
vnode 长啥样
本篇主要围绕
vnode
结构体来说,说透说烂这个文件系统最关键的节点.
struct IATTR { //此结构用于记录 vnode 的属性
/* This structure is used for record vnode attr. */
unsigned int attr_chg_valid;//节点改变有效性 (CHG_MODE | CHG_UID | ... )
unsigned int attr_chg_flags;//额外的系统与用户标志(flag),用来保护该文件
unsigned attr_chg_mode; //确定了文件的类型,以及它的所有者、它的group、其它用户访问此文件的权限 (S_IWUSR | ...)
unsigned attr_chg_uid; //用户ID
unsigned attr_chg_gid; //组ID
unsigned attr_chg_size; //节点大小
unsigned attr_chg_atime; //节点最近访问时间
unsigned attr_chg_mtime; //节点对应的文件内容被修改时间
unsigned attr_chg_ctime; //节点自身被修改时间
};
// 对IATTR的修改最终将落到 vnode->vop->Chattr(vnode, attr);
enum VnodeType {//节点类型
VNODE_TYPE_UNKNOWN, /* unknown type */ //未知类型
VNODE_TYPE_REG, /* regular file */ //vnode代表一个正则文件(普通文件)
VNODE_TYPE_DIR, /* directory */ //vnode代表一个目录
VNODE_TYPE_BLK, /* block device */ //vnode代表一个块设备
VNODE_TYPE_CHR, /* char device */ //vnode代表一个字符设备
VNODE_TYPE_BCHR, /* block char mix device *///块和字符设备混合
VNODE_TYPE_FIFO, /* pipe */ //vnode代表一个管道
VNODE_TYPE_LNK, /* link */ //vnode代表一个符号链接
};
struct Vnode {//vnode并不包含文件名,因为 vnode和文件名是 1:N 的关系
enum VnodeType type; /* vnode type */ //节点类型 (文件|目录|链接...)
int useCount; /* ref count of users *///节点引用(链接)数,即有多少文件名指向这个vnode,即上层理解的硬链接数
uint32_t hash; /* vnode hash */ //节点哈希值
uint uid; /* uid for dac */ //文件拥有者的User ID
uint gid; /* gid for dac */ //文件的Group ID
mode_t mode; /* mode for dac */ //chmod 文件的读、写、执行权限
LIST_HEAD parentPathCaches; /* pathCaches point to parents */ //指向父级路径缓存,上面的都是当了爸爸节点
LIST_HEAD childPathCaches; /* pathCaches point to children */ //指向子级路径缓存,上面都是当了别人儿子的节点
struct Vnode *parent; /* parent vnode */ //父节点
struct VnodeOps *vop; /* vnode operations */ //相当于指定操作Vnode方式 (接口实现|驱动程序)
struct file_operations_vfs *fop; /* file operations */ //相当于指定文件系统
void *data; /* private data */ //文件数据block的位置,指向每种具体设备私有的成员,例如 ( drv_data | nfsnode | ....)
uint32_t flag; /* vnode flag */ //节点标签
LIST_ENTRY hashEntry; /* list entry for bucket in hash table */ //通过它挂入哈希表 g_vnodeHashEntrys[i], i:[0,g_vnodeHashMask]
LIST_ENTRY actFreeEntry; /* vnode active/free list entry */ //通过本节点挂到空闲链表和使用链表上
struct Mount *originMount; /* fs info about this vnode */ //自己所在的文件系统挂载信息
struct Mount *newMount; /* fs info about who mount on this vnode */ //其他挂载在这个节点上文件系统信息
};
解读
-
即七种文件类型,鸿蒙增加了一种VnodeType
,去掉了VNODE_TYPE_BCHR
类型,没搞懂为什么.socket
-
代表硬链接数,任何目录下都会有useCount
,.
两个文件, 前者指向当前目录,后者指向父目录.这样做的好处是由索引页指向的数据块中(目录项)存有父目录和当前目录的索引号,有了索引号就能很快的找到对应的索引页.例如当外部使用..
这样的命令时,只需在当前目录(inode)所指向的目录项中查找cd ../../../
的索引号.这样是非常的快捷和方便的,用自己勤劳的双手就能解决的困扰何必去麻烦别人呢.因为被下级留有记录所以硬链接数会增加.会增加多少呢? 举例说明,..
命令用于查看索引节点信息stat
注意[email protected]:/home/openharmony/code-v1.1.1-LTS/kernel/liteos_a$ stat kernel File: kernel Size: 4096 Blocks: 8 IO Block: 4096 directory Device: 805h/2053d Inode: 1099218 Links: 7 Access: (0755/drwxr-xr-x) Uid: ( 1000/ turing) Gid: ( 1000/ turing)
,而Inode: 1099218
代表Links: 7
被七个地方所关联,除了自己应该还有六个,在哪呢? 用kernel
命令展开ll -a
看看kernel
发现包括[email protected]:/home/openharmony/code-v1.1.1-LTS/kernel/liteos_a/kernel$ ll -a total 36 drwxr-xr-x 7 turing turing 4096 Jun 21 02:38 ./ drwxr-xr-x 21 turing turing 4096 Jul 23 19:45 ../ drwxr-xr-x 11 turing turing 4096 Jun 21 02:38 base/ -rwxr-xr-x 1 turing turing 2214 Jun 21 02:38 BUILD.gn* drwxr-xr-x 3 turing turing 4096 Jun 21 02:38 common/ drwxr-xr-x 9 turing turing 4096 Jun 21 02:38 extended/ drwxr-xr-x 2 turing turing 4096 Jun 21 02:38 include/ -rwxr-xr-x 1 turing turing 2864 Jun 21 02:38 Kconfig* drwxr-xr-x 4 turing turing 4096 Jun 21 02:38 user/
,.
在内有七个目录..
代表的是目录,但是注意其中的d
并不指向../
而是指向它的父级kernel
,其余的liteos_a
,./
,base/..
六个刚好指向common/..
,可以验证下它们的kernel
信息就知道了.inode
会发现[email protected]:/home/openharmony/code-v1.1.1-LTS/kernel/liteos_a$ stat ./kernel/. File: ./kernel/. Size: 4096 Blocks: 8 IO Block: 4096 directory Device: 805h/2053d Inode: 1099218 Links: 7 Access: (0755/drwxr-xr-x) Uid: ( 1000/ turing) Gid: ( 1000/ turing) [email protected]:/home/openharmony/code-v1.1.1-LTS/kernel/liteos_a$ stat ./kernel/base/.. File: ./kernel/base/.. Size: 4096 Blocks: 8 IO Block: 4096 directory Device: 805h/2053d Inode: 1099218 Links: 7 Access: (0755/drwxr-xr-x) Uid: ( 1000/ turing) Gid: ( 1000/ turing) [email protected]:/home/openharmony/code-v1.1.1-LTS/kernel/liteos_a$ stat ./kernel/.. File: ./kernel/.. Size: 4096 Blocks: 8 IO Block: 4096 directory Device: 805h/2053d Inode: 1099213 Links: 21 Access: (0755/drwxr-xr-x) Uid: ( 1000/ turing) Gid: ( 1000/ turing)
和./kernel/.
的./kernel/base/..
都是Inode
,而1099218
的为./kernel/..
是不一样.1099213
- 正常情况下一个目录的
,.
是不一样的,但只有一个目录例外,就是..
/
其[email protected]:/$ stat /. File: /. Size: 4096 Blocks: 8 IO Block: 4096 directory Device: 805h/2053d Inode: 2 Links: 20 [email protected]:/$ stat /.. File: /.. Size: 4096 Blocks: 8 IO Block: 4096 directory Device: 805h/2053d Inode: 2 Links: 20
结果都是一样的,inode
,那么问题来了,Inode: 2
为 和Inode
的节点去哪了呢?1
-
,uid
代表文件所属用户/用户组和权限.discretionary access control (DAC) 自主访问控制.在计算机安全中,自主访问控制 (DAC) 是一种由可信计算机系统评估标准定义的访问控制“作为一种根据对象所属的主体和组的身份限制对对象的访问的手段. 控制方式是自由的,因为具有特定访问权限的主体能够将该权限(可能是间接地)传递给任何其他主体(除非受到强制访问控制的约束)。与其对应的是 mandatory access control (MAC) 强制访问控制.gid``mode
-
路径缓存链表,用户快速查找父子信息.parentPathCaches``childPathCaches
-
指向父节点,父节点不管是什么内容,一样都是文件,都用parent
描述.Vnode
-
这是对VnodeOps *vop
的操作,vnode
本身也是数据,存储在索引表中,记录了用户,用户组,权限,时间等信息,这部分信息是可以修改的,就需要接口来维护,便是vnode
.VnodeOps
看到没有里面的所有方法都是对索引节点(索引页)的增删改查操作,并不操作索引节点指向的数据块(图书区)内容.struct VnodeOps { int (*Create)(struct Vnode *parent, const char *name, int mode, struct Vnode **vnode);//创建节点 int (*Lookup)(struct Vnode *parent, const char *name, int len, struct Vnode **vnode);//查询节点 //Lookup向底层文件系统查找获取inode信息 int (*Open)(struct Vnode *vnode, int fd, int mode, int flags);//打开节点 int (*Close)(struct Vnode *vnode);//关闭节点 int (*Reclaim)(struct Vnode *vnode);//回收节点 int (*Unlink)(struct Vnode *parent, struct Vnode *vnode, const char *fileName);//取消硬链接 int (*Rmdir)(struct Vnode *parent, struct Vnode *vnode, const char *dirName);//删除目录节点 int (*Mkdir)(struct Vnode *parent, const char *dirName, mode_t mode, struct Vnode **vnode);//创建目录节点 int (*Readdir)(struct Vnode *vnode, struct fs_dirent_s *dir);//读目录节点 int (*Opendir)(struct Vnode *vnode, struct fs_dirent_s *dir);//打开目录节点 int (*Rewinddir)(struct Vnode *vnode, struct fs_dirent_s *dir);//定位目录节点 int (*Closedir)(struct Vnode *vnode, struct fs_dirent_s *dir);//关闭目录节点 int (*Getattr)(struct Vnode *vnode, struct stat *st);//获取节点属性 int (*Setattr)(struct Vnode *vnode, struct stat *st);//设置节点属性 int (*Chattr)(struct Vnode *vnode, struct IATTR *attr);//改变节点属性(change attr) int (*Rename)(struct Vnode *src, struct Vnode *dstParent, const char *srcName, const char *dstName);//重命名 .... }
- 那么对数据块(图书区)的修改用什么方法呢? 答案是:
,file_operations_vfs *fop
/* This structure is provided by devices when they are registered with the * system. It is used to call back to perform device specific operations. */ //该结构由设备在向系统注册时提供,它用于回调以执行特定于设备的操作。 struct file_operations_vfs { /* The device driver open method differs from the mountpoint open method */ int (*open)(struct file *filep); /* The following methods must be identical in signature and position because * the struct file_operations and struct mountp_operations are treated like * unions. */ int (*close)(struct file *filep); ssize_t (*read)(struct file *filep, char *buffer, size_t buflen); ssize_t (*write)(struct file *filep, const char *buffer, size_t buflen); off_t (*seek)(struct file *filep, off_t offset, int whence); int (*ioctl)(struct file *filep, int cmd, unsigned long arg); int (*mmap)(struct file* filep, struct VmMapRegion *region); /* The two structures need not be common after this point */ .... };
看参数就知道,很是给file_operations_vfs
的上层的使用的,它是夹在应用层和vnode
中间的一层,是vnode
起承上启下作用的上层,具体为什么要有vnode
存在后续会详细说明,总之通过file
找到file
,从而对vnode
指向的内容区进行修改.我们在应用层比如修改一个vnode
,创建一个ppt
文档这些操作就是通过word
file_operations_vfs
.
一定要搞清楚
和VnodeOps
二者的区别,一个是对索引页的操作,一个是对索引页指向的内容的操作.file_operations_vfs
-
使用了一个data
类型,这是私有格式数据,说明运行时才知道是什么类型,就像一个没有任何提示信息的私人密码箱一样,是打不开的,不知道顺序乱开只会毁掉数据,只有密码箱那边派人来了才能开,而这人就是各种不同的文件系统.每种文件系统如何读取数据的方式是不同的,差异化的就有接口内部来实现了.对外是相同的,无非都是读读写写.void
-
使用哈希算法来检索hashEntry
vnode
-
:这个就不用介绍了,双向链表是内核最重要的结构体,通过它挂到全局空闲链表和使用链表上.actFreeEntry
-
和originMount
是挂载相关的,任务文件系统都需要先挂载到根文件系统下才能使用.关于挂载后续有详细介绍.newMount
鸿蒙内核源码分析.总目录
v08.xx 鸿蒙内核源码分析(总目录) | 百万汉字注解 百篇博客分析 | 51 .c .h .o
百万汉字注解.百篇博客分析
百万汉字注解 >> 精读鸿蒙源码,中文注解分析, 深挖地基工程,大脑永久记忆,四大码仓每日同步更新< gitee | github | csdn | coding >
百篇博客分析 >> 故事说内核,问答式导读,生活式比喻,表格化说明,图形化展示,主流站点定期更新中< 51cto | csdn | harmony | osc >
关注不迷路.代码即人生
热爱是所有的理由和答案 - turing
原创不易,欢迎转载,但麻烦请注明出处.