C挂多管

时间:2011-08-21 19:34:45

标签: c exec pipe hang dup2

我正在尝试实现运行多个shell命令链的程序:

        | --> cmd3 --> cmd4 -->
 cmd2-->|
        | --> cmd5 --> cmd6 -->|--> cmd7
                               |
                               |--> cmd8

依旧......

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <fcntl.h>
#include <stdarg.h>
#include <sys/types.h>

typedef struct command {
    char* name;
char** argv;
} command;

command parsecmd(char* cmd) {
    command c;

    char delimiter[] = " ";
    char* buf = malloc(sizeof(char) * strlen(cmd));
    strcpy(buf, cmd);
    char **args = malloc(sizeof(char*));

    char* token = strtok(buf, delimiter);

    int i = 0;
    while (token != NULL) {
      if (i == 0) {
             c.name = token;
          }

      args[i] = token;

      token = strtok(NULL, delimiter);
          ++i;
     }

     args[i] = NULL;

     c.argv = args;

     return c;
}

int mkproc(char *cmd, int outfd)
{
    command c = parsecmd(cmd);
    int pipeleft[2];
    pipe(pipeleft);
    if(!fork()){
        close(pipeleft[1]);
        dup2(pipeleft[0], 0);
        dup2(outfd, 1);
        execvp(c.name, c.argv);
    }
    close(pipeleft[0]);
    return pipeleft[1];
 }

int mktree(char *cmd, int ofd0, ...)
{
    int piperight[2];
    pipe(piperight);

    int cmdin = mkproc(cmd, piperight[1]);
    close(piperight[1]);
    if(!fork()){
        uchar buf[4096];
        int n;

        while((n=read(piperight[0], buf, sizeof buf))>0){
            va_list ap;
            int fd;
            va_start(ap, ofd0);
            for(fd=ofd0; fd!=-1; fd=va_arg(ap, int)){
                write(fd, buf, n);
            }
            va_end(ap);
        }
    }
    return cmdin;
 }

 int main(int argc, char* argv[]) {
       // THIS WORK
       int chain_in = mkproc("cat foo.txt", mkproc("sort", mkproc("wc -l", 1)));
       // THIS WORK
       int tree_in1 = mktree("cat /tmp/test.log", mkproc("grep a", 1), mkproc("wc -l", 2), -1);

       // NOT WORK -> HANG!
       int tree_in2 = mktree("cat /tmp/test.log", 
              mktree("grep test",
                  mkproc("uniq", mkproc("wc -l", 1)),
                  mkproc("wc -l", 2), -1),
              mkproc("sort", 2), -1);
 }

当在子进程上运行strace时,它被卡在管道读取上, 当在主进程上运行starce时,它也停留在读取... 管道缓冲区是64K,我每个管道只写4k

什么事情发生了?!

感谢!!!

2 个答案:

答案 0 :(得分:0)

我的代码至少会出现两个问题:

char* buf = malloc(sizeof(char) * strlen(cmd));

对于cmd终结符,您需要为0的长度分配一个以上。由于sizeof(char)的定义是1,我将上面写为:

char *buf = malloc(strlen(cmd)+1);

另外,对于:

char **args = malloc(sizeof(char*));

您需要根据需要为尽可能多的参数分配空间:

char **args = malloc(n * sizeof *args);

其中n是参数的数量。

答案 1 :(得分:0)

您没有为程序参数分配足够的内存。在parsecmd中,您只为char **args = malloc(sizeof(char*))中的单个指针分配空间,并且您随后在其中存储多个指针而不重新分配它,从而产生buffer overrun。类似地,你在char* buf = malloc(sizeof(char) * strlen(cmd))中分配比你应该少的一个字节 - 你需要添加一个字节,以便为字符串的终止NUL留出空间。此外,{C}标准保证sizeof(char)为1,因此无需将其置于malloc的调用中。

您的代码存在其他问题:

  • 你在泄露记忆。您对malloc的所有来电都需要对free进行相应的调用,以避免泄露内存。
  • 添加有关您的代码正在执行的操作的更多评论
  • strtok在多线程代码中使用是不安全的,因为它使用共享全局状态。如果此代码都需要成为线程安全的,请考虑将其替换为strtok_r(3)(如果可用)或其他替换。
  • 如果forkexecvppipe失败,您将无法处理错误

解决这些问题并查看是否能解决问题。