监视C中的stdin,stdout和stderr中的字节

我需要计算通过stdin发送给subprocess的字节数,以及subprocess写入stdout和stderr的字节数。 subprocess调用execvp,所以我无法从进程内部监视这些统计信息。 我目前的策略是创build3个额外的subprocess,每个subprocess通过pipe道监视每个stdstream(或者在stdin的情况下,从stdin读取)。

这种策略似乎真的很脆弱,我做了一些奇怪的事情,使得监视stdout / err的进程无法从它们各自的pipe道端读取(并使它们无限期地挂起)。 下面的代码。

这将创build三个帮助subprocess,并允许它们统计统计信息:

void controles(struct fds *des) { int ex[2]; int err[2]; int n_in = 0; int c_in; int n_ex = 0; int c_ex; int n_err = 0; int c_err; pipe(ex); pipe(err); /*has two fields, for the write end of the stdout pipe and the stderr pipe. */ des->err = err[1]; des->ex = ex[1]; switch (fork()) { case 0: /*stdin */ while (read(0, &c_in, 1) == 1) n_in++; if (n_in > 0) printf("%d bytes to stdin\n", n_in); exit(n_in); default: break; } switch (fork()) { case 0: /*stdout */ close(ex[1]); /*pretty sure this is wrong */ while (read(ex[0], &c_ex, 1) == 1) { n_ex++; write(1, &c_ex, 1); } if (n_ex > 0) printf("%d bytes to stdout\n", n_ex); close(ex[0]); exit(n_ex); default: close(ex[0]); } switch (fork()) { case 0: /*error */ close(err[1]); /*also probably have a problem here */ while (read(err[0], &c_err, 1) == 1) { n_err++; write(2, &c_err, 1); } if (n_err > 0) printf("%d bytes to stderr\n", n_err); close(err[0]); exit(n_err); default: close(err[0]); } } 

这是一个代码片段(在subprocess内),它从fds结构中设置两个fd,以便subprocess应该写入pipe道而不是stdin / stderr。

  dup2(des.ex, 1); dup2(des.err, 2); close(des.ex); close(des.err); /*Is this right?*/ execvp(opts->exec, opts->options); /*sure this is working fine*/ 

我迷路了,任何帮助将不胜感激。

我认为你的代码可以通过打破一些事情来改善; 会计和复印程序基本上是一样的任务,如果你选择继续多个过程的道路,可以简单地写:

 void handle_fd_pair(char *name, int in, int out) { char buf[1024]; int count = 0, n; char fn[PATH_MAX]; snprintf(fn, PATH_MAX - 1, "/tmp/%s_count", name); fn[PATH_MAX-1] = '\0'; FILE *output = fopen(fn, "w"); /* handle error */ while((n = read(in, buf, 1024)) > 0) { count+=n; writen(out, buf, n); /* see below */ } fprintf(output, "%s copied %d bytes\n", name, count); fclose(output); } 

我们可以使用Unix环境下的高级编程中的 writen()函数处理部分写入操作,而不是一次一个字符的操作。

 ssize_t /* Write "n" bytes to a descriptor */ writen(int fd, const void *ptr, size_t n) { size_t nleft; ssize_t nwritten; nleft = n; while (nleft > 0) { if ((nwritten = write(fd, ptr, nleft)) < 0) { if (nleft == n) return(-1); /* error, return -1 */ else break; /* error, return amount written so far */ } else if (nwritten == 0) { break; } nleft -= nwritten; ptr += nwritten; } return(n - nleft); /* return >= 0 */ } 

随着帮手的到位,我认为其余的可以更容易。 为每个流分配一个新的子节点,并将管道的in[0]读端, out[1]err[1]写入到子节点。

所有那些close()每个孩子的close()调用都非常难看,但是试图围绕所有fds的数组写一个小包装,并且免除那些作为参数传入的东西,也似乎是麻烦。

 #include <stdio.h> #include <unistd.h> #include <stdlib.h> #ifndef PATH_MAX #define PATH_MAX 128 #endif void handle_fd_pair(char *name, int in, int out) { char buf[1024]; int count = 0, n; char fn[PATH_MAX]; snprintf(fn, PATH_MAX - 1, "/tmp/%s_count", name); fn[PATH_MAX-1] = '\0'; FILE *output = fopen(fn, "w"); /* handle error */ while((n = read(in, buf, 1024)) > 0) { count+=n; writen(out, buf, n); /* see below */ } fprintf(output, "%s copied %d bytes\n", name, count); fclose(output); } int main(int argc, char* argv[]) { int in[2], out[2], err[2]; pid_t c1, c2, c3; pipe(in); pipe(out); pipe(err); if ((c1 = fork()) < 0) { perror("can't fork first child"); exit(1); } else if (c1 == 0) { close(in[0]); close(out[0]); close(out[1]); close(err[0]); close(err[1]); handle_fd_pair("stdin", 0, in[1]); exit(0); } if ((c2 = fork()) < 0) { perror("can't fork second child"); exit(1); } else if (c2 == 0) { close(in[0]); close(in[1]); close(out[1]); close(err[0]); close(err[1]); handle_fd_pair("stdout", out[0], 1); exit(0); } if ((c3 = fork()) < 0) { perror("can't fork third child"); exit(1); } else if (c3 == 0) { close(in[0]); close(in[1]); close(out[0]); close(out[1]); close(err[1]); handle_fd_pair("stderr", err[0], 2); exit(0); } /* parent falls through to here, no children */ close(in[1]); close(out[0]); close(err[0]); close(0); close(1); close(2); dup2(in[0], 0); dup2(out[1], 1); dup2(err[1], 2); system(argv[1]); exit(1); /* can't reach */ } 

它似乎工作的玩具应用无论如何:)

 $ ./dup cat hello hello $ ls -l *count -rw-r--r-- 1 sarnold sarnold 22 2011-05-26 17:41 stderr_count -rw-r--r-- 1 sarnold sarnold 21 2011-05-26 17:41 stdin_count -rw-r--r-- 1 sarnold sarnold 22 2011-05-26 17:41 stdout_count $ cat *count stderr copied 0 bytes stdin copied 6 bytes stdout copied 6 bytes 

我认为值得指出的是,你也可以一个进程来实现这个程序,并使用select(2)来确定哪些文件描述符需要读写。

总的来说,我认为你是在正确的轨道上。

一个问题是,在stderr和stdout处理程序中,应该从管道中选取字节并写入真实的stderr / stdout,然后写回同一个管道。

看看你是如何开始子进程也是有帮助的。 你给了一个代码片断来关闭真正的stderr,然后把管道fd重复回到stderr的fd,但是你可能想要在父进程中(在fork和exec之前)这样做,所以你不需要修改源代码孩子的过程。 你应该能够做到这一般所有从父母。