|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
+ ^- t0 S3 z4 q. v虚拟文件系统(VFS)是linux内核和具体I/O设备之间的封装的一层共通访问接口,通过这层接口,linux内核可以以同一的方式访问各种I/O设备。
) K) x9 ?" |9 N: Z/ C* g$ \0 ^8 N) X' ?
虚拟文件系统本身是linux内核的一部分,是纯软件的东西,并不需要任何硬件的支持。
) C1 Y c. M7 f# e" W8 M! D" U
: w9 [6 t& G! Q( }
4 Z9 Q* w2 L/ s
7 m$ g* H) R! r+ B主要内容:$ Y8 e; Q; e9 A" D! [1 q
6 X% G% G. q4 X) H虚拟文件系统的作用
5 d! z. Z- T* m虚拟文件系统的4个主要对象
' n+ h A D" [8 f文件系统相关的数据结构: Z5 j/ a M! u/ V& a
进程相关的数据结构9 L, {* _0 s7 w9 h) p7 O6 ^
小结% u3 p+ l% a( \! c8 _# n6 Y
+ i8 _1 x1 [3 M* w9 [4 R1 h4 L* X, u: ~+ I5 [- e, B. E
* i. P6 }3 A$ ~4 E, ]+ Y+ y
5 \# j3 `4 X4 F* x1. 虚拟文件系统的作用
2 h" d! C r: c虚拟文件系统(VFS)是linux内核和存储设备之间的抽象层,主要有以下好处。8 M6 q. R v. ?( D* f6 m
9 H5 O6 X) t `5 I6 Z- 简化了应用程序的开发:应用通过统一的系统调用访问各种存储介质
. R4 F7 B: R! {* j) J- q; i3 q8 p' L' S
- 简化了新文件系统加入内核的过程:新文件系统只要实现VFS的各个接口即可,不需要修改内核部分
_* U5 q$ o( s3 L4 d
% Y) w& i# R0 {$ W+ I; f8 q
. p% {! d! B" ^2 ? U! c
- i2 I$ o. Q& `2. 虚拟文件系统的4个主要对象' i+ \: H( s$ c L2 b: {
虚拟文件中的4个主要对象,具体每个对象的含义参见如下的详细介绍。; @+ x V7 o2 v: h1 n
+ d0 B8 `( ?' b$ `3 v1 }3 M/ K
+ j1 B) a% m+ a" {7 {' w
+ p. x D8 l1 p5 S2.1 超级块
$ F; z$ A! X% {超级块(super_block)主要存储文件系统相关的信息,这是个针对文件系统级别的概念。
% A" e9 j6 a% j- L
3 i( \! d/ _& a! C: H0 _它一般存储在磁盘的特定扇区中,但是对于那些基于内存的文件系统(比如proc,sysfs),超级块是在使用时创建在内存中的。
& G1 L/ B: A5 C: r! a7 W" i* y. K" n8 t7 {+ n! v" j
: ~$ r0 r$ H/ f5 s+ L
+ i' I7 s- f6 {* ?6 h; J/ a' A超级块的定义在:<linux/fs.h>, K* Y& a/ @( f; r7 t: [( K8 s
, S0 u# L( j# [$ Z% a: x
复制代码7 p: q' {& ^5 z8 `, x/ ~1 p- M7 {
/*
, S y0 ^6 }' i8 ^% E * 超级块结构中定义的字段非常多,4 d6 T/ v: c1 ]5 p( w' i- c" ~' p
* 这里只介绍一些重要的属性1 N/ C8 x* w+ ]: n( Z
*/
$ t* U8 g3 ~, [3 B3 _% Vstruct super_block {* s/ t! N# \- K$ L% u( F$ D# Z) v
struct list_head s_list; /* 指向所有超级块的链表 */
6 l* q ]* L9 Y3 k+ T1 d7 ~ const struct super_operations *s_op; /* 超级块方法 */
: t+ J0 \$ @6 { struct dentry *s_root; /* 目录挂载点 */) l1 H% D' }+ ? `6 F) A) Q
struct mutex s_lock; /* 超级块信号量 */& c3 M$ g( D. w C. Z# Q2 J
int s_count; /* 超级块引用计数 */) `1 ^+ B/ y' G+ m" |/ A& Y
$ l/ Z ^! n" j* E4 M& A5 L& I struct list_head s_inodes; /* inode链表 */$ _- \9 @! ^+ q$ R# o& f
struct mtd_info *s_mtd; /* 存储磁盘信息 */6 M) n/ f1 O5 j
fmode_t s_mode; /* 安装权限 */
, z$ s; Q7 V, f2 W! V};; n4 v! o( \$ l7 f( v/ R
& Z4 g- h6 [9 V8 E8 |! j
/*
* F1 U9 i; ^# w, i' v * 其中的 s_op 中定义了超级块的操作方法
6 {/ C! ^4 @5 R4 o$ j. c * 这里只介绍一些相对重要的函数( C* i9 x5 ?+ `* N
*/' I9 l% E! {% `- {5 h: e
struct super_operations {
/ p; n y& R1 o" N. U! S$ u struct inode *(*alloc_inode)(struct super_block *sb); /* 创建和初始化一个索引节点对象 */! h& @7 W a" k" ~" ~: g4 u- T
void (*destroy_inode)(struct inode *); /* 释放给定的索引节点 */
1 \ [7 B' j; k _4 f$ i' }+ y! Q, Y3 a6 N* H
void (*dirty_inode) (struct inode *); /* VFS在索引节点被修改时会调用这个函数 */
. V% M: ]; F6 g1 X. l( \- K int (*write_inode) (struct inode *, int); /* 将索引节点写入磁盘,wait表示写操作是否需要同步 */
, w) E2 ^* q. W* e' f void (*drop_inode) (struct inode *); /* 最后一个指向索引节点的引用被删除后,VFS会调用这个函数 */; K; T% f- _* T' H" R
void (*delete_inode) (struct inode *); /* 从磁盘上删除指定的索引节点 */% n$ A" w d9 A1 `
void (*put_super) (struct super_block *); /* 卸载文件系统时由VFS调用,用来释放超级块 */9 B- J7 {+ z. F% U" H$ m
void (*write_super) (struct super_block *); /* 用给定的超级块更新磁盘上的超级块 */
# Y- |, E( q0 x2 O1 r5 g3 Q u int (*sync_fs)(struct super_block *sb, int wait); /* 使文件系统中的数据与磁盘上的数据同步 */
/ |7 B B8 M, x3 T8 x' Y int (*statfs) (struct dentry *, struct kstatfs *); /* VFS调用该函数获取文件系统状态 */
, ^* o/ D ]4 u int (*remount_fs) (struct super_block *, int *, char *); /* 指定新的安装选项重新安装文件系统时,VFS会调用该函数 */3 q% z: t% }( z2 [) I+ v! ^
void (*clear_inode) (struct inode *); /* VFS调用该函数释放索引节点,并清空包含相关数据的所有页面 */" H' v0 d7 P% f9 s
void (*umount_begin) (struct super_block *); /* VFS调用该函数中断安装操作 */, d0 t& n8 L9 f8 @
};
. y: D+ K0 k, T1 |+ O, G6 t$ x复制代码
: A! \5 \8 U- l
. ? N6 [4 v. j% U' }7 ]4 N6 ?
. b. W! O: C) N0 I# Z2.2 索引节点
1 Q" W/ L; M' O/ H2 n8 r* v8 T索引节点是VFS中的核心概念,它包含内核在操作文件或目录时需要的全部信息。
( z. D/ M; _* A) Z3 i4 I4 X! N9 L$ |. k
一个索引节点代表文件系统中的一个文件(这里的文件不仅是指我们平时所认为的普通的文件,还包括目录,特殊设备文件等等)。
! d* x4 p7 L: _5 N* V `" g4 {7 {2 E+ x7 h" r3 r, T# t
索引节点和超级块一样是实际存储在磁盘上的,当被应用程序访问到时才会在内存中创建。1 d- J, _ d) X% `7 Q( I" ^
5 D5 c9 \/ s% n
+ x$ z, p; H s: Q) W3 v) [; R
8 O% L1 M: p8 `/ I( G; ]6 g索引节点定义在:<linux/fs.h>
3 V2 z, K" f6 M- p: s8 d; Z+ g5 T
! }0 n9 F) G) C- \: W复制代码
# {- v4 p2 u0 L2 y/* $ V- |0 R9 e3 ^; [, C, v
* 索引节点结构中定义的字段非常多,
) m7 i$ `# }$ A8 B }& u * 这里只介绍一些重要的属性
( a8 S. I! N( N- Q */$ B, {( d- d6 |" O, I7 F2 C6 }# P
struct inode {
) I8 L0 j* |; R8 Z! Y! v struct hlist_node i_hash; /* 散列表,用于快速查找inode */. f; G2 K" |! W' n; E7 @
struct list_head i_list; /* 索引节点链表 */2 L% [8 y* ?1 F! z0 x
struct list_head i_sb_list; /* 超级块链表超级块 */' R* d/ X0 v* j8 s+ O
struct list_head i_dentry; /* 目录项链表 */
1 D' ~2 _" O+ L6 ]& X" ?4 G! r unsigned long i_ino; /* 节点号 */
( e s! c" O6 \9 Z" g atomic_t i_count; /* 引用计数 */
2 h' X1 }5 U# Y5 n$ O" l1 @ unsigned int i_nlink; /* 硬链接数 */1 F% \* r- l3 V8 Q4 E0 B
uid_t i_uid; /* 使用者id */5 B9 p! v' c. Z" S
gid_t i_gid; /* 使用组id */
1 n: W- m) }9 W( s9 x& s* {3 b( L struct timespec i_atime; /* 最后访问时间 */
+ ]5 T J. I) J6 J/ A1 X struct timespec i_mtime; /* 最后修改时间 */
1 u4 o- n, F- z* ` c C6 Y+ N struct timespec i_ctime; /* 最后改变时间 */
" ~$ P d6 u8 c2 X; {, v const struct inode_operations *i_op; /* 索引节点操作函数 */
d2 U; u9 a8 m( X1 O/ z) T. S% m const struct file_operations *i_fop; /* 缺省的索引节点操作 */
3 ` a* k/ @# `7 G) u; x8 c& G struct super_block *i_sb; /* 相关的超级块 */
0 w5 G/ A1 @3 J" R) s struct address_space *i_mapping; /* 相关的地址映射 *// @) I/ s+ c$ B$ [: \, b
struct address_space i_data; /* 设备地址映射 */
) _& G; B. U- H* x- i7 `/ j unsigned int i_flags; /* 文件系统标志 */
}" |1 { A3 `6 n* g# c void *i_private; /* fs 私有指针 */
# v% W- B$ x) B1 ]! D8 N2 a};
. h, r& B* O, L5 k, h/ L" H4 o2 E3 F# o; X8 O, m$ R- g: D) Y" S1 m
/*
) p2 g( Z g$ @) s" Z * 其中的 i_op 中定义了索引节点的操作方法* N& \* p0 L. o* p$ t& E. J5 P
* 这里只介绍一些相对重要的函数
% O0 {% N4 O. o9 K */- [' p- l" i7 t" g0 Z! `
struct inode_operations {. q8 u. R2 q& X9 I! K2 V% g
/* 为dentry对象创造一个新的索引节点 */, a5 t0 Y5 {7 p1 u9 K
int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
0 ]6 T8 S' m0 p. q /* 在特定文件夹中寻找索引节点,该索引节点要对应于dentry中给出的文件名 */1 U2 C1 I3 @- n6 z5 o2 y
struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);3 `+ f. K1 a' o8 Z" X( p
/* 创建硬链接 */: M1 }4 g6 [8 s
int (*link) (struct dentry *,struct inode *,struct dentry *); ^/ k* }" p' L r: V
/* 从一个符号链接查找它指向的索引节点 */
( a8 T/ v$ E6 p) d7 W! J0 q7 v void * (*follow_link) (struct dentry *, struct nameidata *);8 v8 u: U: J( k3 I5 F1 a
/* 在 follow_link调用之后,该函数由VFS调用进行清除工作 */
- \% o( q" q! P' z void (*put_link) (struct dentry *, struct nameidata *, void *);
+ T" u7 Q; d: T2 D: ? /* 该函数由VFS调用,用于修改文件的大小 */ z, ?' z. X3 U5 U Z* i0 p
void (*truncate) (struct inode *);# t% Q1 A; h/ v
};
" S* {2 `7 U2 w1 \: J# I( x* f( f复制代码& a" r7 Z# t! R* p
/ |) p: [3 y% ?0 }. E% n9 z% g0 X8 q
% i0 E6 ]1 F7 U) I) d& S* a2.3 目录项8 [! [' T! P& n1 b7 p( j
和超级块和索引节点不同,目录项并不是实际存在于磁盘上的。
& i( X5 s, K" Z4 V" n1 N
& B0 ?% {' o/ C, m# C在使用的时候在内存中创建目录项对象,其实通过索引节点已经可以定位到指定的文件,( D- m. {+ z# n" i1 @6 f3 d) ?! z
4 R1 Y1 r- r3 w& N但是索引节点对象的属性非常多,在查找,比较文件时,直接用索引节点效率不高,所以引入了目录项的概念。
4 u, z# Z A0 {8 @4 s9 ?, v- [6 G2 y$ ^; e" ~
: C# {7 b# ?, c1 M& Z
6 B4 J) v5 U1 F$ H( O* k
路径中的每个部分都是一个目录项,比如路径: /mnt/cdrom/foo/bar 其中包含5个目录项,/ mnt cdrom foo bar7 a' Q- @9 p8 r0 v& C
# K4 G( J+ g* J2 O( C0 B: u. |
2 _3 `" V: f5 J `0 b
+ d3 T" f2 ]) L5 g/ q每个目录项对象都有3种状态:被使用,未使用和负状态# @6 X9 g6 {4 t' B( }
0 F3 A) Y% F" ?4 d( L, @9 O* H- 被使用:对应一个有效的索引节点,并且该对象由一个或多个使用者
/ h/ k( b% `6 S1 e) Q
% N; i) Z4 r: `! {8 V1 q- 未使用:对应一个有效的索引节点,但是VFS当前并没有使用这个目录项, k6 Z; i* H5 c: M2 e# o* g
# C6 ]. B+ D y# q) L* B
- 负状态:没有对应的有效索引节点(可能索引节点被删除或者路径不存在了)$ a" _1 Q0 T0 P& N& N+ {( V
7 R K$ I1 y. p4 z9 \
4 }4 A$ u; t0 t# E# X$ S2 M$ V1 f7 A- i. f H# \# ]: Y; A0 K
目录项的目的就是提高文件查找,比较的效率,所以访问过的目录项都会缓存在slab中。+ G0 S" X3 `& s1 N
- {& c! x* N8 q. o
slab中缓存的名称一般就是 dentry,可以通过如下命令查看:* r7 G5 i8 |$ n3 V5 c, Z
8 x8 u: ~, Z( O% e9 u. l2 W[wangyubin@localhost kernel]$ sudo cat /proc/slabinfo | grep dentry1 W- O/ e2 w' Z2 w5 t) _6 G
dentry 212545 212625 192 21 1 : tunables 0 0 0 : slabdata 10125 10125 0
' S7 z6 h3 t: d" r' G 7 g: Z! ]. }. n: }* D
1 E9 S% l7 J1 u! }- u7 x9 I目录项定义在:<linux/dcache.h>
0 g: Z1 R+ G E9 b/ Z+ L
0 q. B% @ }6 q' {7 P复制代码
/ U u, s. Z; e# h+ E, K2 d/* 目录项对象结构 */
* P" c2 r$ _- b6 J5 S. g7 a, Lstruct dentry {
& w2 Z1 U8 ?+ u% c& A atomic_t d_count; /* 使用计数 */* I+ T6 N# N! F5 s5 d% i4 K1 p8 q
unsigned int d_flags; /* 目录项标识 */
: o4 h- M/ |- | spinlock_t d_lock; /* 单目录项锁 */
! }/ B) Y3 D" Y% D7 K6 {9 r int d_mounted; /* 是否登录点的目录项 */! U0 _' N* k( p9 m2 R7 q# u% u0 a
struct inode *d_inode; /* 相关联的索引节点 */
. s6 @. g/ X8 V/ Y5 m struct hlist_node d_hash; /* 散列表 */! {+ w( d$ X5 W% z
struct dentry *d_parent; /* 父目录的目录项对象 */% {4 T. g! Y n
struct qstr d_name; /* 目录项名称 */ z! @8 Q% v2 W5 O) r7 }3 L
struct list_head d_lru; /* 未使用的链表 */+ Q5 g/ L* E; ^. S* L0 R; C& N* O
/*
9 d- t; W* x; E# a * d_child and d_rcu can share memory
& U- J2 C& G7 q7 p */
& f8 [$ L8 f; {4 M" n+ R: `! h union {% r# R6 O0 o3 A3 a
struct list_head d_child; /* child of parent list */. I- r+ h) s& P# ~& Y X7 C' `
struct rcu_head d_rcu;
5 h' E5 l* n# V3 b7 V) J) k } d_u;; G) o, @4 I) s
struct list_head d_subdirs; /* 子目录链表 */
3 N* F0 r4 p+ y struct list_head d_alias; /* 索引节点别名链表 */
2 K6 m v! l3 q0 u6 J( A& y" D unsigned long d_time; /* 重置时间 */: e a( ^! W1 b8 U- c a
const struct dentry_operations *d_op; /* 目录项操作相关函数 */
. h3 q8 G( p2 \# v, U+ A struct super_block *d_sb; /* 文件的超级块 */
9 V' Y! \# E1 u! ]$ |: M5 d. L2 \ void *d_fsdata; /* 文件系统特有数据 */' g- h! x$ B, r+ P5 m9 M
* U$ m1 q8 q# j! q3 V. D4 t unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* 短文件名 */8 H- b2 M- J+ h' [
};
. ?( ]( `3 r1 i7 [! X X0 v! m0 \9 m* z) U
/* 目录项相关操作函数 */% k6 { j" o. S& G5 G5 s+ f
struct dentry_operations {: [( o" T7 n0 G/ p
/* 该函数判断目录项对象是否有效。VFS准备从dcache中使用一个目录项时会调用这个函数 */' w- ?' ]( o" G" l2 F& V6 S) S0 L
int (*d_revalidate)(struct dentry *, struct nameidata *);
, L2 k- Q2 `' x6 O, L" f /* 为目录项对象生成hash值 */* O% C" p. _8 h" [1 |! d
int (*d_hash) (struct dentry *, struct qstr *);; \& |: T4 x4 ~, ^" A' U) m7 W
/* 比较 qstr 类型的2个文件名 */+ m9 X) Z3 q& X, l
int (*d_compare) (struct dentry *, struct qstr *, struct qstr *);7 \, b) m4 J" g% |+ A5 w* o4 ]) u
/* 当目录项对象的 d_count 为0时,VFS调用这个函数 */% r2 Q- g, Q3 d, T1 U2 d! n) K
int (*d_delete)(struct dentry *);
* I c2 {) A! p9 h% X /* 当目录项对象将要被释放时,VFS调用该函数 */
8 X, P7 |& M5 E void (*d_release)(struct dentry *);8 I" a/ u/ n1 N( f
/* 当目录项对象丢失其索引节点时(也就是磁盘索引节点被删除了),VFS会调用该函数 */
6 X _% H2 j- M9 \7 K8 R void (*d_iput)(struct dentry *, struct inode *);
+ \# F7 L* A* M0 o+ W6 p5 E char *(*d_dname)(struct dentry *, char *, int);, `7 J8 X9 z2 V
};
- e% m4 I) F b0 j; s) t复制代码5 ~) E! z0 L- P$ ]. R% t
j: ~( t1 ]& N- F4 K) d* E* ^4 ^
& i; [' i( p/ k: {2.4 文件对象# W8 N4 E: u* ?5 S3 {# |& s0 f
文件对象表示进程已打开的文件,从用户角度来看,我们在代码中操作的就是一个文件对象。% x6 Z( A9 t/ b1 @
1 e4 s" k7 Z5 j' r2 F0 c ^3 t文件对象反过来指向一个目录项对象(目录项反过来指向一个索引节点)
! e) k( H0 o* l3 X1 G7 k& Q$ k) Y0 {( n. {# d
其实只有目录项对象才表示一个已打开的实际文件,虽然一个文件对应的文件对象不是唯一的,但其对应的索引节点和目录项对象却是唯一的。
; e% }+ }$ g( |6 K: ?' g4 P+ v. B. R3 x# X" w7 F' P
- C1 o, S2 W6 O. N" s- {
* ?1 g# G' o6 @文件对象的定义在: <linux/fs.h>
8 g" `$ n4 T+ }' T5 B
, T b9 O1 x. M: Q复制代码' x8 R* O: Y: m% d }
/*
0 q% `, B3 H# A/ o6 t) f2 P2 J& p" X7 ] * 文件对象结构中定义的字段非常多,4 Q4 j# Z) ?* V z
* 这里只介绍一些重要的属性. A9 F$ S. ~+ R( N
*/
5 R3 w# t. M! x" G1 w2 F1 ~6 {; E+ sstruct file {
6 H! J) i" V3 O union {" ~/ ] d- J; i- A' X' ]1 Y4 V
struct list_head fu_list; /* 文件对象链表 */+ O% m1 r& @9 K5 ~
struct rcu_head fu_rcuhead; /* 释放之后的RCU链表 */
$ k; f& Q3 x/ v( h2 h. J } f_u;
. \9 W, E7 ?5 u struct path f_path; /* 包含的目录项 */0 @$ `5 r8 k# S( s8 A. D
const struct file_operations *f_op; /* 文件操作函数 */9 |' X2 B3 x, ~6 W4 K; o
atomic_long_t f_count; /* 文件对象引用计数 */; t6 n, R6 j+ E
};3 y1 B$ z F6 Q: \9 s- {" j! W$ g
$ s( G( C( f4 E3 z
/** h- p4 ]3 f1 }% F3 q
* 其中的 f_op 中定义了文件对象的操作方法$ _( C" F' G5 I, S; t6 Z( ^
* 这里只介绍一些相对重要的函数; D3 {5 W) \4 Z; Q
*/2 O3 f+ @, l9 p. K; T( |: W q
struct file_operations {; W- x* ~) D0 [8 u+ m/ E8 d
/* 用于更新偏移量指针,由系统调用lleek()调用它 */. M4 h! [2 F" p. o) I
loff_t (*llseek) (struct file *, loff_t, int);
8 o, L" _1 G* ?' ^ /* 由系统调用read()调用它 */
& V+ _6 D) `: c ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);, `0 n/ l! f! L' N0 N5 H
/* 由系统调用write()调用它 */
) m" F8 e& l: ?3 C/ y8 U3 U; Y8 T ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
2 K9 h8 R ~" F6 {, n8 q c /* 由系统调用 aio_read() 调用它 */
% W( s2 H* C! w' b! X5 i4 V ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);) g* L0 b9 H a) I8 W& v$ Y( N2 F% u6 r3 W6 n
/* 由系统调用 aio_write() 调用它 */
$ }: M+ f7 f* J7 o6 k- P! n/ v- `- [ ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
: h2 n$ O4 U6 d9 U+ S1 W7 G$ ] /* 将给定文件映射到指定的地址空间上,由系统调用 mmap 调用它 */# @( d( {& y; q2 }$ U9 z7 c6 q
int (*mmap) (struct file *, struct vm_area_struct *);
+ {# Q. p4 K) M# r0 H. }) u /* 创建一个新的文件对象,并将它和相应的索引节点对象关联起来 */+ f! n p9 {$ F; o" r7 S
int (*open) (struct inode *, struct file *);0 _2 I" Y6 q: N. e0 B+ ^
/* 当已打开文件的引用计数减少时,VFS调用该函数 */4 D; Z- `, C) Z* X1 k% H
int (*flush) (struct file *, fl_owner_t id);
( l& ?3 X2 A6 @/ K0 z};. A. v" ?7 R$ I$ a% F
复制代码
7 v: d/ e5 ~9 H9 {; H; t4 t
% M% H6 p: M3 m9 f% f ]( M/ C! q8 H% y- [1 |. N. ]3 }9 _
2.5 四个对象之间关系图
# o* J) R4 L0 [. q u9 I% D1 X上面分别介绍了4种对象分别的属性和方法,下面用图来展示这4个对象的和VFS之间关系以及4个对象之间的关系。( j0 c& W# A) B: ?& k
6 e6 I/ x/ Q: _+ K
(这个图是根据我自己的理解画出来的,如果由错误请帮忙指出,谢谢!)4 u/ b& U$ e7 E' O% F& g! C. o4 Q" d2 L
9 f- @' C0 S, m
6 M+ a; T& a% M) A" ?8 j: j
- u7 ]4 S+ S4 i1 d2 X* ~
8 W- z, c" A7 o5 v. H
# ~% q2 @( @3 H1 E
# c% r$ x: ^, {% H! l
. G# ]0 e: `( M+ ^- x2 H ) x' I! o5 `0 c/ `1 b- l
6 U7 D' d- d, s ^* E/ e
3. 文件系统相关的数据结构
$ Y3 q8 M( \! c' q. g处理上面4个主要的对象之外,VFS中还有2个专门针对文件系统的2个对象,5 V$ g. O& Z t: |
* C! a }8 \# f3 \6 U- struct file_system_type: 用来描述文件系统的类型(比如ext3,ntfs等等)
7 O: e' B9 D, [. S; w
& V0 K2 o2 W& | K0 d- struct vfsmount : 描述一个安装文件系统的实例6 x! c1 Z, _8 _3 `+ U
0 R# a+ M# Y+ _3 X% R
, j5 m2 ~! y3 i3 M
! \; ?+ x8 _4 ~; J dfile_system_type 结构体位于:<linux/fs.h># Y: _! v5 n. t
* T, y+ d6 Y" H* U& g8 X. m( G P
复制代码
- b5 K# b6 L* v; K( Rstruct file_system_type {
% G% r! k% K2 q1 W5 D) {9 z: ] L const char *name; /* 文件系统名称 */' C4 g& s& N* l5 L7 q+ U* J
int fs_flags; /* 文件系统类型标志 */: t' W7 v; _1 D. g% r1 G* k
/* 从磁盘中读取超级块,并且在文件系统被安装时,在内存中组装超级块对象 */+ h$ l* j) Z" I7 |- K' N- Y$ g
int (*get_sb) (struct file_system_type *, int,6 _- b4 L: r0 O$ ], A. a
const char *, void *, struct vfsmount *);
]% m' c3 Y9 C$ f* `* c& T6 g /* 终止访问超级块 */. q4 B$ U# W8 k9 G( s
void (*kill_sb) (struct super_block *);
6 j& L6 k- ]/ `5 t7 ~ struct module *owner; /* 文件系统模块 */
& W6 {% ~0 k! P) Z- s E9 h struct file_system_type * next; /* 链表中下一个文件系统类型 */
3 t3 h5 H2 _6 Q* S& y& J struct list_head fs_supers; /* 超级块对象链表 */
: ~6 c& {, {. Y7 y# M+ V9 V0 H, R; N% q# `: L, z
/* 下面都是运行时的锁 */
# f% O: O4 e7 q2 A- h struct lock_class_key s_lock_key;3 N* [* V# A! P2 Y0 m9 [" @7 l! \' Q" s
struct lock_class_key s_umount_key;9 k! L- Y! z% g- g# @$ ~$ `6 x
! D, R% d; w/ {* {
struct lock_class_key i_lock_key;5 G* Y5 g3 ] d. L$ _- C
struct lock_class_key i_mutex_key;" w! l& y8 h$ \" M* E( q( _
struct lock_class_key i_mutex_dir_key;
5 l$ Q1 p5 W' m6 j struct lock_class_key i_alloc_sem_key;
0 W5 X5 @- C/ z G3 l};5 t8 z4 {, T# x9 c
复制代码
2 u2 S0 P% }9 q. g+ L每种文件系统,不管由多少个实例安装到系统中,还是根本没有安装到系统中,都只有一个 file_system_type 结构。, i3 h7 i; |" p9 W' s2 s
& r( V; q2 @" m L7 p6 J+ |3 i& F( E( V
$ e$ j( y# Y/ n" Z T# ?5 J
* _8 V* X' V* d0 _* j当文件系统被实际安装时,会在安装点创建一个 vfsmount 结构体。7 O( S8 @. @' B. T M3 v/ e
. i1 R! V! F- w: D, c" b
结构体代表文件系统的实例,也就是文件系统被安装几次,就会创建几个 vfsmount
: J/ G8 L; J+ Z, X- ?( j3 t! Y. L# J
% n/ P8 `: V: ~! z& {5 D- }4 x# q* K, Ivfsmount 的定义参见:<linux/mount.h>
/ S5 w, P% s: c7 @( ^3 [! _4 D* K0 f' Y8 }9 o# s
复制代码
/ s' D/ F6 B& r; V- K; ^% Qstruct vfsmount {
5 ~9 V1 l) y8 r8 e& ~ struct list_head mnt_hash; /* 散列表 */
! d* \- o+ Y: S4 \+ F struct vfsmount *mnt_parent; /* 父文件系统,也就是要挂载到哪个文件系统 */, L7 ^; p6 S+ P4 L* |. V2 T* J
struct dentry *mnt_mountpoint; /* 安装点的目录项 */
. o. u6 `4 `9 K( ^ struct dentry *mnt_root; /* 该文件系统的根目录项 */* m. C% e1 T4 x8 \$ H
struct super_block *mnt_sb; /* 该文件系统的超级块 */6 U. r3 d. j* G
struct list_head mnt_mounts; /* 子文件系统链表 */) F9 E! Q5 E: u% @9 o
struct list_head mnt_child; /* 子文件系统链表 */
. t* o( E, w: S" h# I { int mnt_flags; /* 安装标志 */
4 J% K# i, o% G0 Z0 x$ F8 e* i$ N /* 4 bytes hole on 64bits arches */
+ z: ]3 }8 H! @: w$ h5 s# t const char *mnt_devname; /* 设备文件名 e.g. /dev/dsk/hda1 */
& I! [7 R5 a* S$ i struct list_head mnt_list; /* 描述符链表 */9 |$ K# A6 Q3 h4 ?
struct list_head mnt_expire; /* 到期链表的入口 */8 q; J9 K) k2 c% X; R+ S
struct list_head mnt_share; /* 共享安装链表的入口 */
" s0 X) Z+ |$ x! b8 A6 G# V struct list_head mnt_slave_list;/* 从安装链表 */6 }5 b7 P( J% |; x- L
struct list_head mnt_slave; /* 从安装链表的入口 */# Z$ t/ H2 ^' H: h+ @& B; X
struct vfsmount *mnt_master; /* 从安装链表的主人 */( R3 J' ~& C4 P+ S0 h* ~
struct mnt_namespace *mnt_ns; /* 相关的命名空间 */1 R) ^: j* e" Z' e; `4 y
int mnt_id; /* 安装标识符 */( q7 \7 A) G4 \$ f0 n: V
int mnt_group_id; /* 组标识符 */& j% o, J# d5 O' h; {+ o. x
/*
/ E$ c9 y! L5 U: u * We put mnt_count & mnt_expiry_mark at the end of struct vfsmount
6 j* f$ w3 P& f3 \8 [ * to let these frequently modified fields in a separate cache line" T" O. G u1 R% E8 g
* (so that reads of mnt_flags wont ping-pong on SMP machines)
) m( i3 v' T W% ~, H */
. L% j* |) }& S: k/ Y1 x2 E atomic_t mnt_count; /* 使用计数 */
" D, H/ |: Z' k0 g% c int mnt_expiry_mark; /* 如果标记为到期,则为 True */
. x, f& l( K6 |8 F0 K/ x. y int mnt_pinned; /* "钉住"进程计数 */
/ s4 ^3 P3 @8 w( l; L% Z5 |+ f int mnt_ghosts; /* "镜像"引用计数 */# [5 L) W) c m ^* T
#ifdef CONFIG_SMP9 t: R6 ~& ?% l6 ~* D4 d5 t
int *mnt_writers; /* 写者引用计数 */% s( f4 J" a$ E1 x
#else
; i3 F5 y! d7 @- v5 W3 I3 {9 b1 D3 f int mnt_writers; /* 写者引用计数 */
) H3 r4 K" Y8 ?; D z#endif
. E( L) I7 o, _' W};/ ? l) [' d! ^) r5 e# r1 `
复制代码
; s; k. Z/ b: ^: } ; k; X. \9 a3 `- _7 t. ?
) M& H% ~) k) k5 h' s! Y5 n/ `
4. 进程相关的数据结构# h2 ~9 w6 s6 v% S- m/ r' n
以上介绍的都是在内核角度看到的 VFS 各个结构,所以结构体中包含的属性非常多。
( ]) P8 J+ }5 B' M( D/ H* i; `2 e+ k+ O
而从进程的角度来看的话,大多数时候并不需要那么多的属性,所有VFS通过以下3个结构体和进程紧密联系在一起。
7 s+ k$ a( ?1 [6 k8 w
5 j! g: T) u B& K. ?- struct files_struct :由进程描述符中的 files 目录项指向,所有与单个进程相关的信息(比如打开的文件和文件描述符)都包含在其中。
l$ n& N( G2 X: [' W6 \; ~, L. l w1 R8 |
- struct fs_struct :由进程描述符中的 fs 域指向,包含文件系统和进程相关的信息。* Y' Z9 b9 G: C5 V
! g _: Y: k6 {" o) V R/ l
- struct mmt_namespace :由进程描述符中的 mmt_namespace 域指向。9 ]: C G; V4 n. p& J$ J
3 m- p' ^ O7 |
) @' Y/ q( s7 B1 g
* d9 d6 `# M/ ^0 u6 ~9 g4 c
struct files_struct 位于:<linux/fdtable.h>
0 C: z c% w/ s" a$ r) D3 N+ Y9 |2 t* d. c
复制代码
0 I) a3 [: G+ n$ hstruct files_struct {0 L# L `' _- k1 H g( w/ E: r" D( n2 u
atomic_t count; /* 使用计数 */1 ~' \8 u* m. n3 I
struct fdtable *fdt; /* 指向其他fd表的指针 */
* Q7 Q* m5 k N struct fdtable fdtab;/* 基 fd 表 */
# r% d: h! e$ V Q& N( Q# O K# w' p4 A; r spinlock_t file_lock ____cacheline_aligned_in_smp; /* 单个文件的锁 */
o6 J* N* Z- @ int next_fd; /* 缓存下一个可用的fd */
# S2 O- l6 R4 ?4 l0 D# c8 c struct embedded_fd_set close_on_exec_init; /* exec()时关闭的文件描述符链表 */8 P2 m0 ?9 b3 F3 `; @$ c
struct embedded_fd_set open_fds_init; /* 打开的文件描述符链表 */" z; i3 F& F% L5 a1 \
struct file * fd_array[NR_OPEN_DEFAULT]; /* 缺省的文件对象数组 */
% u6 C3 @: _3 r1 k# k( Z) A: G};
: v/ m3 _* U. h复制代码: C- K; K0 ~; V: P- B( S
+ C2 r9 \, x) O
w E4 `1 J; Z
struct fs_struct 位于:<linux/fs_struct.h>' G9 t* G. S, _; C m+ @. Y' T
( d& e4 U# t7 C) y0 f1 y) r
复制代码5 A8 }- U8 }6 A
struct fs_struct {: ? n% S2 |' X, d" s
int users; /* 用户数目 */
! ?8 Y/ N1 \: W0 H6 l8 D rwlock_t lock; /* 保护结构体的读写锁 */
( B$ N) e; m* W6 o int umask; /* 掩码 */+ A6 }$ G. C- |" `/ b# x, q
int in_exec; /* 当前正在执行的文件 */' C1 H0 A0 f+ t0 `( h
struct path root, pwd; /* 根目录路径和当前工作目录路径 *// |8 x1 e; _( q5 `
};, b6 p2 a9 f' K/ }) N* [! Y
复制代码
( I- X# l6 }- T* |* ~ @0 i
% m0 r6 b6 U, h' C& A
( \% l! [. G! v( nstruct mmt_namespace 位于:<linux/mmt_namespace.h>
4 \% M. a& R2 d% I
: q7 L4 h% ~6 ~! S但是在2.6内核之后似乎没有这个结构体了,而是用 struct nsproxy 来代替。
! J- }& m1 D+ ]5 x5 \# R7 v# c1 D& E7 \7 q- B
以下是 struct task_struct 结构体中关于文件系统的3个属性。 P+ W0 N& G# H- D/ _& T$ f$ j
+ u" T" X+ e* V& |
struct task_struct 的定义位于:<linux/sched.h>5 s: I: R8 c3 t9 @
% N1 N, P6 o: A/ G8 g复制代码
0 ]$ {) q7 F' N7 A/ s6 S4 r9 k/* filesystem information */0 B/ y8 D6 r' u; M. E2 h
struct fs_struct *fs;
9 g1 @% l0 @! m3 G0 e* l/* open file information */( j1 h, C* w; I# q9 T0 ?$ a6 }/ u
struct files_struct *files;# v* a ]1 [, C1 U4 O
/* namespaces */0 O) y" y$ ?) k$ o- y4 v* Z7 B4 Z
struct nsproxy *nsproxy;( a9 g9 x' V# e7 C" Y
复制代码- C I+ l- j, b" [ G! g( R
* _( e" L3 P" C% t+ t# U w" u3 z Y5 d
5. 小结9 G: u" _/ ~: F# }' s/ V% I! e) U
VFS 统一了文件系统的实现框架,使得在linux上实现新文件系统的工作变得简单。
* C# o( X7 \8 u8 T; @& ` A) J, `* p' L8 B7 }: E$ R Y$ I" q" f1 w
目前linux内核中已经支持60多种文件系统,具体支持的文件系统可以查看 内核源码 fs 文件夹下的内容。 |
|