Я думаю, что ваш код можно улучшить, немного разбив вещи; все процедуры учета и копирования - в основном одна и та же задача, и если вы решите продолжить работу с несколькими процессами, можно написать просто:
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);
}
Вместо одного символа за раз, который неэффективен для умеренных объемов данных, мы можем обрабатывать частичные записи с помощью функции writen()
из Advanced Programming в источнике Unix Environment код:
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]
read-end, 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)
, чтобы определить, какой
файловые дескрипторы требуют чтения и записи.