【问题标题】:How can I repeatedly read the same line of a FILE in c?如何在 c 中重复读取 FILE 的同一行?
【发布时间】:2014-10-15 15:52:01
【问题描述】:

我想监视一个不断更改特定行的文件。具体来说,我正在监控 /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);

但这不是更新。如何在打开文件时继续打印文件的行?我每次都需要关闭文件还是有更快的方法?

【问题讨论】:

  • 您可以尝试获取文件的“最后修改”详细信息并继续检查它是否有修改。试试这个roseindia.net/c-tutorials/c-file-last.shtml
  • 人们使用fseek()fp 指针向后设置,但是fseek() 只能应用于文件 而不是管道

标签: c file popen


【解决方案1】:

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

因此,实际上,您需要重新打开该/proc/19596/smaps 文件。阅读速度非常快,而且不涉及任何磁盘 I/O!它与阅读pipe(7) 的速度差不多。

您使用popen 是错误的(使用cat 是无用的)。您最好打开(使用fopen(3)/proc/19596/smaps 文件,循环读取每一行(例如使用fgets(3)),将其(使用例如strstr(3))与"stack" 文字字符串进行比较,然后读取下一行,最后是fclose它紧随其后。

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

【讨论】:

    【解决方案2】:

    老实说,我不明白你为什么要在 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 伪文件系统支持它。

    【讨论】:

      【解决方案3】:

      不确定这是否最适合/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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-19
        • 2016-10-17
        • 1970-01-01
        • 1970-01-01
        • 2011-04-28
        • 2015-11-23
        • 2023-03-09
        • 2017-11-03
        相关资源
        最近更新 更多