直道相思了无益
未妨惆怅是清狂
问题是什么?
如果要命令行运行某个程序,那么如何知道这个进程对资源的使用量呢?
从我的知识角度来说,第一反就是通过特殊的文件系统,比如读取/proc/<pid>/
目录下的某个文件,比如/proc/[pid]/stat
就会列出进程使用的CPU信息和内存信息,包括峰值和实时的值。
但问题就是进程退出后,/proc/[pid]/stat
这个文件就不见了,除非能有某种方法让进程退出前停一下。。。但是好像不太科学。。。
还有一种办法,就是利用系统调用wait3/wait4
。
fork + execl + wait3/4
fork函数
fork就不多说了,感觉无论是例子,还是理论还是其他,各个博客都讲的很多,也没什么好补充的。这里就不再多bb了。
execl
exec是一个家族,包括了好几个类似功能的函数,但效果都是一样的,通过命令行执行某个可执行文件,并让这个新生进程顶掉当前进程,且进程ID不变。
这里我比较好奇实现的原理,所以大概地查阅资料,了解一下。
#include <unistd.h>
int execl( const char *pathname, const char *arg0, ... /* (char *)0 */ );
int execv( const char *pathname, char *const argv[] );
int execle( const char *pathname, const char *arg0, ... /* (char *)0, char *const envp[] */ );
int execve( const char *pathname, char *const argv[], char *const envp[] );
int execlp( const char *filename, const char *arg0, ... /* (char *)0 */ );
int execvp( const char *filename, char *const argv[] );
6个函数返回值:若出错则返回-1,若成功则不返回值
从资料来看,原调用进程的内容除了进程号外,其他全部被新的进程替换了。另外,这里的可执行文件既可以是二进制文件,也可以是Linux下任何可执行的脚本文件。
新的进程会把原进程的代码段、数据段和内存堆栈全部进行替换,从而实现“顶替”。。。具体实现原理我也不知道是什么,系统调用就是这样,一脸懵逼。。。
这里我使用的是execl
函数:
int execl( const char *pathname, const char *arg0, ... /* (char *)0 */ );
- 第一个参数是可执行文件路径
- 第二个参数是可执行文件的名字
- 第三个参数是参数列表。。。没参数就传
nullptr/NULL
就行了
感觉问题不大。
wait3/wait4
这里我直接贴man文档的说明,然后我稍微翻译一下比较好,网上资料也不是很多。
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
pid_t wait3(int *status, int options,
struct rusage *rusage);
pid_t wait4(pid_t pid, int *status, int options,
struct rusage *rusage);
wait3和wait4除了能够等待子进程结束,给子进程收尸之外,还能通过最后的参数,返回一些资源使用信息。
二者的区别在于,wait3等的是所有的子进程,而wait4可以指定等待某个子进程。。。这个和waitpid区别不大。
如果rusage
不为空的话,对应的结构体就会被填充子进程的资源使用信息,这个的话可以见getrusage。
获取命令行执行的子进程资源使用信息
使用上面三个系统调用进行组合就能做到。
- 首先
fork
出子进程 - 然后用
exec
顶掉原来的子进程,替换成命令行执行的子进程 - 最后用
wait3/4
进行收尸
下面给出示例。。。。
首先是内存测试程序:
#include <cstdio>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <iostream>
#include <sys/time.h>
#include <sys/resource.h>
#include <cstring>
long wait4stop(pid_t pid) {
struct rusage usage;
int status = 99;
do {
if (wait4(pid, &status, 0, &usage) == -1 || WIFEXITED(status) || WIFSIGNALED(status))
return usage.ru_maxrss;
} while(!WIFSTOPPED(status));
return 1;
}
int main() {
// 修改成自己的可执行文件路径
std::string exefile = "/home/bbkgl/vimcode/test/build/testo";
pid_t chpid = -666;
if ((chpid = fork()) == 0) {
execl(exefile.c_str(), "testo", nullptr); // testo是可执行文件名字,注意修改!
}
long mem_size = wait4stop(chpid);
fprintf(stdout, "Memory use %.2lf MB.", 1.0 * mem_size / 1024);
return 0;
}
被测试程序:
#include <cstdio>
#include <iostream>
#include <unistd.h>
#include <random>
#include <cstring>
using namespace std;
constexpr int ARRAY_NUM = 50 * 1024 * 1024;
constexpr int MEM_SIZE = 4 * ARRAY_NUM;
int main() {
int *array = new int[ARRAY_NUM];
std::memset(array, 0, MEM_SIZE);
delete[] array;
return 0;
}
然后是结果: