解析proc/pid/cmdline以获取函数参数

5

我试图通过cmdline中的数据提取应用程序调用时使用的参数。

如果我像这样启动一个应用程序实例:

myapp 1 2

然后查看myapp的cmdline,我会看到类似于myapp12的东西。

我需要提取这些值,并使用以下代码来完成:


pid_t proc_id = getpid();

sprintf(buf,"/proc/%i/cmdline",proc_id);

FILE * pFile;
pFile = fopen (buf,"r");
if (pFile!=NULL)
{
    fread(buf,100,100,pFile);
    cout << "PID " << proc_id << endl;
    string str = buf;
    cout << buf << endl;
    size_t found=str.find_last_of("/\\");
    cout << " file: " << str.substr(found+1) << endl;

    fclose (pFile);
}

但我只得到了应用程序名称,没有参数...


更新来自回答:

好吧,我的问题现在似乎是如何读取cmdline文件而不停在第一个NULL字符处...

fopen(cmdline, "rb")

它不会做任何其他事情,因此...


今天为了解决这个问题,我想指出我不得不使用:fread(buf,1,100,pFile);(size=1,count=缓冲区长度)。 - opello
3个回答

9
通常我会用命令 /usr/bin/strings /proc/1337/cmdline 来完成这个任务。

7

/proc/XXX/cmdline中的所有命令行参数(以argv[]数组形式出现)实际上是由空字符分隔的字符串。

abatkin@penguin:~> hexdump -C /proc/28460/cmdline
00000000  70 65 72 6c 00 2d 65 00  31 20 77 68 69 6c 65 20  |perl.-e.1 while |
00000010  74 72 75 65 00                                    |true.|

这就解释了为什么当你使用cat命令来读取cmdline时,它们会被“粘”在一起(cat会忽略无效的NULL字符),以及为什么你的cout在第一个命令行参数(进程名称)后停止,因为它认为进程名称是以NULL结尾的字符串,并在此处停止查找更多字符。
处理命令行参数有几个选项。如果你只想要整个命令行作为一个巨大的字符串,可以循环从0到(numRead-2)(其中numRead是读取的字符数),并将任何NULL字节(curByte == 0)替换为空格。然后只需确保将最后一个字符设置为NULL字节(以防由于固定大小的缓冲区而截断内容)。
如果你想要一个包含所有参数的数组,需要更有创意。一种选择是循环从0到(numRead-1)并收集所有的NULL字节。然后分配一个长度为char*的数组。然后再次循环遍历命令行,将每个字符串的开头(即数组中的第一个字节,加上每个NULL字节后面的每个字节)设置为char*数组的连续元素。
请注意,由于你读取的是一个固定大小的缓冲区,因此超出该缓冲区的任何内容都将被截断。所以请记住,无论你做什么,都需要手动确保最后一个字符串的结尾被设置为NULL,否则大多数字符串处理函数都不知道字符串在哪里结束,会一直继续下去。

read或fread的问题在于它们会在第一个NULL处停止,无法获取其余参数...我该如何解决这个问题?干杯 - André Moreira
1
readfread 不会停止,可能是 cout(或者可能是从缓冲区构造的 string)导致停止。检查你的 fread 的返回值,你就会明白我的意思。 - Adam Batkin
Adam,这是我表达对你帮助的感谢。我正在经历程序员综合症并且想摧毁Esc键;)你的建议很准确。再次感谢;) - André Moreira

0

|path| 中提供的许多文件大小不正确(proc 文件等)。因此,该文件是按顺序读取而不是一次性读取。

std::string cmdline;
char buf[1024];
size_t len;
FILE *fp = fopen("/proc/self/cmdline", "rb");
if (fp) {                       
    while ((len = fread(buf, 1, sizeof(buf), fp)) > 0) {
        cmdline.append(buf, len); // note: `len` here is very important
    }
}

然后,你可以获得整个命令行和参数。但是当你尝试打印它时,你只能获得应用程序名称或文件路径而没有参数的原因是你获得的命令行是用'\0'分隔的,类似于"./exefile_name\0-param1=p1\0-param2=p2"。因此,你只能获得第一个'\0'之前的部分,因为机器认为字符串在那个位置结束。你还需要首先使用'\0'将其标记化,然后再尝试打印或使用它。

std::stringstream tokens(cmdline);
std::string tmp;
while (getline(tokens, tmp, '\0')){
    std::cout << tmp << std::endl;
}
fclose(fp);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接