在标准输入,标准输出和标准错误中的监视器字节C
我需要计算通过标准输入发送到子进程的字节数,以及子进程写入标准输出和标准错误的字节数。子进程调用execvp,所以我无法从进程内部监视这些统计信息。我目前的策略是创建3个额外的子进程,每个子进程通过管道监视每个std流(或者在stdin的情况下,从stdin读取)。在标准输入,标准输出和标准错误中的监视器字节C
这种策略看起来真的很虚弱,我做了一些奇怪的事情,使得监视stdout/err的进程无法从管道各自的末端读取(并使它们无限期挂起)。下面的代码。
这将创建三个辅助子进程,并应让他们算统计:
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]);
}
}
,这是一个代码片段(子进程中),其从FDS结构设置两个FD的以便子进程应该写入管道而不是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);
}
而不是一个字符-AT-A-时间,这是低效的温和的数据,我们可以处理从Advanced Programming in the Unix Environment源代码与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()
电话是很丑陋的,但试图 写身边所有的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)
来确定哪些文件描述符需要读取和写入。
谢谢。我的主要错误是我没有正确关闭所有文件描述符(导致子进程挂起),但是您帮助我组织并更好地理解了我的代码。 – bejar37 2011-05-27 14:44:09
总的来说,我认为你是在正确的轨道上。
一个问题是,在stderr和stdout处理程序中,应该从管道中选取字节并写入真实的stderr/stdout,然后写回同一个管道。
这也将有助于看到你如何开始子进程。你给了一个代码片段来关闭真正的stderr,然后将管道fd重复回到stderr的fd,但是你可能想要在父进程中(在fork和before exec之后)这样做,这样你就不需要修改源代码孩子的过程。你应该能够一般地从父母那里做到这一点。
嗯,我应该更清楚。我遇到的问题是,在处理程序中,无限期地读取块的调用,我不知道为什么。 我不应该这样做,因为我只想监视子进程写入stdout/err的内容,而不是父进程。 – bejar37 2011-05-26 21:44:44
1.你可以在一个进程中提供所有三个管道(根据你希望结果如何传递,它甚至可以是一个shell脚本)2.如果进程挂起,这可能在工作中缓冲;确保管道没有缓冲(至少对于输入管道) – sehe 2011-05-26 21:38:38