在C 语言中使用read() 解析 /proc/meminfo会得到不正确的内存信息
我正在尝试用 open() 和 read() 从 /proc/meminfo 提取 MemTotal 与 MemFree(不使用 fgets() 或 scanf())。
然而,这些数值有时不正确或异常大。
总内存(KB): 1575626486920
可用内存(KB): 6622208
已使用百分比:99%
总内存显然是错误的。
我期望:
正确的数值应为:
MemTotal: 16384256 kB
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
int main(int argc, char const *argv[])
{
int fd = 0;
int iRet = 0;
char Buffer[1024];
memset(Buffer, '\0', sizeof(Buffer));
char *total = "MemTotal:";
char *free = "MemFree:";
char *start1 = total;
char *start2 = free;
fd = open("./meminfo", O_RDONLY);
if(fd == -1)
{
return -1;
}
int flag = 0;
unsigned long totalbytes = 0;
long freebytes = 0;
while((iRet = read(fd, Buffer, 1024)) > 0)
{
for(int i = 0; i < iRet; i++)
{
if(flag == 0)
{
if(*start1 == Buffer[i])
{
start1++;
if(*start1 == '\0')
{
flag = 1;
}
}
else
{
start1 = total;
}
}
else if(flag == 1)
{
if(Buffer[i] == ' ')
{
continue;
}
if(Buffer[i] >= '0' && Buffer[i] <= '9')
{
totalbytes = (totalbytes * 10) + (Buffer[i] - '0');
}
if(Buffer[i] == 'k')
{
break;
}
}
}
flag = 0;
for(int i = 0; i < iRet; i++)
{
if(flag == 0)
{
if(*start2 == Buffer[i])
{
start2++;
if(*start2 == '\0')
{
flag = 1;
}
}
else
{
start2 = free;
}
}
else if(flag == 1)
{
if(Buffer[i] == ' ')
{
continue;
}
if(Buffer[i] >= '0' && Buffer[i] <= '9')
{
freebytes = (freebytes * 10) + (Buffer[i] - '0');
}
if(Buffer[i] == 'k')
{
break;
}
}
}
}
printf("Total Memory in KB : %ld\n", totalbytes);
printf("Free Memory in KB : %ld\n", freebytes);
double Percentage = ((totalbytes - freebytes) * 100.0) / totalbytes;
printf("Percentage Used : %.2f%%\n", Percentage);
return 0;
}
解决方案
这是来自我个人库的代码。它可能为你的实现提供一些思路。
一个特别之处是根据关键字长度触发的开关。这种优化旨在防止对 memcmp() 调用过多。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef unsigned long long u64;
typedef const char * ccp;
typedef struct MemoryStatus_t
{
// man 5 proc
u64 total; // Total usable RAM.
u64 free; // The sum of LowFree + HighFree.
u64 avail; // An estimate of available memory for starting new applications.
u64 buffers; // Relatively temporary storage.
u64 cached; // In-memory cache for files read from the disk.
u64 used; // = total - free - buffers - cached;
}
MemoryStatus_t;
MemoryStatus_t GetMemoryStatus(void)
{
MemoryStatus_t mem = {0};
FILE *f = fopen("/proc/meminfo","r");
if (f)
{
char buf[200];
int count = 5; // abort, if all 5 params scanned
while ( count > 0 && fgets(buf,sizeof(buf),f) )
{
ccp colon = strchr(buf,':');
if (colon)
{
u64 num = strtoul(colon+1,0,10) * 1024;
switch (colon-buf)
{
case 6:
if (!memcmp(buf,"Cached",6)) { count--; mem.cached = num; break; }
break;
case 7:
if (!memcmp(buf,"MemFree",7)) { count--; mem.free = num; break; }
if (!memcmp(buf,"Buffers",7)) { count--; mem.buffers = num; break; }
break;
case 8:
if (!memcmp(buf,"MemTotal",8)) { count--; mem.total = num; break; }
break;
case 12:
if (!memcmp(buf,"MemAvailable",12)) { count--; mem.avail = num; break; }
break;
}
}
}
fclose(f);
mem.used = mem.total - mem.free - mem.buffers - mem.cached;
}
return mem;
}
int main(void)
{
MemoryStatus_t ms = GetMemoryStatus();
printf("tot=%llu, free=%llu, avail=%llu, used=%llu, buf=%llu, cache=%llu\n",
ms.total, ms.free, ms.avail, ms.used, ms.buffers, ms.cached );
return 0;
}
备选方案
关于你的方法和架构有足够多的注释。因此本回答仅点出实际的错误。
简化到相关结构,你的程序看起来是这样的:
int main(int argc, char const *argv[]) {
/* ... */
while ((iRet = read(fd, Buffer, 1024)) > 0) {
for (int i = 0; i < iRet; i++) {
if (flag == 0) {
/* check for "MemTotal:", on success flag = 1;*/
}
else if (flag == 1) {
/* ignore blanks, append digits to totalbytes, break on 'k' */
}
}
flag = 0;
for (int i = 0; i < iRet; i++) {
if (flag == 0) {
/* check for "MemFree:", on success flag = 1;*/
}
else if (flag == 1) {
/* ignore blanks, append digits to freebytes, break on 'k' */
}
}
}
/* ... */
}
以下是发生的情况:
- 读取了 "/proc/meminfo" 的第一块1024字节,其中包含了 "MemTotal:" 与 "MemFree:"(至少在我的系统上如此)。
- 第一个
for循环愉快地收集了 "MemTotal:" 的数字,并在遇到 'k' 时中断。 flag被重置为0。- 第二个
for循环愉快地收集了 "MemFree:" 的数字,并在遇到 'k' 时中断。 - 读取下一块。恰如所发生的,第二块以空格开头,随后是数字和一个 'k'。
- 由于
flag在第二个for循环后仍然为1,第一個for循环继续收集数字。 - 已经没有更多的块可读,因为 "/proc/meminfo" 的大小小于2048字节(至少在我的系统上是这样)。
你可以通过在第二个 for 循环后将 flag 重置为0 来纠正你的程序:
int main(int argc, char const *argv[]) {
/* ... */
while ((iRet = read(fd, Buffer, 1024)) > 0) {
for (int i = 0; i < iRet; i++) {
if (flag == 0) {
/* check for "MemTotal:", on success flag = 1;*/
}
else if (flag == 1) {
/* ignore blanks, append digits to totalbytes, break on 'k' */
}
}
flag = 0;
for (int i = 0; i < iRet; i++) {
if (flag == 0) {
/* check for "MemFree:", on success flag = 1;*/
}
else if (flag == 1) {
/* ignore blanks, append digits to freebytes, break on 'k' */
}
}
flag = 0; /* <<< HERE */
}
/* ... */
}
你怎么自己就能发现这个问题?
有位程序员哥已经给你提供了链接到 "How to debug small programs" 和Stack Overflow的问题及其答案 "What is a debugger and how can it help me diagnose problems?",它们都值得一读。
你可以在“有意思”的位置放置 printf() 语句。这些只是一些建议:
int main(int argc, char const *argv[]) {
/* ... */
while ((iRet = read(fd, Buffer, 1024)) > 0) {
printf("%d %d \"%s\"\n", iRet, flag, Buffer);
for (int i = 0; i < iRet; i++) {
if (flag == 0) {
/* check for "MemTotal:", on success flag = 1;*/
}
else if (flag == 1) {
/* ignore blanks, append digits to totalbytes, break on 'k' */
if (Buffer[i] == 'k') {
printf("T %lu\n", totalbytes);
break;
}
}
}
flag = 0;
for (int i = 0; i < iRet; i++) {
if (flag == 0) {
/* check for "MemFree:", on success flag = 1;*/
}
else if (flag == 1) {
/* ignore blanks, append digits to freebytes, break on 'k' */
if (Buffer[i] == 'k') {
printf("F %lu\n", freebytes);
break;
}
}
}
}
/* ... */
}
输出会为你带来启发。
关于你的方法还有一些缺陷:
- 如果包含 "MemTotal:" 或 "MemFree:" 的行在块边界处断开,你的程序就无法正确收集值。
- 如果尾部的 'k' 未被发现,因为
for循环超出块边界运行,你正在读取Buffer之后的内存。
当然还有更多的问题。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。