更好的方式从C中的exec'd程序的stdout读取

时间:2012-06-21 00:27:20

标签: c memory-management

我使用管道读取了一个exec'd程序的stdout:

int pipes[2];
pipe(pipes);
if (fork() == 0) {
    dup2(pipes[1], 1);
    close(pipes[1]);
    execlp("some_prog", "");
} else {
    char* buf = auto_read(pipes[0]);
}

要从stdout读取,我有一个函数auto_read,可以根据需要自动分配更多内存。

char* auto_read(int fp) {
    int bytes = 1000;
    char* buf = (char*)malloc(bytes+1);
    int bytes_read = read(fp, buf, bytes);
    int total_reads = 1;
    while (bytes_read != 0) {
        realloc(buf, total_reads * bytes + 1);
        bytes_read = read(fp, buf + total_reads * bytes, bytes);
        total_reads++;
    }
    buf[(total_reads - 1) * bytes + bytes_read] = 0;
    return buf;
}

我这样做的原因是我不知道该程序将提前发出多少文本,我不想创建一个过大的缓冲区并成为内存耗尽。我想知道是否有:

  1. 一种更清晰的写作方式。
  2. 更多内存或速度效率的方法。

1 个答案:

答案 0 :(得分:4)

如果您只需要阅读某个流程并且位于* NIX平台上,请使用popen

FILE *programStdout = popen("command", "r");

// read from programStdout (fread(), fgets(), etc.)
char buffer[1024];

while (fgets(buffer, 1024, programStdout))
{
    puts(buffer);
}

编辑:您要求一种方法将程序输出映射到文件,所以在这里:

#import <stdio.h>
#import <unistd.h>
#import <sys/mman.h>

void *dataWithContentsOfMappedProgram(const char *command,  size_t *len)
{
    // read the data
    char template[] = "/tmp/tmpfile_XXXXXX";
    int fd = mkstemp(template);

    FILE *output = fdopen(fd, "w+");
    FILE *input = popen(command, "r");

#define BUF_SIZ 1024
    char buffer[BUF_SIZ];
    size_t readSize = 0;
    while ((readSize = fread(buffer, 1, BUF_SIZ, input)))
    {
        fwrite(buffer, 1, readSize, output);
    }
    fclose(input);

    input = NULL;
#undef BUF_SIZ

    // now we map the file
    long fileLength = ftell(output);
    fseek(output, 0, SEEK_SET);

    void *data = mmap(NULL, fileLength, PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE, fd, 0);

    close(fd);

    if (data == MAP_FAILED)
        return NULL;

    return data;
}


int main()
{
    size_t fileLen = 0;
    char *mapped = dataWithContentsOfMappedProgram("echo Hello World!", &fileLen);

    puts(mapped);

    munmap(mapped, fileLen);
}