本文介绍C++中几个统计程序内存数据的方式:<sys/resource.h>(Unix/Linux)、GetProcessTimes(Windows)
<sys/resource.h>
介绍
<sys/resource.h> 是一个 Unix 或类 Unix 系统(如 Linux、macOS)提供的头文件,用于获取进程资源使用环境等信息。实测它无法在Windows上用,就算你用的MingW也不行。
rusage 布局体的定义如下:
- struct rusage {
- struct timeval ru_utime
- ; /* user CPU time used */
- struct timeval ru_stime
- ; /* system CPU time used */
- long ru_maxrss; /* maximum resident set size */
- long ru_ixrss; /* integral shared memory size */
- long ru_idrss; /* integral unshared data size */
- long ru_isrss; /* integral unshared stack size */
- long ru_minflt; /* page reclaims (soft page faults) */
- long ru_majflt; /* page faults (hard page faults) */
- long ru_nswap; /* swaps */
- long ru_inblock; /* block input operations */
- long ru_oublock; /* block output operations */
- long ru_msgsnd; /* messages sent */
- long ru_msgrcv; /* messages received */
- long ru_nsignals; /* signals received */
- long ru_nvcsw; /* voluntary context switches */
- long ru_nivcsw; /* involuntary context switches */
- };
复制代码 每一个成员都对应一个当前进程的内容,比如:
- 寄义:用户模式下进程使用的 CPU 时间。用户模式是指进程在执行用户程序代码时所处的模式,不涉及内核操作。
- 数据范例:struct timeval,该布局体包含两个成员 tv_sec(秒)和 tv_usec(微秒),用于精确表现时间。
- 寄义:内核模式下进程使用的 CPU 时间。当进程执行系统调用(如文件读写、网络操作等)时会进入内核模式。
- 数据范例:同样是 struct timeval。
- 寄义:进程使用的最大驻留集大小(Resident Set Size,RSS),以千字节(KB)为单位。驻留集是指当前时刻进程在物理内存中现实占用的页面聚集。
- 寄义:进程使用的共享内存的积分值。积分值通常是在一段时间内对某个资源使用量的累积。
- 寄义:进程使用的非共享数据段的积分值。非共享数据段包含了进程独有的数据,如局部变量等。
- 寄义:进程使用的非共享栈段的积分值。栈用于存储函数调用的上下文信息。
- 寄义:软页面错误(Page Reclaims)的次数。软页面错误发生时,所需的页面固然不在物理内存中,但可以通过简单的操作(如从互换空间或文件系统缓存中获取)将其调入内存,无需进行磁盘 I/O。
- 寄义:硬页面错误(Page Faults)的次数。硬页面错误发生时,所需的页面不仅不在物理内存中,还需要从磁盘读取,会导致显着的性能开销。
- 寄义:进程被互换出物理内存的次数。当系统内存不足时,操作系统会将一些不常用的进程或页面互换到磁盘上的互换空间,以腾出物理内存供其他进程使用。
- 寄义:进程执行的块输入操作次数。块输入操作通常涉及从磁盘等存储装备读取数据块。
- 寄义:进程执行的块输出操作次数。块输出操作通常涉及将数据块写入磁盘等存储装备。
- 寄义:进程发送的消息数量。消息传递是一种进程间通信(IPC)机制,用于在差别进程之间互换数据。
- 寄义:进程吸收到的信号数量。信号是一种异步通信机制,用于关照进程发生了某些变乱,如中断、停止等。
- 寄义:进程志愿进行上下文切换的次数。志愿上下文切换通常发生在进程自动放弃 CPU 控制权,如等待 I/O 操作完成、调用 sleep 函数等。
- 寄义:进程非志愿进行上下文切换的次数。非志愿上下文切换通常是由于操作系统调度器为了公中分配 CPU 时间,强制将进程从运行状态切换到就绪状态。
使用
getrusage 函数用于获取指定进程的资源使用信息,其源代码(接口)如下:
- /* Return resource usage information on process indicated by WHO
- and put it in *USAGE. Returns 0 for success, -1 for failure. */
- extern int getrusage (__rusage_who_t __who, struct rusage *__usage) __THROW;
复制代码
- who:指定要获取资源使用信息的进程范例,可以是以下值之一:
- RUSAGE_SELF:获取当前进程的资源使用信息。
RUSAGE_CHILDREN:获取当前进程的全部已停止子进程的资源使用信息。
RUSAGE_THREAD:获取当前线程的资源使用信息(某些系统支持)。
- usage:指向 rusage 布局体的指针,用于存储获取到的资源使用信息。
返回值为0时调用成功,-1时失败。可以写个简单的代码测试调用:
- #include <iostream>
- #include <sys/resource.h>
- #include <sys/time.h>
- // 打印 rusage 结构体中的信息
- void print_rusage(const struct rusage& usage) {
- // 用户模式下使用的 CPU 时间
- std::cout << "用户模式下使用的 CPU 时间:"
- << usage.ru_utime.tv_sec << " 秒 "
- << usage.ru_utime.tv_usec << " 微秒" << std::endl;
- // 内核模式下使用的 CPU 时间
- std::cout << "内核模式下使用的 CPU 时间:"
- << usage.ru_stime.tv_sec << " 秒 "
- << usage.ru_stime.tv_usec << " 微秒" << std::endl;
- // 进程使用的最大驻留集大小(以 KB 为单位)
- std::cout << "进程使用的最大驻留集大小:" << usage.ru_maxrss << " KB" << std::endl;
- // 进程使用的共享内存的积分值
- std::cout << "进程使用的共享内存的积分值:" << usage.ru_ixrss << std::endl;
- // 进程使用的非共享数据段的积分值
- std::cout << "进程使用的非共享数据段的积分值:" << usage.ru_idrss << std::endl;
- // 进程使用的非共享栈段的积分值
- std::cout << "进程使用的非共享栈段的积分值:" << usage.ru_isrss << std::endl;
- // 软页面错误(Page Reclaims)的次数
- std::cout << "软页面错误的次数:" << usage.ru_minflt << std::endl;
- // 硬页面错误(Page Faults)的次数
- std::cout << "硬页面错误的次数:" << usage.ru_majflt << std::endl;
- // 进程被交换出物理内存的次数
- std::cout << "进程被交换出物理内存的次数:" << usage.ru_nswap << std::endl;
- // 进程执行的块输入操作次数
- std::cout << "进程执行的块输入操作次数:" << usage.ru_inblock << std::endl;
- // 进程执行的块输出操作次数
- std::cout << "进程执行的块输出操作次数:" << usage.ru_oublock << std::endl;
- // 进程发送的消息数量
- std::cout << "进程发送的消息数量:" << usage.ru_msgsnd << std::endl;
- // 进程接收的消息数量
- std::cout << "进程接收的消息数量:" << usage.ru_msgrcv << std::endl;
- // 进程接收到的信号数量
- std::cout << "进程接收到的信号数量:" << usage.ru_nsignals << std::endl;
- // 进程自愿进行上下文切换的次数
- std::cout << "进程自愿进行上下文切换的次数:" << usage.ru_nvcsw << std::endl;
- // 进程非自愿进行上下文切换的次数
- std::cout << "进程非自愿进行上下文切换的次数:" << usage.ru_nivcsw << std::endl;
- }
- int main() {
- struct rusage usage;
- // 获取当前进程的资源使用信息
- if (getrusage(RUSAGE_SELF, &usage) == 0) {
- std::cout << "当前进程的资源使用信息:" << std::endl;
- print_rusage(usage);
- } else {
- std::cerr << "获取资源使用信息失败。" << std::endl;
- return 1;
- }
- return 0;
- }
复制代码 一次运行的效果:

<windows.h>
Windows上并没有雷同rusage这种工具,但是也提供了一些工具来辅助分析程序。比如可以使用GetProcessTimes函数来获取进程的用户模式和内核模式 CPU 时间:
- WINBASEAPI
- BOOL
- WINAPI
- GetProcessTimes(
- _In_ HANDLE hProcess,
- _Out_ LPFILETIME lpCreationTime,
- _Out_ LPFILETIME lpExitTime,
- _Out_ LPFILETIME lpKernelTime,
- _Out_ LPFILETIME lpUserTime
- );
- WINBASEAPI
- HANDLE
- WINAPI
- GetCurrentProcess(
- VOID
- );
复制代码 GetProcessTimes函数用于获取指定进程的创建时间、退出时间、内核模式 CPU 时间和用户模式 CPU 时间。数据泉源是进程的handler指针,可以通过GetCurrentProcess获得。
FILETIME 布局体用于表现一个特定的时间,使用两个 32 位的 DWORD 值(dwLowDateTime 和 dwHighDateTime)来表现一个 64 位的时间戳。为了方便处理这个 64 位时间戳,通常会将 FILETIME 布局体的值转换为 ULARGE_INTEGER 范例,其定义如下:
- typedef struct _FILETIME {
- DWORD dwLowDateTime;
- DWORD dwHighDateTime;
- } FILETIME, *PFILETIME, *LPFILETIME;
复制代码 ULARGE_INTEGER 是 Windows API 中定义的一个布局体,用于表现一个 64 位无符号整数。其定义如下:
- typedef union _ULARGE_INTEGER {
- struct {
- DWORD LowPart;
- DWORD HighPart;
- } DUMMYSTRUCTNAME;
- struct {
- DWORD LowPart;
- DWORD HighPart;
- } u;
- ULONGLONG QuadPart;
- } ULARGE_INTEGER;
复制代码 从定义可以看出,ULARGE_INTEGER 是一个团结体(union),这意味着 LowPart、HighPart 和 QuadPart 共享同一块内存空间。具体关系如下:
- LowPart:表现 64 位整数的低 32 位。DWORD 范例通常是 32 位无符号整数,用于存储 64 位值的较低部分。
- HighPart:表现 64 位整数的高 32 位。同样是 DWORD 范例,用于存储 64 位值的较高部分。
- QuadPart:表现整个 64 位无符号整数。ULONGLONG 范例是 64 位无符号整数范例,可以直接用来操作整个 64 位的值。
在早期的 Windows 系统中,部分硬件平台可能不直接支持 64 位整数运算。通过将 64 位整数拆分为高 32 位(HighPart)和低 32 位(LowPart),可以在不支持 64 位运算的环境下,使用 32 位运算来模仿 64 位运算。如允许以确保代码在差别的硬件平台和操作系统版本上都能正常工作。
最后我们可以写出如下的代码:
- #include <iostream>
- #include <windows.h>
- void printProcessCPUTime() {
- HANDLE hProcess = GetCurrentProcess();
- FILETIME creationTime, exitTime, kernelTime, userTime;
- if (GetProcessTimes(hProcess, &creationTime, &exitTime, &kernelTime, &userTime)) {
- ULARGE_INTEGER kernelTimeValue, userTimeValue;
- kernelTimeValue.LowPart = kernelTime.dwLowDateTime;
- kernelTimeValue.HighPart = kernelTime.dwHighDateTime;
- userTimeValue.LowPart = userTime.dwLowDateTime;
- userTimeValue.HighPart = userTime.dwHighDateTime;
- std::cout << "Kernel mode CPU time: " << kernelTimeValue.QuadPart / 10000000.0 << " seconds" << std::endl;
- std::cout << "User mode CPU time: " << userTimeValue.QuadPart / 10000000.0 << " seconds" << std::endl;
- } else {
- std::cerr << "Failed to get process CPU time." << std::endl;
- }
- }
- int main() {
- printProcessCPUTime();
- return 0;
- }
复制代码 一个运行的效果如下:
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |