• Linux中的/proc文件系统详解(C/C++代码实现)


    Linux /proc这个特殊的目录包含有关Linux系统的所有详细信息,包括其内核、进程和配置参数。通过研究/proc目录,可以了解Linux命令的工作原理,甚至可以执行一些管理任务。

    走进Linux的/proc目录

    今天,我们将查看/proc目录并熟悉它。/proc目录存在于所有Linux系统上,无论其风格或体系结构如何。

    /proc中的文件不是真正的文件,它们充当内核数据结构和进程信息的接口。由于它们不是真正的文件,文件大小等属性不适用于它们。


    最有趣的部分是,/proc文件系统中几乎所有文件的大小都是0字节。让许多用户困惑的是,尽管它们的大小为0,但在查看时仍然包含数据。这怎么可能?

    Linux能够处理许多不同类型的文件系统,因为它被称为VFS(虚拟文件系统)。/proc文件系统也由内核使用VFS访问。因此,当用户试图访问/proc文件系统中的文件时,proc文件系统会借助内核中的信息创建该文件的内容。这就是为什么在列出/proc目录时,大多数目录的大小显示为0字节,但在访问时会动态填充。

    让我们实际检查并理解它。Linux中有一个名为file的命令。

    file [ -bcnsvzL ] [ -f 命名文件 ] [ -m 幻数文件 ] file …


    linux中的“file”命令用于通过检查文件的内容来确定文件的类型。如果文件为空,它将给出“文件为空”的输出。让我们尝试使用file命令检查任何/proc文件的文件类型。


    输出表明文件为空。但是,让我们尝试使用vi、cat或更少的编辑器访问该文件。


    因此,当您访问内容时,当前值将从内核填充。这就是为什么您可以从/proc中的文件获得系统的最新和准确状态的原因。

    我们之前看到proc手册页将proc定义为“进程信息伪文件系统”。这是因为它包含所有当前正在运行的进程的详细信息。让我们看看/proc的目录列表


    可以在上面显示的目录列表中看到,里面有很多编号的目录。这些目录用相应的PID编号。当进程开始和停止时,它们会动态地出现和消失。以各自PID命名的每个目录都包含进程当前状态的详细信息,我们随便进入一个PID看看里面的信息。

    让我们看看/proc中PID目录的内容。下图显示了PID目录的内容。

    Linux中 /proc/[pid] 目录各文件简析

    在 /proc 目录里, 每个正在运行的进程都有一个以该进程 ID 命名的子目录, 其下包括如下的目录和伪文件

    • auxv

    包含传递给进程的 ELF 解释器信息,格式是每一项都是一个 unsigned long长度的 ID 加上一个 unsigned long 长度的值。

    • cmdline

    该文件保存了进程的完整命令行. 如果该进程已经 被交换出内存, 或者该进程已经僵死, 那么就没有 任何东西在该文件里,这时候对该文件的读操作将返回零 个字符. 该文件以空字符 null 而不是换行符作为结 束标志.

    • comm

    comm 包含进程的命令名。

    cwd

    一个符号连接, 指向进程当前的工作目录. 例如, 要找出进程 20 的 cwd, 你可以:cd /proc/20/cwd;
    /bin/pwd

    environ

    该文件保存进程的环境变量, 各项之间以空字符分隔, 结尾也可能是一个空字符. 因此, 如果要输出进程 1 的环境变量, 你应该: (cat /proc/1/environ; echo) | tr “;\000”; “;\n”;

    exe

    也是一个符号连接, 指向被执行的二进制代码

    fd

    进程所打开的每个文件都有一个符号连接在该子目 录里, 以文件描述符命名, 这个名字实际上是指向 真正的文件的符号连接,(和 exe记录一样). 例如, 0 是标准输入, 1是标准输出, 2 是标准错误, 等等

    maps

    该文件包含当前的映象内存区及他们的访问许可.

    格式如下:
       address           perms offset   dev   inode
       00000000-0002f000 r-x-- 00000400 03:03 1401
       0002f000-00032000 rwx-p 0002f400 03:03 1401
       00032000-0005b000 rwx-p 00000000 00:00 0
       60000000-60098000 rwx-p 00000400 03:03 215
       60098000-600c7000 rwx-p 00000000 00:00 0
       bfffa000-c0000000 rwx-p 00000000 00:00 0
    
    address 是进程所占据的地址空间, perms 是权限集:
       r = read
       w = write
       x = execute
       s = shared
       p = private (copy on write)
    
    offset 是文件或者别的什么的偏移量, dev 是设备号(主设 备号:从设备号), 而 inode 则是设备的节点号. 0 表明没有 节点与内存相对应, 就象 bss 的情形.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    mem

    该文件并不是 mem (1:1) 设备, 尽管它们有相同的设备号. /dev/mem 设备是做任何地址转换之前的物理内存, 而这里的 mem 文件是访问它的进程的内存.目前这个 mem 还不能 mmap(2) (内存映射)出去,而且可能一直要等到内核中增加了一个通用的mmap(2) 以后才能实现. (也许在你读本手册页时这一切已经发生了)

    mmap

    mmap(2) 做的 maps 映射目录,是和 exe, fd/* 等类似的符号连接. 请注意 maps 包含了比
    /proc/*/mmap 更多的信息, 所以应该废弃 mmap.

    root

    依靠系统调用 chroot(2), unix 和 linux 可以让 每个进程有各自的文件系统根目录. 由 chroot(2) 系统调用设置. 根指向文件系统的根,性质就象 exe, fd/* 等一样.

    stat

    进程状态信息, 被命令 ps(1) 使用.


    通过访问/proc中的文件来获取信息

    cpuinfo

    保存了CPU 以及体系架构依赖条目的列表. 对于不同的系 统架构有不同的列表, 共有的两项是 cpu 和 BogoMIPS, cpu 可能是当前在用的 CPU, 而 BogoMIPS 则是内核初始化时计算出的一个系统常数.

    devices

    主设备号及设备组的列表, 文本格式. MAKEDEV 脚本使用 该文件来维持内核的一致性.

    dma

    一个列表, 指出正在使用的ISA DMA (直接内存访问)通道.

    filesystems

    以文本格式列出了被编译进内核的文件系统. 当没有给 mount(1) 指明哪个文件系统的时候, mount(1) 就依靠该文件遍历不同的文件系统.

    interrupts

    该文件以 ASCII 格式记录了(至少是在 i386 体系上的)每次 IRQ 的中断数目.

    ioports

    该文件列出了当前在用的已注册 I/O 端口范围.

    kcore

    该伪文件以 core 文件格式给出了系统的物理内存映象, 再 利用未卸载的内核 (/usr/src/linux/tools/zSystem), 我 们就可以用 GDB 查探当前内核的任意数据结构.

    kmsg

    可以用该文件取代系统调用 syslog(2) 来记录内核信息. 但是读该文件需要超级用户权限, 并且一次只能有一个进 程可以读该文件,因而如果一个使用了 syslog(2)系统调用功能来记录内核信息的系统日志进程正在运行的话,别的进程就不能再去读该伪文件了.该文件的内容可以用 dmesg(8) 来察看.

    ksyms

    该文件保存了内核输出的符号定义, modules(X) 使用该文件 动态地连接和捆绑可装载的模块.

    loadavg

    平均负载数给出了在过去的 1, 5, 15 分钟里在运行队列里 的任务数, 与 uptime(1) 等命令的结果相同.

    locks

    这个文件显示当前文件锁.

    malloc

    只有在编译时定义了 CONFIGDEBUGMALLOC 才会有该文件.

    meminfo

    free(1) 利用该文件来给出系统总的空闲内存和已用内存 (包括物理内存和交换内存), 以及内核所使用的共享内存 和缓冲区.该文件与 free(1) 格式相同, 但是以字节为单位而不是 KB.

    modules

    列出了系统已载入的模块, 文本格式.

    self

    当某进程访问 /proc 目录时, 该目录就指向 /proc 下以该进 程 ID 命名的目录.

    stat

    内核及系统的统计数据.

    uptime

    该文件包含两个数: 系统正常运行时间和总的空闲时间, 都以秒为单位.

    version

    指明了当前正在运行的内核版本

    获取当前进程的名称 C/C++代码实现
    proc.h

    ...
    struct proc {
    	pid_t pid;
    	pid_t ppid;
    	pid_t tracer;
    	unsigned long *auxvals;
    	size_t nauxvals;
    	char state;
    	char *path;
    	char *name;
    	struct environ *environ;
    	size_t nenviron;
    	char **cmdline;
    	size_t ncmdline;
    	pid_t *threads;
    	size_t nthreads;
    };
    
    struct module {
    	void *base;
    	size_t size;
    	void *end;
    	char *path;
    	char *name;
    };
    
    struct page {
    	void *base;
    	size_t size;
    	unsigned long offset;
    	void *end;
    	int prot;
    	int flags;
    };
    
    int proc_enumpids(int(*callback)(pid_t pid, void *arg), void *arg);
    int proc_checkpid(pid_t pid);
    pid_t proc_getppid(pid_t pid);
    pid_t proc_gettracer(pid_t pid);
    int proc_enumauxvals(pid_t pid, int(*callback)(unsigned long type,
    		     unsigned long val, void *arg), void *arg);
    unsigned long proc_getauxval(pid_t pid, unsigned long type);
    uid_t proc_getuid(pid_t pid);
    uid_t proc_geteuid(pid_t pid);
    gid_t proc_getgid(pid_t pid);
    gid_t proc_getegid(pid_t pid);
    char proc_getstate(pid_t pid);
    unsigned long proc_getplatform(pid_t pid);
    size_t proc_getpath(pid_t pid, char **ppathbuf, size_t maxlen);
    size_t proc_getname(pid_t pid, char **pnamebuf, size_t maxlen);
    int proc_enumenviron(pid_t pid,
    		     int(*callback)(char *name, char *value, void *arg),
    		     void *arg);
    int proc_enumcmdline(pid_t pid, int(*callback)(char *cmdarg, void *arg),
    		     void *arg);
    size_t proc_getcmdline(pid_t pid, char **pcmdlinebuf, size_t maxlen);
    int proc_enumthreads(pid_t pid, int(*callback)(pid_t tid, void *arg),
    		     void *arg);
    unsigned long proc_getentry(pid_t pid);
    ssize_t proc_vmread(pid_t pid, off_t src, void *dst, size_t size);
    ssize_t proc_vmwrite(pid_t pid, off_t dst, void *src, size_t size);
    int proc_openproc(pid_t pid, struct proc *pproc);
    void proc_closeproc(struct proc *pproc);
    
    • 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

    proc.c

    ...
    int proc_openproc(pid_t pid, struct proc *pproc)
    {
    	int ret = -1;
    	char *status_filebuf;
    	char status_path[64] = { 0 };
    
    	snprintf(status_path, sizeof(status_path) - 1, "/proc/%d/status", pid);
    	status_filebuf = get_filebuf(status_path);
    	if (!status_filebuf)
    		goto EXIT;
    
    	pproc->pid = pid;
    
    	pproc->ppid = _proc_getppid(status_filebuf);
    	if (pproc->ppid == (pid_t)-1)
    		goto ERR_PPID;
    	
    	pproc->tracer = _proc_gettracer(status_filebuf);
    	if (pproc->tracer == (pid_t)-1)
    		goto ERR_TRACER;
    	
    	pproc->auxvals = (unsigned long *)NULL;
    	pproc->nauxvals = 0;
    	proc_enumauxvals(pproc->pid, _proc_openproc_callback_auxv,
    			 (void *)pproc);
    	if (!pproc->auxvals || !pproc->nauxvals)
    		goto ERR_AUXVALS;
    	
    	pproc->state = _proc_getstate(status_filebuf);
    	if (pproc->state == 0)
    		goto ERR_STATE;
    	
    	if (!proc_getpath(pproc->pid, &pproc->path, 0))
    		goto ERR_PATH;
    	
    	if (!_proc_getname(status_filebuf, &pproc->name, 0))
    		goto ERR_NAME;
    	
    	pproc->environ = (struct environ *)NULL;
    	pproc->nenviron = 0;
    	if (proc_enumenviron(pproc->pid, _proc_openproc_callback_env, (void *)pproc))
    		goto ERR_ENVIRON;
    	
    	pproc->cmdline = (char **)NULL;
    	pproc->ncmdline = 0;
    	if (proc_enumcmdline(pproc->pid, _proc_openproc_callback_cmd, (void *)pproc))
    		goto ERR_CMDLINE;
    
    	pproc->threads = (pid_t *)NULL;
    	pproc->nthreads = 0;
    	if (proc_enumthreads(pproc->pid, _proc_openproc_callback_tid, (void *)pproc))
    		goto ERR_THREADS;
    
    	ret = 0;
    	goto EXIT; /* skip errors */
    
    ERR_THREADS:
    	{
    		size_t i;
    
    		for (i = 0; i < pproc->ncmdline; ++i)
    			free(pproc->cmdline[i]);
    		
    		free(pproc->cmdline);
    	}
    ERR_CMDLINE:
    	{
    		size_t i;
    
    		for (i = 0; i < pproc->nenviron; ++i) {
    			free(pproc->environ[i].name);
    			free(pproc->environ[i].value);
    		}
    
    		free(pproc->environ);
    	}
    ERR_ENVIRON:
    	free(pproc->name);
    ERR_NAME:
    	free(pproc->path);
    ERR_PATH:
    ERR_STATE:
    	free(pproc->auxvals);
    ERR_AUXVALS:
    ERR_TRACER:
    ERR_PPID:
    	free(status_filebuf);
    EXIT:
    	return ret;
    }
    ...
    
    • 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

    main.c

    int main(int argc, char **argv)
    {
    ...
    
    	pid = getpid();
    	if (proc_openproc(pid, &proc)) {
    		printf("[!] Unable to open process\n");
    		return -1;
    	}
    
    	printf("[*] PID: %d\n", proc.pid);
    	printf("[*] PPID: %d\n", proc.ppid);
    	printf("[*] Tracer: %d\n", proc.tracer);
    	printf("[*] UID: %d\n", (uid_t)proc.auxvals[AT_UID]);
    	printf("[*] EUID: %d\n", (uid_t)proc.auxvals[AT_EUID]);
    	printf("[*] GID: %d\n", (gid_t)proc.auxvals[AT_GID]);
    	printf("[*] EGID: %d\n", (gid_t)proc.auxvals[AT_EGID]);
    	printf("[*] Entry: %p\n", (void *)proc.auxvals[AT_ENTRY]);
    	printf("[*] State: %c\n", proc.state);
    	printf("[*] Path: %s\n", proc.path);
    	printf("[*] Name: %s\n", proc.name);
    	printf("[*] Environment Variables: \n");
    	{
    		size_t i;
    		
    		for (i = 0; i < proc.nenviron; ++i) {
    			printf("\t%s=%s\n", proc.environ[i].name,
    			       proc.environ[i].value);
    		}
    	}
    	printf("[*] Command Line: ");
    	{
    		size_t i;
    
    		for (i = 0; i < proc.ncmdline; ++i) {
    			printf("%s ", proc.cmdline[i]);
    		}
    	}
    	printf("\n");
    	printf("[*] Threads: { ");
    	{
    		size_t i;
    		
    		for (i = 0; i < proc.nthreads; ++i) {
    			printf("%d ", proc.threads[i]);
    		}
    	}
    	printf("}\n");
    
    	printf("===================\n");
    
    	proc_closeproc(&proc);
    
    	printf("Press ENTER to exit...");
    	getchar();
    
    	return 0;
    }
    
    • 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

    编译运行

    在这里插入图片描述proc获取当前进程的名称,其中包含有关进程的宝贵信息,如命令行、绝对路径、ppid、跟踪程序pid、状态、环境变量等。

    If you need the complete source code of proc, add your WeChat number (c17865354792)

    总结

    proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间。它以文件系统的方式为访问系统内核数据的操作提供接口。

    系统中当前运行的每一个进程都有对应的一个目录在 proc 下,以进程的 PID 号为目录名,它们是读取进程信息的接口。

    Welcome to follow WeChat official account【程序猿编码

    参考:https://man7.org/linux/man-pages/man5/proc.5.html

  • 相关阅读:
    RK3399平台开发系列讲解(PCI/PCI-E)5.51、PCIE EP模式软件架构
    CompletableFuture 异步调用,获取返回值
    创建django文件
    一篇文章带您了解PHP数组
    cmd/bat 输出符,控制台日志输出到文件
    rocketmq
    学习人工智能
    element ui el-table表格复选框,弹框关闭取消打勾选择
    CRC8校验算法源码——C语言版
    获取zabbix API 监控数据shell脚本,自动日常巡检服务器信息、并发送指定群组
  • 原文地址:https://blog.csdn.net/chen1415886044/article/details/128071810