【问题标题】:C Unix Pipes ExampleC Unix 管道示例
【发布时间】:2011-06-17 23:42:30
【问题描述】:

试图实现一个外壳,主要是管道。我已经编写了这个测试用例,我希望它可以简单地将 ls 传输到 wc ......它绝对不能按预期工作。它将 ls 打印到终端,然后打印内存耗尽。 我非常迷茫如何解决这个问题并让它工作。 find_path 在我的所有测试中都有效。

编辑 - 我必须对项目使用 execv,它是一个类的东西,但我已经尝试使用 execvp 以防万一,它做的事情完全相同。这也只是一个例子,一个测试它为什么不起作用的测试,我为两个命令和 waitpid 调用了两次 fork 一次,因为我没有别的事可做。

#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
int find_path(char* execname, char** dst)
{        
        char *path = getenv("PATH");
        path = strdup(path);
        char *pos;
        path = strtok_r(path, ":", &pos);
        char *originalpath  = path;
        do
        {
                char* test = (char*)calloc(strlen(path) + strlen(execname) + 2, sizeof(char));
                test = strcpy(test, path);
                int testlen = strlen(test);
                (*(test+testlen)) = '/';
                strcpy(test + testlen + 1,execname);
                struct stat buf;
                int result = stat(test, &buf);
                if (result == 0)
                {
                        *dst = test;
                        free (originalpath);
                        return 1;
                }
                else
                {
                        free(test);
                }

        } while ((path = strtok_r(NULL, ":", &pos)) != NULL);
        free(originalpath);
        return 0;
}

int main()
{
    char *cmd1 = "ls";
    char *cmd2 = "wc";
    int filedes[2];
    pipe(filedes);
    char** argv = (char**)calloc(1, sizeof(char*)); 
    argv[0] = (char*)malloc(sizeof(char*));
    argv[0] = NULL;

    pid_t pid = fork();
    if (pid == 0)
    {
        char *path;
                find_path(cmd1, &path);
        dup2(filedes[1],stdout);

        execv(path,argv); 
    }
    pid = fork();
    if (pid == 0)
    {
        dup2(filedes[0], stdin);
        char *path;
        find_path(cmd2, &path);
        execv(path, argv);

    }
    else
        waitpid(pid);

}

【问题讨论】:

  • 您是否看过去年“如何使用 ... fork ... pipe ... exec ... 实现 shell”问题?答案可能在其中一个(更可能是多个)中。

标签: c unix fork pipe


【解决方案1】:

通常当很难调试程序时,最好稍微简化一下以消除错误源。这是您的程序,简化后将 find_path 作为错误源删除:

#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>

int main(void)
{
    int filedes[2];
    pipe(filedes);

    /* Run LS. */
    pid_t pid = fork();
    if (pid == 0) {
        /* Set stdout to the input side of the pipe, and run 'ls'. */
        dup2(filedes[1], 1);
        char *argv[] = {"ls", NULL};
        execv("/bin/ls", argv);
    } else {
        /* Close the input side of the pipe, to prevent it staying open. */
        close(filedes[1]);
    }

    /* Run WC. */
    pid = fork();
    if (pid == 0) {
        dup2(filedes[0], 0);
        char *argv[] = {"wc", NULL};
        execv("/usr/bin/wc", argv);
    }

    /* Wait for WC to finish. */
    waitpid(pid);
}

这应该符合您的预期。

在简化过程中,出现了一些错误:

  • argv[] 设置不正确,特别是 argv[0] 被设置为 NULL;
  • 程序未关闭分配给ls 的管道的输入端。当ls 完成时,管道并没有关闭(因为wc 进程仍然打开它),阻止wc 完成。
  • 程序混淆了值stdoutstdin(类型为FILE*)与文件描述符编号01(由duppipe 等使用。 )

【讨论】:

  • 太棒了,你犯了我所有的愚蠢错误。在我的测试中,我尝试了您建议的所有方法,除了我完全忘记了传入的第一个命令行参数应该是可执行文件名!非常感谢。
【解决方案2】:

您可以做很多事情来改进此代码(例如,将其分解为更小的函数将是一个开始),但我怀疑您的内存不足问题来自 find_path() 中的代码,您可以通过以下方式完全避免使用execvp,它将使用标准 PATH 机制为您定位可执行文件。使用sigaction 安装信号处理程序来处理 SIGCHLD 并从信号处理程序调用 waitpid 可能是一个好主意,而不是像你正在做的那样临时调用 waitpid()。你分叉的次数似乎比你想要的多,而且你没有检查错误。希望这些建议对您有所帮助。

【讨论】:

  • 我编辑了我的帖子来解释为什么我使用 find_path,但是 execvp 做同样的事情,等待可能不是最好的方法,但我只是在这里创建一个问题的例子,所以这不是真的与错误有关。
猜你喜欢
  • 1970-01-01
  • 2018-09-18
  • 2011-12-11
  • 1970-01-01
  • 1970-01-01
  • 2010-12-14
  • 2011-04-25
  • 1970-01-01
相关资源
最近更新 更多