在C 语言中使用read() 解析 /proc/meminfo会得到不正确的内存信息

编程语言 2026-07-12

我正在尝试用 open()read()/proc/meminfo 提取 MemTotalMemFree(不使用 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' */
            }
        } 
    }   

    /* ... */
}

以下是发生的情况:

  1. 读取了 "/proc/meminfo" 的第一块1024字节,其中包含了 "MemTotal:" 与 "MemFree:"(至少在我的系统上如此)。
  2. 第一个 for 循环愉快地收集了 "MemTotal:" 的数字,并在遇到 'k' 时中断。
  3. flag 被重置为0。
  4. 第二个 for 循环愉快地收集了 "MemFree:" 的数字,并在遇到 'k' 时中断。
  5. 读取下一块。恰如所发生的,第二块以空格开头,随后是数字和一个 'k'。
  6. 由于 flag 在第二个 for 循环后仍然为1,第一個 for 循环继续收集数字。
  7. 已经没有更多的块可读,因为 "/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导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章