Chang的多管道

发布于 2024-11-30 13:50:27 字数 2517 浏览 2 评论 0原文

我正在尝试实现将运行多个 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 时,它​​也卡在 read 上...... 管道缓冲区是 64K,我一次只为每个管道写入 4k,

这是怎么回事?

谢谢!!!

I am trying to implement program that will run multiple chains of shell commands:

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

and so on...

#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);
 }

when running strace on the sub processes, it's stuck on read from pipe,
when running starce on the main process, it's also stuck on read...
The pipe buffer is 64K and I am writing only 4k at once for each pipe

WHAT'S GOING ON?!

THANKS!!!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

疯狂的代价 2024-12-07 13:50:27

我可以看到您的代码至少有两个问题:

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

您需要为 0 终止符分配比 cmd 长度多一个的值。由于 sizeof(char) 根据定义为 1,我会将上面的内容写为:

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

另外,对于:

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

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

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

其中 n 是参数的数量。

I can see at least two problems with your code:

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

You need to allocate one more than the length of cmd, for the 0 terminator. Since sizeof(char) is 1 by definition, I would write the above as:

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

Also, for:

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

You need to allocate space for as many arguments as needed:

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

where n is the number of arguments.

仄言 2024-12-07 13:50:27

您没有为程序参数分配足够的内存。在 parsecmd 中,您仅为 char **args = malloc(sizeof(char*)) 中的单个指针分配空间,并且随后存储了超过其中一个指针而不重新分配它,导致缓冲区溢出。同样,您在 char* buf = malloc(sizeof(char) * strlen(cmd)) 中分配的字节数比应有的字节数少了 1 个字节,您需要在其中添加 1,以便有空间字符串以 NUL 结尾。此外,C 标准保证 sizeof(char) 为 1,因此无需将其放入对 malloc 的调用中。

您的代码的其他问题:

  • 您正在泄漏内存。对 malloc 的所有调用都需要对 free 进行相应的调用,以避免内存泄漏。
  • 添加更多关于代码正在执行的操作的注释
  • strtok 在多线程代码中使用不安全,因为它使用共享全局状态。如果此代码需要成为线程安全的,请考虑将其替换为 strtok_r(3)< /code>(如果可用)或其他替代品。
  • 如果 forkexecvppipe 失败,则说明您无法处理错误。

修复这些问题,看看是否能解决您的挂起问题。

You're not allocating enough memory for your program arguments. In parsecmd, you're only allocating space for a single pointer in char **args = malloc(sizeof(char*)), and you're subsequently storing more than one pointer in it without reallocating it, resulting in a buffer overrun. Similarly, you're allocating one byte less than you should in char* buf = malloc(sizeof(char) * strlen(cmd))—you need to add one to that in order to have space for the string's terminating NUL. Also, sizeof(char) is guaranteed to be 1 by the C standard, so there's no need to put that in the call to malloc.

Other problems with your code:

  • You're leaking memory. All of your calls to malloc need a corresponding call to free to avoid leaking memory.
  • Add more comments about what your code is doing
  • strtok is not safe to use in multithreaded code, since it uses shared global state. If this code every needs to become thread-safe, consider replacing it with strtok_r(3) if available, or some other replacement.
  • You're failing to handle errors, if fork, execvp, or pipe fails

Fix these problems and see if that solves your hang.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文