传统上,文件系统用于在块设备上持久存储数据。但也可以使用文件系统来组织、提供或交换并不存储在块设备上的信息,这些信息可以由内核动态生成。
proc
文件系统(proc filesystem),它使得内核可以生成与系统的状态和配置有关的信息。
Sysfs
是另一个特别重要的虚拟文件系统例子。Sysfs按照惯例总是装载在/sys
目录,但这不是强制规定,装载到其他位置也是可以的。它设计为从内核向用户层导出非常结构化的信息。与procfs
相比,它并不供人直接使用,因为信息是层次化、深度嵌套的。此外,文件包含的信息并不总是ASCII文本形式,也有可能使用不可读的二进制串。但对于想要收集系统中的硬件和设备间拓扑关联方面详细信息的工具而言,该文件系统是非常有用的。
用于专门目的的小文件系统,可以由内核提供的标准函数构建。在内核内部,libfs
库提供了所需功能。此外,内核提供了易于实现顺序文件的方法。在调试文件系统debugfs
中同时使用了这两种技术,该文件系统使得内核开发者能够快速地向用户空间导出值或从用户空间导入值,而无需创建定制的接口或专门的文件系统。
proc
文件系统是一种虚拟文件系统,其信息不能从块设备读取。只有在读取文件内容时,才动态生成相应的信息。
使用proc文件系统,可以获得有关内核各子系统的信息(例如,内存利用率、附接的外设,等等),也可以在不重新编译内核源代码的情况下修改内核的行为,或重启系统。与该文件系统密切相关的是系统控制机制(system control mechanism,简称sysctl),前面各章已经频繁引用过该机制。proc文件
系统提供了一种接口,可用于该机制导出的所有选项,使得可以不费力气地修改参数。无需开发专门的通信程序,只需要一个shell和标准的cat、echo程序。通常,进程数据文件系统(process data filesystem
,procfs
的全称)装载在/proc
,它的缩写proc FS
即由此得名。但有一点值得注意,该文件系统可以装载到目录树的任何位置,就像是其他任何文件系统一样,虽然这种做法并不常见。
可以分为以下几大类:
每个系统进程,无论当前状态如何,都有一个对应的子目录(与其PID同名),包含了该进程的有关信息。
ls -l /proc/31561
进程详细信息:
total 0
dr-xr-xr-x. 2 admin admin 0 Jul 12 14:05 attr
-rw-r--r--. 1 admin admin 0 Jul 12 14:05 autogroup
-r--------. 1 admin admin 0 Jul 12 14:05 auxv
-r--r--r--. 1 admin admin 0 Jul 12 14:05 cgroup
--w-------. 1 admin admin 0 Jul 12 14:05 clear_refs
-r--r--r--. 1 admin admin 0 Jul 12 14:04 cmdline
-rw-r--r--. 1 admin admin 0 Jul 12 14:05 comm
-rw-r--r--. 1 admin admin 0 Jul 12 14:05 coredump_filter
-r--r--r--. 1 admin admin 0 Jul 12 14:05 cpuset
lrwxrwxrwx. 1 admin admin 0 Jul 12 14:05 cwd -> /cloud
-r--------. 1 admin admin 0 Jul 12 14:05 environ
lrwxrwxrwx. 1 admin admin 0 Jul 12 14:05 exe -> /usr/lib/jvm/java-1.8-openjdk/jre/bin/java
dr-x------. 2 admin admin 0 Jul 12 14:05 fd
dr-x------. 2 admin admin 0 Jul 12 14:05 fdinfo
-rw-r--r--. 1 admin admin 0 Jul 12 14:05 gid_map
-r--------. 1 admin admin 0 Jul 12 14:05 io
-r--r--r--. 1 admin admin 0 Jul 12 14:05 limits
-rw-r--r--. 1 admin admin 0 Jul 12 14:05 loginuid
dr-x------. 2 admin admin 0 Jul 12 14:05 map_files
-r--r--r--. 1 admin admin 0 Jul 12 14:05 maps
-rw-------. 1 admin admin 0 Jul 12 14:05 mem
-r--r--r--. 1 admin admin 0 Jul 12 14:05 mountinfo
-r--r--r--. 1 admin admin 0 Jul 12 14:05 mounts
-r--------. 1 admin admin 0 Jul 12 14:05 mountstats
dr-xr-xr-x. 5 admin admin 0 Jul 12 14:05 net
dr-x--x--x. 2 admin admin 0 Jul 12 14:05 ns
-r--r--r--. 1 admin admin 0 Jul 12 14:05 numa_maps
-rw-r--r--. 1 admin admin 0 Jul 12 14:05 oom_adj
-r--r--r--. 1 admin admin 0 Jul 12 14:05 oom_score
-rw-r--r--. 1 admin admin 0 Jul 12 14:05 oom_score_adj
-r--r--r--. 1 admin admin 0 Jul 12 14:05 pagemap
-r--------. 1 admin admin 0 Jul 12 14:05 patch_state
-r--r--r--. 1 admin admin 0 Jul 12 14:05 personality
-rw-r--r--. 1 admin admin 0 Jul 12 14:05 projid_map
lrwxrwxrwx. 1 admin admin 0 Jul 12 14:05 root -> /
-rw-r--r--. 1 admin admin 0 Jul 12 14:05 sched
-r--r--r--. 1 admin admin 0 Jul 12 14:05 schedstat
-r--r--r--. 1 admin admin 0 Jul 12 14:05 sessionid
-rw-r--r--. 1 admin admin 0 Jul 12 14:05 setgroups
-r--r--r--. 1 admin admin 0 Jul 12 14:05 smaps
-r--r--r--. 1 admin admin 0 Jul 12 14:05 stack
-r--r--r--. 1 admin admin 0 Jul 12 14:04 stat
-r--r--r--. 1 admin admin 0 Jul 12 14:05 statm
-r--r--r--. 1 admin admin 0 Jul 12 14:04 status
-r--r--r--. 1 admin admin 0 Jul 12 14:05 syscall
dr-xr-xr-x. 519 admin admin 0 Jul 12 14:05 task
-r--r--r--. 1 admin admin 0 Jul 12 14:05 timers
-rw-r--r--. 1 admin admin 0 Jul 12 14:05 uid_map
-r--r--r--. 1 admin admin 0 Jul 12 14:05 wchan
cmdline
是用于起点进程的命令行,即一个字符串,包含了程序名和所有参数:
cat cmdline
java-Djava.security.egd=file:/dev/./urandom-javaagent:/opt/agent/skywalking-agent.jar-jar-Xms1024m-Xmx2048m-XX:SurvivorRatio=8-XX:+UseConcMarkSweepGC/cloud/app.jar
数据各项不是以空格作为分隔,而是以
\0
作为分隔,可以使用od -t a
查看字节。
表示为该程序设置的所有环境变量,其仍然使用了0字节作为分隔符。
以文本形式,列出了进程使用的所有库(和进程本身的二进制文件)的内存映射。
7f6c5e9e6000-7f6c5e9ea000 r--p 00000000 fd:00 143113780 /usr/lib/libfreebl3.so.41
7f6c5e9ea000-7f6c5ea4b000 r-xp 00004000 fd:00 143113780 /usr/lib/libfreebl3.so.41
7f6c5ea4b000-7f6c5ea6d000 r--p 00065000 fd:00 143113780 /usr/lib/libfreebl3.so.41
包含了有关进程状态的一般信息(文本格式)。不仅提供了有关UID/GID及进程其他数值的信息,还包括内存分配、进程能力、各个信号掩码的状态(待决、阻塞,等等)。
# cat status
Name: java
Umask: 0022
State: S (sleeping)
Tgid: 31561
Ngid: 0
Pid: 31561 #进程id
PPid: 31541 #父进程id
TracerPid: 0
Uid: 1000 1000 1000 1000
Gid: 1000 1000 1000 1000
FDSize: 512
Groups: 1000
VmPeak: 4611472 kB
VmSize: 4600164 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 1371028 kB
VmRSS: 1339168 kB
RssAnon: 1338532 kB
RssFile: 636 kB
RssShmem: 0 kB
VmData: 4578520 kB
VmStk: 132 kB
VmExe: 8 kB
VmLib: 9776 kB
VmPTE: 4064 kB
VmSwap: 66056 kB
Threads: 517
SigQ: 0/192296
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000200001000
SigCgt: 2000000001004ccf
CapInh: 00000000a80425fb
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 00000000a80425fb
CapAmb: 0000000000000000
NoNewPrivs: 0
Seccomp: 2
Speculation_Store_Bypass: vulnerable
Cpus_allowed: ffffffff
Cpus_allowed_list: 0-31
Mems_allowed: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001
Mems_allowed_list: 0
voluntary_ctxt_switches: 5
nonvoluntary_ctxt_switches: 2
stat
和statm
以一连串数字的形式,提供了进程及其内存消耗的更多状态信息。
fd子目录包含了一些文件,文件名都是数字。这些文件名表示进程的各个文件描述符。这里的每个文件都是一个符号链接,指向文件名对应的文件描述符在文件系统中的位置,当然得假定该描述符确实是文件。其他的文件类型,如果也能够通过文件描述符访问(如管道),那么将给出一个链接目标,如pipe:[1434]。
lrwx------. 1 admin admin 64 Jul 12 14:21 0 -> /dev/null
l-wx------. 1 admin admin 64 Jul 12 14:21 1 -> pipe:[67223432]
l-wx------. 1 admin admin 64 Jul 12 14:21 2 -> pipe:[67223433]
lr-x------. 1 admin admin 64 Jul 12 14:21 3 -> /usr/lib/jvm/java-1.8-openjdk/jre/lib/rt.jar
lr-x------. 1 admin admin 64 Jul 12 14:21 9 -> /opt/agent/plugins/apm-avro-plugin-7.0.0.jar
lr-x------. 1 admin admin 64 Jul 12 14:21 90 -> /dev/random
lr-x------. 1 admin admin 64 Jul 12 14:21 91 -> /dev/random
lr-x------. 1 admin admin 64 Jul 12 14:21 92 -> /dev/urandom
lr-x------. 1 admin admin 64 Jul 12 14:21 93 -> /dev/urandom
指向进程当前工作目录。如果用户有适当的权限,则可以使用cd cwd切换到该目录,而无需知道cwd到底指向哪个目录。
lrwxrwxrwx. 1 admin admin 0 Jul 12 14:05 cwd -> /cloud
指向包含了应用程序代码的二进制文件。
lrwxrwxrwx. 1 admin admin 0 Jul 12 14:05 exe -> /usr/lib/jvm/java-1.8-openjdk/jre/bin/java
指向进程的根目录。这不见得是全局的根目录(参见chroot
机制)。
不仅/proc
的子目录包含了信息,/proc
本身也包含了一些信息。与特定的内核子系统无关(或由几个子系统共享)的一般性信息,一般存放在/proc
下的文件中。
iomem
和ioports
提供了用来与设备通信的内存地址和端口的有关信息。
buddyinfo
和slabinfo
提供了伙伴系统和slab分配器当前的使用情况,而meminfo
给出了一般性的内存使用情况,分为高端内存、低端内存、空闲内存、已分配区域、共享区域、交换和回写内存。
Vmstat
给出了内存管理的其他特征信息,包括当前在内存管理的各个子系统中内存页的数目。
kallsyms
和kcore
项用于支持内核代码调试。前者是一个符号表,给出了所有全局内核变量和函数在内存中的地址。
kcore
是一个动态的内核文件,“包含”了运行中的内核的所有数据,即主内存的全部内容。
interrupts
保存了当前操作期间引发的中断的说明。
loadavg
和uptime
。前者给出了过去60秒、5分钟、15分钟的平均系统负荷(即,运行队列的长度),后者给出了系统的运行时间,即从系统启动以来经过的时间。
/proc/net
子目录提供了内核的各种网络选项的有关数据。其中保存了各种协议和设备数据,包括以下几个有趣的数据项。
udp
和tcp
提供了IPv4
的UDP
和TCP套接字的统计数据。IPv6
的对应数据保存在udp6
和tcp6
中。UNIX套接字的统计数据记录在unix
。
用于反向地址解析的ARP
表,可以在arp
文件中查看。
dev
保存了通过系统的网络接口传输的数据量的统计数据(包括环回接口)。该信息可用于检查网络的传输质量,因为其中也包括了传输不正确的数据包、被丢弃的数据包和冲突相关的数据。
有些网络驱动程序(如,流行的英特尔PRO/100芯片组的驱动程序)在/proc/net创建了额外的子目录,提供了更详细的特定于硬件的信息。
用于动态地检查和修改内核行为的系统控制参数,在proc
文件系统的数据项中,属于最多的一部分。但这并不是修改相关数据的唯一方法,还可以使用sysctl
系统调用。后者需要的工作量更多,因为首先必须写一个程序,来支持通过系统调用接口与内核通信。结果,在内核版本2.5开发期间,sysctl
机制标记为废弃(每次调用sysctl
时,内核将输出一个警告信息),计划在未来的某个时候去掉。但是,删除系统调用引起了争论,直至内核版本2.6.25,该调用仍然存在于内核中,而警告信息也仍然会出现。
sysctl
系统调用实际上是不必要的,因为通过/proc
接口对内核数据的操作已经简单到了极点。sysctl
参数由一个独立的子目录/proc/sys
管理,它进一步划分为各种子目录,对应于内核的各个子系统。
dr-xr-xr-x. 1 root root 0 Jul 12 14:38 abi
dr-xr-xr-x. 1 root root 0 Jul 12 14:04 crypto
dr-xr-xr-x. 1 root root 0 Jul 12 14:38 debug
dr-xr-xr-x. 1 root root 0 Jul 12 14:38 dev
dr-xr-xr-x. 1 root root 0 Jul 3 08:15 fs
dr-xr-xr-x. 1 root root 0 Jul 3 08:15 kernel
dr-xr-xr-x. 1 root root 0 Jul 12 14:04 net
dr-xr-xr-x. 1 root root 0 Jul 12 14:38 user
dr-xr-xr-x. 1 root root 0 Jul 12 14:38 vm