Linux
系统/proc
目录下,有一些特殊的目录和文件,用来展示或者设置内核数据。例如,/proc/meminfo
展示系统内存信息:
$ cat /proc/meminfo
MemTotal: 506160 kB
MemFree: 73528 kB
MemAvailable: 335160 kB
Buffers: 54756 kB
Cached: 162888 kB
SwapCached: 0 kB
Active: 247648 kB
Inactive: 96840 kB
Active(anon): 127044 kB
Inactive(anon): 4332 kB
Active(file): 120604 kB
Inactive(file): 92508 kB
Unevictable: 0 kB
Mlocked: 0 kB
SwapTotal: 0 kB
SwapFree: 0 kB
Dirty: 104 kB
Writeback: 0 kB
AnonPages: 126824 kB
Mapped: 14412 kB
Shmem: 4532 kB
Slab: 70608 kB
SReclaimable: 59128 kB
SUnreclaim: 11480 kB
KernelStack: 2928 kB
PageTables: 3028 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 253080 kB
Committed_AS: 530132 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 5536 kB
VmallocChunk: 34359731707 kB
HardwareCorrupted: 0 kB
AnonHugePages: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
DirectMap4k: 69568 kB
DirectMap2M: 454656 kB
这些数据随着系统的变化动态调整,感觉好神奇!
还有一些文件,直接跟一些内核变量映射。除了可以读出数据,还可以更新数据呢。例如,/proc/sys/net/ipv4/ip_forward
用来控制IP
包转发,设置为1
则是是启用转发:
$ cat /proc/sys/net/ipv4/ip_forward
0
echo "1" > /proc/sys/net/ipv4/ip_forward
$ cat /proc/sys/net/ipv4/ip_forward
1
那么,这些文件本质是什么呢?是真正的文件吗?
来龙去脉
系统用户和程序(进程)经常需要某些内核信息,该怎么获得呢?
以/proc/sys/net/ipv4/ip_forward
为例,内核里有一个变量用来控制协议栈是否转发IP
包。系统用户要控制转发是否开启,就需要去设置这个内核变量。怎么设置呢?要知道用户空间可是没有办法直接访问内核空间的。
可以考虑实现一个新的系统调用——int set_ip_forward(int value)
。然而,天呀有好多这样或那样的场景,这样系统调用表是要爆炸的!而且这种方式用起来也麻烦,需要实现一个专用的命令,调用这个系统调用完成设置。
有没有更通用的方式呢?其实,让这个系统调用更加通用化,也是一种思路——int set_kernel_value(char *path, void *value)
。这样一个系统调用就搞定了一些列设置要求,但是还是没有解决调用麻烦的问题。
如果,可以将内核数据伪装成一个文件,用read
系统调用获取;用write
系统调用设置不就完美了吗?这样,有现成的命令可以直接使用,比如用cat
来获取,用echo
来设置!
那么内核有办法做到吗?
办法肯定是有的。最直观的想法是,在内核处理read
的代码进行控制,让read
直接从内核数据中拷贝,而不是磁盘。当然了,内核黑客们不会写这么恶心的代码,而是抽象了一层——VFS
。
VFS
Linux
支持各种各样的文件系统,光ext
系列就有:ext2
、ext3
、ext4
。内核如何实现不同文件系统差异性的呢?
答案是,内核抽象了VFS
接口。跟面向对象编程中的接口有点类似,具体的文件系统按照各自的方式分别实现(多态)。
如上图所示,不同的文件系统以各自的方式实现处理接口,如read
和write
等等。这些处理接口被封装成结构一致的结构体注册。这样,同样的系统调用,在不同的文件系统下,由不同的函数处理。
因此,完全有可能实现一个假的文件系统,就叫procfs
,读写分别映射为变量的读出和写入。
流程
最后,图解说明访问/proc
下文件的整个流程:
- 用户进程发起一个系统调用
read
; - 内核
VFS
根据文件描述符找出操作描述体; - 内核从描述体取出
read
处理函数指针; - 内核执行该处理函数;
- 处理函数读出内核变量并拷贝到用户态空间;