Linux-特殊文件系统 - STEMHA's Blog

Linux-特殊文件系统

前言

网络和磁盘文件系统能够使用户处理存放在内核之外的信息。
特殊文件系统可以为系统程序员和管理员提供一种容易的方式来操作内核的数据结构并实现操作系统的特殊特征。

Linux中常用的 特殊文件系统

下表列出了最常用的特殊文件系统

名字 安装 说明
bdev none 块设备
binfmt_misc any 其他可执行格式
devpts /dev/pts 伪终端支持(开放组织的Unix98标准)
eventpollfs none 由有效事件轮询机制使用
futexfs none 由futex(快速用户空间加锁)机制使用
pipefs none 管道
proc /proc 对内核数据结构的常规访问点。一个反映内核运行情况的虚的文件系统,并不实际存在于磁盘上。历史最早,最初就是用来跟内核交互的唯一方式,用来获取处理器、内存、设备驱动、进程等各种信息。
rootfs none 为启动阶段提供一个空的根目录
shm none IPC共享线性区
mqueue any 实现POSIX消息队列时使用
sockfs none 套接字
sysfs /sys 对系统数据的常规访问点(一般是些驱动), 跟 kobject 框架紧密联系,而 kobject 是为设备驱动模型而存在的,所以 sysfs 是为设备驱动服务的。
tmpfs any 临时文件(如果不被交换出去就保持在RAM中)
usbfs /proc/bus/usb USB设备

注意:

  • 有几个特殊文件系统没有固定的安装点(也就是any)。这些文件系统可以由用户自由的安装和使用。
  • 还有一些特殊文件系统根本没有安装点(也就是none)。它们不是用于和用户交互,但是内核可以使用它们,以便更容易地重用VFS层的某些代码;

特殊文件系统不限于物理块设备。然而,内核给每个安装的特殊文件系统分配一个虚拟的块设备,让其主设备号为0而次设备号具有任意值(每个特殊文件系统有不同的值)。

set_anon_super()函数用于初始化特殊文件系统的超级块;该函数本质上获得一个未使用的次设备号dev,然后用主设备号0和次设备号dev设置新超级块的s_dev字段。
而另一个kill_anon_super()函数移走特殊文件系统的超级块。unnamed_dev_idr变量包含指向一个辅助结构(记录当前在用的次设备号)的指针。
尽管有些内核设计者不喜欢虚拟块设备标识符,但是这些标识符有助于内核以统一的方式处理特殊文件系统和普通文件系统。

/proc文件系统

/proc 是一个特殊的文件系统,其安装点一般都固定为/proc。这个文件系统中所有的文件都是特殊文件,其内容不存在于任何设备上。
每当创建一个进程时,系统就以其pid 为文件名在这个目录下建立起一个特殊文件,使得通过这个文件就可以读/写相应进程的用户空间,而当进程退出时则将此文件删除。
/proc 文件系统中的目录项结构dentry,在磁盘上没有对应结构,而以内存中的proc_dir_entry 结构来代替。

查看Linux源码可以通过在线的linux源码进行查看,非常方便的。通过tag切换还可以查看不同版本的linux。

proc_dir_entry定义的地方:

  • Linux kernel 2.6.38.8 在#include <linux/proc_fs.h>
  • Linux kernel 3.10.17 在fs/proc/internal.h:struct proc_dir_entry

Linux内核提供了很多proc文件系统的API,相关API定义在kernel-3.10/fs/proc/generic.c中,并在kernel-3.10\include\linux\proc_fs.h中引用

struct proc_dir_entry数据结构

定义路径:kernel-3.10/fs/proc/internal.h

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
//这还没有完全实现。这个想法是为了在这些proc_dir_entries中创建一个内存树(就像实际的/proc文件系统一样 ),
//这样我们就可以动态地 向/proc添加新文件。
/*
* This is not completely implemented yet. The idea is to
* create an in-memory tree (like the actual /proc filesystem
* tree) of these proc_dir_entries, so that we can dynamically
* add new files to /proc.
*
* The "next" pointer creates a linked list of one /proc directory,
* while parent/subdir create the directory structure (every
* /proc file has a parent, but "subdir" is NULL for all
* non-directory entries).
*/
struct proc_dir_entry {
unsigned int low_ino;
umode_t mode;
nlink_t nlink;
kuid_t uid;
kgid_t gid;
loff_t size;
const struct inode_operations *proc_iops;
const struct file_operations *proc_fops;
struct proc_dir_entry *next, *parent, *subdir;
void *data;
atomic_t count; /* use count */
atomic_t in_use; /* number of callers into module in progress; */
/* negative -> it's going away RSN */
struct completion *pde_unload_completion;
struct list_head pde_openers; /* who did ->open, but not ->release */
spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
u8 namelen;
char name[];
};

在/proc/下创建目录

proc_mkdir函数定义路径:kernel-3.10/fs/proc/generic.c

1
2
3
4
5
6
struct proc_dir_entry *proc_mkdir(const char *name,
struct proc_dir_entry *parent)
/*proc_mkdir_data()第二个参数mode=0表示mode = S_IRUGO | S_IXUGO; */
{
return proc_mkdir_data(name, 0, parent, NULL);
}
  • 参数parent表示name的父目录,一般为NULL,表示挂载在/proc/下。
  • 参数name就是在proc/下创建的目录名。

该函数实际上调用了proc_mkdir_data函数来创建一个目录的。proc_mkdir_data函数定义路径:kernel-3.10/fs/proc/generic.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode,
struct proc_dir_entry *parent, void *data)
{
struct proc_dir_entry *ent;

if (mode == 0)
mode = S_IRUGO | S_IXUGO;

ent = __proc_create(&parent, name, S_IFDIR | mode, 2);
if (ent) {
ent->data = data;
if (proc_register(parent, ent) < 0) {
kfree(ent);
ent = NULL;
}
}
return ent;
}

当需要对创建的目录指定mode的时候,可用下面这个API:

1
2
3
4
struct proc_dir_entry *proc_mkdir_mode(const char *name, umode_t mode,
struct proc_dir_entry *parent)
{
return proc_mkdir_data(name, mode, parent, NULL);

移除创建的目录或文件

remove_proc_entry函数定义路径:kernel-3.10/fs/proc/generic.c

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
void remove_proc_entry(const char *name, struct proc_dir_entry *parent)
{
struct proc_dir_entry **p;
struct proc_dir_entry *de = NULL;
const char *fn = name;
unsigned int len;

spin_lock(&proc_subdir_lock);
if (__xlate_proc_name(name, &parent, &fn) != 0) {
spin_unlock(&proc_subdir_lock);
return;
}
len = strlen(fn);

for (p = &parent->subdir; *p; p=&(*p)->next ) {
if (proc_match(len, fn, *p)) {
de = *p;
*p = de->next;
de->next = NULL;
break;
}
}
spin_unlock(&proc_subdir_lock);
if (!de) {
WARN(1, "name '%s'\n", name);
return;
}

proc_entry_rundown(de);

if (S_ISDIR(de->mode))
parent->nlink--;
de->nlink = 0;
WARN(de->subdir, "%s: removing non-empty directory "
"'%s/%s', leaking at least '%s'\n", __func__,
de->parent->name, de->name, de->subdir->name);
pde_put(de);
}

参考资料

github上的linux开源代码
Linux 文件系统:procfs, sysfs, debugfs 用法简介
proc文件系统详解
Linux内核通信之—proc文件系统(详解)
《深入分析Linux内核源代码》
《深入理解Linux内核》

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×