简体   繁体   English

如何在c中重复读取FILE的同一行?

[英]How can I repeatedly read the same line of a FILE in c?

I want to monitor a file that keeps changing a specific line. 我想监视一个不断更改特定行的文件。 Specifically, I am monitoring a processes memory usage in /proc/[PID]/smaps. 具体来说,我在/ proc / [PID] / smaps中监视进程的内存使用情况。

Right now, I check smaps with: 现在,我使用以下方法检查smap:

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);

but this is not updating. 但这没有更新。 How can I keep printing out the lines of the file as they open? 打开文件时,如何继续打印出文件中的行? Do I need to close the file each time or is there a faster way? 我是否需要每次关闭文件,还是有更快的方法?

/proc/ is a Linux specific filesystem made of pseudo-files, which you should access sequentially. /proc/是由伪文件组成的Linux特定文件系统,您应按顺序访问。 See proc(5) . 参见proc(5)

So in practice, you need to reopen that /proc/19596/smaps file. 因此,在实践中,您需要重新打开/proc/19596/smaps文件。 Read it is very quick and does not involve any disk I/O! 读取速度非常快,并且不涉及任何磁盘I / O! It is about as fast as reading from a pipe(7) . 它大约和从pipe(7)读取数据一样快。

You are wrong in using popen (with a useless use of cat ). 您使用popen是错误的(不带cat用法)。 You should better open (using fopen(3) ) the /proc/19596/smaps file, loop on reading every line (eg using fgets(3) ), comparing it (using eg strstr(3) ) with "stack" literal string, and reading the next line, and finally fclose it immediately after. 您最好打开(使用fopen(3)/proc/19596/smaps文件,循环读取每一行(例如,使用fgets(3) ),并将其与"stack"文字字符串进行比较(例如,使用strstr(3) ) ,然后阅读下一行,最后立即将其fclose

BTW, if your process 19596 happens to have several threads, I am not sure that you are measuring a meaningful stack size. 顺便说一句,如果您的进程19596恰好有多个线程,则我不确定您正在测量有意义的堆栈大小。

Honestly speaking I don't see why you want to do this in C. 老实说,我不明白为什么要在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;
}

I recommend you to use scripting languages such as Python for this kind of task. 我建议您将脚本语言(例如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: ])

Combination of Bash and standard Linux commands is an option, too. 也可以选择将Bash与标准Linux命令结合使用。 Indeed you already used grep for your initial version. 确实,您已经使用grep作为初始版本。

Note that you cannot use fseek(3) against a pipe (it's a stream in a strict sense) but the /proc/PID/smaps pseudo-filesystem supports it. 请注意,您不能对管道使用fseek(3) (严格意义上讲,它是一个流),但是/proc/PID/smaps伪文件系统支持它。

Not sure if this works optimally with the files in /proc but you can use Linux's inotify system to have the kernel notify you of changes on watched files so that you don't have to poll. 不知道这是否可以与/proc的文件配合使用,但是您可以使用Linux的inotify系统让内核将监视文件的更改通知您,从而使您不必轮询。 You can access the system in C, but trying it out in the shell by using the inotifywait tool should be a good starting point (the following should be way more efficient than the C code in your question). 您可以使用C语言访问系统,但是使用inotifywait工具在shell中进行inotifywait应该是一个很好的起点(以下内容应比您问题中的C代码更有效)。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM