Linux下C/C++实现进程内存使用分析工具(memstat)

在做Linux系统优化的时候,提到进程消耗的内存大小,我们或多或少听到VSS、RSS、PSS、USS等信息。自然的,Linux也提供了非常多的方法来监控宝贵的内存资源的使用情况。可以在Linux上敲命令 ps 、top、free查看得到。

我们在Linux上启动进程,会有一个栈空间(stack)和一个堆空间(heap), 栈空间用于函数调用和局部变量,堆空间是C语言的 malloc 来分配的全局指针。这些都是进程的私有数据,除了这些,还有映射进来的动态库,进程间的共享内存等共享空间。另外,从进程自身的角度看,虚拟内存是进程独立的,所有内存都是私有的,包括自身代码、共享库、堆栈等,它不用关心共享内存的事情。但实际上在物理内存的层面,很多东西是可以共享的,比如共享的代码库(.so)、自身代码甚至是自身运行时私有的堆栈内存。

什么是虚拟内存与物理内存

简单来讲,当我们的进程向系统申请内存时,比如通过malloc方法,得到的其实是虚拟内存。物理内存对于进程来说是透明的,进程直接操作的是虚拟内存。而数据和代码是存放在真实的物理内存的,之所以进程在虚拟内存中寻址可以获取数据,是因为虚拟内存与物理内存存在着映射关系。

说一说RSS、SWP、USS、SHR、WSS

  • RSS

RSS表示了进程中真正被加载到物理内存中的页的大小。但是用它来表示进程占用的内存大小也不太合适,因为还有个共享代码库的概念(Shared Libraries)。

比如libxxx.so这个程序库,有多个进程会用到它,而系统在物理内存只会加载一遍这个代码库,然后这块物理内存会被映射到不同进程的虚拟内存空间中,对于单独的进程来说,就像是这个库只加载在自己的虚拟内存中一样,不需要关心它是否与其它进程共享。

而进程的RSS是包含这块共享库的内存空间的,因此如果简单把系统中所有进程的RSS相加的话,结果是比系统总的内存大的,因为共享库占的内存被计算了多遍。

  • SWP

Linux中Swap(即:交换分区),类似于Windows的虚拟内存,就是当内存不足的时候,把一部分硬盘空间虚拟成内存使用,从而解决内存容量不足的情况。Android是基于Linux的操作系统,所以也可以使用Swap分区来提升系统运行效率。

  • USS

是单个进程私有的内存大小,即该进程独占的内存部分。USS揭示了运行一个特定进程在的真实内存增量大小。如果进程终止,USS就是实际被返还给系统的内存大小。

  • SHR

SHR是share(共享)的缩写,它表示的是进程占用的共享内存大小。

  • WSS

进程保持工作所需的内存,是估算进程最近访问过的 Pages 数,包括物理内存、内核内存、脏页。

使用 /proc 下文件

proc文件系统是在系统启动时动态创建的虚拟文件系统,并在系统关闭时解散。它包含关于当前正在运行的进程的有用信息,它被视为内核的控制和信息中心。proc文件系统还提供了内核空间和用户空间之间的通信介质。

如果列出目录,您会发现每个进程的PID都有一个专用目录。

现在让我们检查指定PID的特定进程,您可以从ps命令获得任何正在运行的进程的PID.

ps -aux


在linux中,/proc包括每个正在运行的进程(包括内核进程)的目录,在名为/proc/PID的目录中,以下是存在的目录:

/proc/PID/cmdline 命令行参数。
/proc/PID/cpu 执行该命令的当前和最后一个cpu。
/proc/PID/cwd 链接到当前工作目录。
/proc/PID/environ 环境变量的值。
/proc/PID/exe 链接到此进程的可执行文件。
/proc/PID/fd 目录,其中包含所有文件描述符。
/proc/PID/maps 内存映射到可执行文件和库文件。
/proc/PID/mem 此进程持有的内存。
/proc/PID/root 链接到此进程的根目录。
/proc/PID/stat 进程状态。
/proc/PID/statm 进程内存状态信息。
/proc/PID/status 可读形式的过程状态。
/ proc / PID/ pagemap 来获取给定页面的物理地址。
/ proc / PID/comm 包含进程的命令名
/ proc / PID/smaps显示每个分区更详细的内存占用数据

maps: 文件可以查看某个进程的代码段、栈区、堆区、动态库、内核区对应的虚拟地址


smaps:文件是基于 /proc/PID/maps 的扩展,他展示了一个进程的内存消耗,比同一目录下的maps文件更为详细。maps文件只能显示简单的分区,smap文件可以显示每个分区的更详细的内存占用数据。

在这里插入图片描述
pagemap :此文件允许用户空间进程找出每个虚拟页面映射到的物理帧。

pagemap条目是二进制格式。专门用来 记录所链接进程的物理页号信息 。

kpagecount:这个文件包含64位计数 , 表示每一页被映射的次数,按照PFN值固定索引。 kpageflags:此文件包含为64位的标志集 ,表示该页的属性,按照PFN索引。

memstat-进程内存使用分析工具C/C++实现

在Linux下,一切都作为文件进行管理;甚至设备也可以作为文件访问。尽管可能认为“普通”文件是文本文件或二进制文件,但/proc目录包含一种奇怪的类型:虚拟文件。这些文件已列出,但实际上并不存在于磁盘上。

....
static void get_system_meminfo(void)
{
	FILE *meminfo_file;

	meminfo_file = fopen("/proc/meminfo", "r");
	if (!meminfo_file)
		die("fopen(/proc/meminfo failed (%s)", strerror(errno));

	line[0] = '\0';
	while (fgets(line, sizeof(line), meminfo_file)) {
		if (!memcmp(line, "MemTotal:", 9))
			meminfo.mem_total = read_proc_count(&line[9]);
		else if (!memcmp(line, "MemFree:", 8))
			meminfo.mem_free = read_proc_count(&line[8]);
		else if (!memcmp(line, "MemAvailable:", 13))
			meminfo.mem_avail = read_proc_count(&line[13]);
		else if (!memcmp(line, "Shmem:", 6))
			meminfo.shared = read_proc_count(&line[6]);
		else if (!memcmp(line, "Buffers:", 8))
			meminfo.buffers = read_proc_count(&line[8]);
		else if (!memcmp(line, "Cached:", 7))
			meminfo.cached = read_proc_count(&line[7]);
		else if (!memcmp(line, "SwapCached:", 11))
			meminfo.swap_cached = read_proc_count(&line[11]);
		else if (!memcmp(line, "SwapTotal:", 10))
			meminfo.swap_total = read_proc_count(&line[10]);
		else if (!memcmp(line, "SwapFree:", 9))
			meminfo.swap_free = read_proc_count(&line[9]);
		else if (!memcmp(line, "PageTables:", 11))
			meminfo.page_tables = read_proc_count(&line[11]);
		else if (!memcmp(line, "KernelStack:", 12))
			meminfo.k_stacks = read_proc_count(&line[12]);
		else if (!memcmp(line, "Slab:", 5))
			meminfo.k_slabs = read_proc_count(&line[5]);
		else if (!memcmp(line, "KReclaimable:", 13))
			meminfo.k_reclaimable = read_proc_count(&line[13]);
		else if (!memcmp(line, "Hugepagesize:", 13))
			meminfo.huge_page_size = read_proc_count(&line[13]);
		line[0] = '\0';
	}

	fclose(meminfo_file);
}
...
int main(int argc, char *argv[])
{

	first_pid = parse_command_line(argc, argv);
	if (first_pid < argc)
		parse_pids_from_cmdline(argc, argv, first_pid);
	else
		parse_pids_from_proc();

	get_system_config();
	get_system_meminfo();

	if (args.general)
		print_general_info();

	if (args.maps) {
		kpc_fd = open("/proc/kpagecount", O_RDONLY);
		if (kpc_fd < 0)
			die("failed to open /proc/kpagecount (%s)", strerror(errno));

		kpf_fd = open("/proc/kpageflags", O_RDONLY);
		if (kpf_fd < 0)
			die("failed to open /proc/kpageflags (%s)", strerror(errno));
	}

	if (!args.verbose)
		print_heading();

	wss_grand_total = 0;

	for (pid = args.pids; *pid; ++pid) {

		sprintf(path, "/proc/%u/cmdline", *pid);
		cmd_file = fopen(path, "r");
		if (!cmd_file) {
			fprintf(stderr, "Failed to access /proc/%u/\n", *pid);
			continue;
		}

		if (!fgets(cmdline, sizeof(cmdline), cmd_file))
			cmdline[0] = '\0';
		fclose(cmd_file);

		if (cmdline[0] == '\0') {
			if (!args.all)
				continue;   /* kernel process */

			sprintf(path, "/proc/%u/comm", *pid);
			cmd_file = fopen(path, "r");
			if (!cmd_file)
				die("failed to open /proc/PID/comm (%s)", strerror(errno));

			if (!fgets(&cmdline[1], sizeof(cmdline) - 2, cmd_file)) {
				cmdline[0] = '\0';
			} else {
				cmdline[0] = '[';
				char *p = strchr(cmdline, '\n');
				if (p)
					*p = ']';
			}
			fclose(cmd_file);
		}

		if (args.verbose)
			print_verbose_heading(*pid, cmdline);

		memset(&total, 0, sizeof(total));

		if (args.maps)
			maps_count_process(*pid, kpc_fd, kpf_fd, &total);
		else
			smaps_count_process(*pid, &total);

		wss_grand_total += total.wss;

		if (args.kibyte)
			reduce_pstats_to_kib(&total);

		if (args.verbose)
			print_verbose_totals(&total);
		else
			print_totals(*pid, &total, cmdline);
	}

...
}
...

If you need to add the complete source code of memstat, you can use WeChat (c17865354792)

运行结果

memstat - 显示整个系统内存使用情况。memstat通过遍历/proc下所有进程,然后解析内存使用情况。

给定一个进程ID,memstat可以显示进程pid的内存使用情况

总结

/proc文件系统是一种内核和内核模块用来向进程发送信息的机制。这个伪文件系统可以和内核内部的数据结构进行交互,获取实时的进程信息。注意,/proc文件系统是存储与内存而不是硬盘,/proc虚拟文件系统实质是以文件系统的形式访问内核数据的接口。内存管理是一个巨大的话题,后续再分享。

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

参考:proc(5) - Linux manual page

在QNX系统中,可以使用很多不同的命令来查看设备内存大小。其中最常用的命令是vmstatmemstat和top。这些命令可以告诉我们整个系统的内存使用情况以及每个进程内存使用情况。同时也可以使用一些特定的命令来查看某个特定设备的内存大小。 首先,我们需要了解一下在QNX系统中设备内存的概念。在QNX系统中,设备内存通常是指设备所占用的内存空间,包括CPU内存和物理内存。对于一个设备而言,他的内存大小和其工作功能息息相关。某些设备可能只需要占用很小的一部分内存,而某些大型设备则需要大量的内存来存储数据和指令。 为了查看QNX设备的内存大小,可以使用QNX Neutrino的内存管理工具。该工具提供了一些重要的命令,包括vmstatmemstat和top。下面将简单介绍这三个命令的用法。 vmstat命令用于监测系统内存使用和处理器活动情况。要使用该命令,只需在命令行中输入“vmstat”即可。该命令将返回系统当前的内存使用情况以及CPU的活动情况。 memstat命令用于显示QNX系统的内存使用情况和系统内存区域的详细信息。要使用该命令,只需在命令行中输入“memstat”即可。该命令将返回系统当前的内存使用情况,包括内存总量、可用内存、缓存和虚拟内存等方面的详细信息。 top命令用于监测QNX系统中各个进程的CPU和内存活动情况。通过该命令,用户可以了解系统中各个进程的当前状态以及占用资源的情况。要使用该命令,只需在命令行中输入“top”即可。 除了以上三个命令,还可以使用cat /proc/meminfo命令来查看内存信息并了解设备内存使用情况。 总之,在QNX系统中,可以使用多种不同的命令来查看设备内存大小。用户可以根据自己的需要选择最合适的命令进行监测。同时在使用这些命令时,还需要注意分析返回的数据,以便确定设备内存使用情况是否正常。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值