我是靠谱客的博主 冷艳时光,这篇文章主要介绍管道文件系统pipefs,现在分享给大家,希望可以做个参考。

概述

  在Linux的世界里,万物皆文件,并且都是通过虚拟文件系统VFS来同一管理调用不同的文件系统,因此Linux中可以通过文件IO系统调用来进行操作。而管道就是一个伪文件系统,其通过pipefs来实现。同其他真正的文件系统(ext3、ext4等)一样,都实现VFS中的四种主要对象:super_block、inode、dentry和文件对象file。当对管道进行读写操作时,VFS就会将请求转发给pipefs,而pipefs则会调用自己特定的一些操作函数。 

file_system_type操作表

  pipefs是一个文件系统,就会有一个被称为file_system_type的数据结构,在系统启动或者文件系统模块挂载时用于在VFS中进行注册。所有的已注册的文件系统的file_system_type结构形成一个链表,链表头由file_systems变量指定。pipefs的file_system_type操作表如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static struct file_system_type pipe_fs_type = {
.name = "pipefs",
.mount = pipefs_mount,
.kill_sb = kill_anon_super, //用于移除特殊文件系统的超级块
};

3.18内核版本file_system_type结构体中增加了一个mount成员的钩子函数,

复制代码
1
2
3
4
5
struct dentry *(*mount) (struct file_system_type *, int, const char *, void *);

对于pipefs、sockfs和bdev等伪文件系统调用的该钩子函数实现都是对mount_pseudo函数的封装,该函数主要是根据file_system_type创建一个super_block,并进行一系列的初始化工作,然后根据该超级块和伪文件系统名(pipefs、sockfs和bdev)在内存中分配一个目录项缓存,将其设置为该超级块的根目录的目录项对象,最后返回目录项:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/*
* pipefs should _never_ be mounted by userland - too much of security hassle,
* no real gain from having the whole whorehouse mounted. So we don't need
* any operations on the root directory. However, we need a non-trivial
* d_name - pipe: will go nicely and kill the special-casing in procfs.
*/
static struct dentry *pipefs_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
return mount_pseudo(fs_type, "pipe:", &pipefs_ops,
&pipefs_dentry_operations, PIPEFS_MAGIC);
}

mount_pseudo函数定义如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/*
* Common helper for pseudo-filesystems (sockfs, pipefs, bdev - stuff that
* will never be mountable)
* 伪文件系统的常用助手(sockfs,pipefs,bdev - 永远不可安装的东西)
*/struct dentry *mount_pseudo(struct file_system_type *fs_type, char *name,
const struct super_operations *ops,
const struct dentry_operations *dops, unsigned long magic)
{
struct super_block *s;
struct dentry *dentry;
struct inode *root;
struct qstr d_name = QSTR_INIT(name, strlen(name));
 
s = sget(fs_type, NULL, set_anon_super, MS_NOUSER, NULL); //调用set_anon_super函数初始化特殊文件系统的超级块;
if (IS_ERR(s))
return ERR_CAST(s);
 
s->s_maxbytes = MAX_LFS_FILESIZE;
s->s_blocksize = PAGE_SIZE;
s->s_blocksize_bits = PAGE_SHIFT;
s->s_magic = magic;
s->s_op = ops ? ops : &simple_super_operations;
s->s_time_gran = 1;
root = new_inode(s);
if (!root)
goto Enomem;
/*
* since this is the first inode, make it number 1. New inodes created
* after this must take care not to collide with it (by passing
* max_reserved of 1 to iunique).
*/
root->i_ino = 1;
root->i_mode = S_IFDIR | S_IRUSR | S_IWUSR;
root->i_atime = root->i_mtime = root->i_ctime = CURRENT_TIME;
//__d_alloc函数分配一个目录项缓存dcache的入口目录,没有足够内存,则返回NULL,分配成功,则返回一个dentry结构体
dentry = __d_alloc(s, &d_name);
if (!dentry) {
iput(root);
goto Enomem;
}
d_instantiate(dentry, root);
s->s_root = dentry; //设置pipefs文件系统根目录的目录项对象,目录项对象操作表
s->s_d_op = dops;
s->s_flags |= MS_ACTIVE;
return dget(s->s_root); //增加该dentry的引用计数,并返回
 
Enomem:
deactivate_locked_super(s);
return ERR_PTR(-ENOMEM);
}

管道文件系统比较简单,只是一个存在于内存中的文件,因而管道文件系统的超级块操作表比较简单,只定义了两个操作函数,free_inode_nonrcu用于释放inode对象;simple_statfs用于获取pipefs文件系统的状态信息

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static const struct super_operations pipefs_ops = {
.destroy_inode = free_inode_nonrcu, //用于释放inode对象
.statfs = simple_statfs, //用于获取pipefs文件系统的状态信息
};

pipefs初始化

接下来看看pipefs文件系统的初始化,主要是进行pipefs的注册,并进行pipefs伪文件系统的装载(没有挂载点)。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
static int __init init_pipe_fs(void)
{
int err = register_filesystem(&pipe_fs_type);
 
if (!err) {
pipe_mnt = kern_mount(&pipe_fs_type);
if (IS_ERR(pipe_mnt)) {
err = PTR_ERR(pipe_mnt);
unregister_filesystem(&pipe_fs_type);
}
}
return err;
}
 
fs_initcall(init_pipe_fs);

以上就是pipefs伪文件系统的一个初始化,挂载过程。接下来看下管道文件具体的IO操作过程。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
const struct file_operations pipefifo_fops = {
.open = fifo_open,
.llseek = no_llseek,
.read = new_sync_read,
.read_iter = pipe_read,
.write = new_sync_write,
.write_iter = pipe_write,
.poll = pipe_poll,
.unlocked_ioctl = pipe_ioctl,
.release = pipe_release,
.fasync = pipe_fasync,
};

new_sync_read函数是一个通用的读函数,其中调用了read_iter的钩子函数,即就是最终还是调用pipe_read函数进行管道的读操作,同理管道的写操作调用pipe_write。那么read和read_iter有什么区别呢?其实在老版本内核中使用read通常每次读取一个缓冲区的内存,若是存在多个缓冲区就需要多次调用read函数。而在新的内核版本中添加了read_iter函数,其是一个聚合读函数,可以同时读取多个缓冲区内容,性能较好。

代码基于Linux 3.18.24

最后

以上就是冷艳时光最近收集整理的关于管道文件系统pipefs的全部内容,更多相关管道文件系统pipefs内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(71)

评论列表共有 0 条评论

立即
投稿
返回
顶部