在shell终端里不带任何参数,直接运行mount
命令可以显示正在挂载的文件系统。其中有这么一行
none on /proc type proc (rw)
这就是/proc
文件系统。第一个域显示none
,说明这个文件没有和任何硬件设备挂钩。/proc
文件系统实际上是一个通向Linux内核的窗口,看起来像一个能够向内核提供参数、数据结构、统计信息等的文件。/proc
文件系统的内容是随内核运行变化的。用户进程还可以通过改变/proc
文件系统内容来改变内核的设置。
在Linux手册的proc(5)
项里查看/proc
文件系统的详细介绍。其源码位于/usr/src/linux/fs/proc/
。
1 获取信息
/proc
文件系统中的信息大多都是可读的,也很容易解析。例如,/proc/cpuinfo
含有系统CPU的信息。
$ cat /proc/cpuinfo
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 61
model name : Intel(R) Core(TM) i5-5250U CPU @ 1.60GHz
stepping : 4
cpu MHz : 1599.999
cache size : 3072 KB
physical id : 0
siblings : 1
core id : 0
cpu cores : 1
apicid : 0
initial apicid : 0
fpu : yes
fpu_exception : yes
cpuid level : 20
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc pni pclmulqdq monitor ssse3 cx16 sse4_1 sse4_2 movbe popcnt aes xsave avx rdrand hypervisor lahf_lm abm 3dnowprefetch rdseed
bugs :
bogomips : 3199.99
clflush size : 64
cache_alignment : 64
address sizes : 39 bits physical, 48 bits virtual
power management:
在C语言中提取文件信息的一个简单办法是:先将文件读取到缓存中,然后调用sscanf
函数在内存中解析数据。
#include <stdio.h>
#include <string.h>
/* Returns the clock speed of the system’s CPU in MHz, as reported by /proc/cpuinfo.
* On a multiprocessor machine, returns the speed of the first CPU.
* On error returns zero.
*/
float get_cpu_clock_speed ()
{
FILE* fp;
char buffer[1024];
size_t bytes_read;
char* match;
float clock_speed;
/* Read the entire contents of /proc/cpuinfo into the buffer. */
fp = fopen (“/proc/cpuinfo”, “r”);
bytes_read = fread (buffer, 1, sizeof (buffer), fp);
fclose (fp);
/* Bail if read failed or if buffer isn’t big enough. */
if (bytes_read == 0 || bytes_read == sizeof (buffer))
return 0;
/* NUL-terminate the text. */
buffer[bytes_read] = ‘ ’;
/* Locate the line that starts with “cpu MHz”. */
match = strstr (buffer, “cpu MHz”);
if (match == NULL)
return 0;
/* Parse the line to extract the clock speed. */
sscanf (match, “cpu MHz : %f”, &clock_speed);
return clock_speed;
}
int main ()
{
printf (“CPU clock speed: %4.0f MHz
”, get_cpu_clock_speed ());
return 0;
}
不同版本Linux的/proc
文件系统不尽相同。编写跨版本程序时一定要考虑到这个问题。
2 进程条目
每一个正在运行的进程都在/proc
文件系统对应一个文件夹。文件夹名称就是相应进程的ID。这些文件夹随着进程的生成而建立,并随进程的终止而移除。这些文件夹里的文件都描述着对应进程的信息:
cmdline
里是进程的参数表。cwd
是一个指向当前进程工作目录的链接符号。environ
包含了进程的运行环境信息。exe
是一个指向进程正在执行的可执行文件的链接符号。fd
是一个子目录,里面都是进程所打开的文件描述符。maps
里都是映射到当前进程的地址空间的文件的信息,例如文件名称、所映射的内存地址以及这些地址的访问权限。root
是指向进程根目录的链接符号. 进程根目录通常就是系统根目录/
。进程在进程根目录可以调用超级用户(chroot)命令或系统调用。stat
包含有进程的运行状态信息和统计数据。这些内容很难读懂,但容易用代码解析。statm
包含有进程的内存使用信息。status
包含有进程的运行状态信息和统计数据,而且方便调阅。- 只有SMP Linux系统会有一个
cpu
条目,里面是“breakdown of process time (user and system) by CPU“。
出于安全考虑,其中一些条目只有进程所有者或超级用户具有访问权限。
/proc/self
每条进程调用/proc/self
都会指向各自的/proc/<pid>
目录。
下面代码展示了如何利用/proc
文件系统实现getpid
函数,即获取调用进程的PID。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
/* Returns the process ID of the calling processes, as determined from the /proc/self symlink. */
pid_t get_pid_from_proc_self ()
{
char target[32];
int pid;
/* Read the target of the symbolic link. */
readlink (“/proc/self”, target, sizeof (target));
/* The target is a directory named for the process ID. */
sscanf (target, “%d”, &pid);
return (pid_t) pid;
}
int main ()
{
printf (“/proc/self reports process id %d
”, (int) get_pid_from_proc_self ());
printf (“getpid() reports process id %d
”, (int) getpid ());
return 0;
}
进程参数表
每一个进程的/proc/<pid>/cmdline
里有进程的参数表。里面的参数都是单个字符,并用NUL
隔开。不过问题是,大部分字符串函数无法处理字符串中间夹杂的NUL
。
NUL
vsNULL
NUL
是一个字符,整数值为0。而NULL
是一个值为0的指针。C语言中,所有字符串的结尾都是
NUL
。所以字符串Hello, world!
虽然只有13个字符,但在C语言眼里中,这里有14个字符。因为还有一个NUL
在感叹号之后。在C语言中,NUL
被定义为