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