如何在c中重复读取同一行文件?

时间:2014-10-15 15:52:01

标签: c file popen

我想监视一个不断更改特定行的文件。具体来说,我正在监视/ proc / [PID] / smaps中的进程内存使用情况。

现在,我检查了smaps:

fp = popen("/bin/cat /proc/19596/smaps | grep stack --after-context=1", "r");
 if (fp == NULL) {
   printf("Failed to run command\n" );
   exit;
 }

 /* Read the output a line at a time - output it. */
 while(1){
    while (fgets(path, sizeof(path)-1, fp) != NULL) {
      printf("%s", path);
    }
 }
 /* close */
 pclose(fp);

但这不是更新。如何在文件打开时保持打印出来的文件?我是否需要每次都关闭文件或更快的方式?

3 个答案:

答案 0 :(得分:1)

/proc/是由伪文件组成的Linux特定文件系统,您应该按顺序访问它们。见proc(5)

因此在实践中,您需要重新打开/proc/19596/smaps文件。阅读它非常快,不涉及任何磁盘I / O!它与从pipe(7)读取的速度一样快。

使用popen(无用地使用cat)是错误的。您应该更好地打开(使用fopen(3)/proc/19596/smaps文件,循环读取每一行(例如使用fgets(3)),比较它(使用例如strstr(3))和{{1文字字符串,然后读取下一行,最后是"stack"

顺便说一句,如果您的进程19596碰巧有多个线程,我不确定您是否正在测量有意义的堆栈大小。

答案 1 :(得分:0)

老实说,我不明白你为什么要在C中这样做。

#define _XOPEN_SOURCE 500

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>

#include <getopt.h>
#include <unistd.h>

enum { ERR = -1 };

static int s_running = 1;

static void sigintHandler(int s) {
    (void)s;
    s_running = 0;
}

int main(int argc, char *argv[]) {
    int pid = -1, sleepMsec = 1000;
    char optchr, buf[260];
    FILE *fp;

    signal(SIGINT, sigintHandler);

    while (ERR != (optchr = getopt(argc, argv, "p:s:"))) {
        switch (optchr) {
            case 'p': pid = strtol(optarg, NULL, 0); break;
            case 's': sleepMsec = strtol(optarg, NULL, 0); break;
            default:
                fprintf(stderr, "USAGE: smap -p pid [-s msec]\n");
                return -10;
        }
    }

    snprintf(buf, sizeof buf, "/proc/%d/smaps", pid);
    buf[sizeof buf - 1] = '\0';
    fp = fopen(buf, "r");
    if (NULL == fp) {
        char buf1[sizeof buf];
        snprintf(buf1, sizeof buf1, "fopen for [%s]", buf);
        buf1[sizeof buf1 - 1] = '\0';
        perror(buf1);
        return -20;
    }

    for (;;) {
        if (ERR == fseek(fp, 0, SEEK_SET)) {
            perror("fseek(3)");
            return -30;
        }
        while (fgets(buf, sizeof buf, fp)) {
            if (strstr(buf, "stack")) {
                printf("%s", buf);
                if ( ! fgets(buf, sizeof buf, fp)) {
                    fprintf(stderr, "fgets(3) found EOF\n");
                    return -40;
                }
                printf("%s", buf);
            }
        }
        usleep(1000 * sleepMsec);
        if ( ! s_running)
            break;
    }

    printf("Bye!\n");
    return 0;
}

我建议您使用Python等脚本语言来完成此类任务。

#! /usr/bin/env python

from __future__ import print_function

import time
import sys
import getopt


def main(args):
    pid = -1
    sleepMsec = 1000
    opts, _ = getopt.getopt(args, "p:s:")
    for (k,a) in opts:
        if "-p" == k:
            pid = int(a)
        elif "-s" == k:
            sleepMsec = int(a)

    try:
        with open("/proc/{}/smaps".format(pid)) as fp:
            while True:
                fp.seek(0)
                while True:
                    line = fp.readline()
                    if not line:
                        break
                    if 0 <= line.find("stack0"):
                        print(line, fp.readline(), sep='', end='')
                        break
                time.sleep(1e-3 * sleepMsec)
    except KeyboardInterrupt:
        print("Bye!")

if "__main__" == __name__:
    main(sys.argv[1: ])

Bash和标准Linux命令的组合也是一种选择。事实上,您已经使用grep作为初始版本。

请注意,您不能对管道使用fseek(3)(严格意义上说它是一个流),但/proc/PID/smaps伪文件系统支持它。

答案 2 :(得分:0)

不确定这是否与/proc中的文件最佳匹配,但您可以使用Linux的inotify系统让内核通知您已观看文件的更改,以便您不必轮询。 您可以使用C访问系统,但是使用inotifywait工具在shell中尝试它应该是一个很好的起点(以下应该比您的问题中的C代码更有效)。

pid=19596
file=/proc/$pid/smaps
cmd="grep stack --after-context=1"
while intofitywait -e modify "$file"; do $cmd "$file"; done