C Unix管道示例

时间:2011-01-30 04:21:41

标签: c unix fork pipe

尝试实现一个shell,主要是管道。我已经编写了这个测试用例,我希望将其简单地传递给wc ......它肯定不能按预期工作。它将ls打印到终端然后打印耗尽的内存。 我很失落如何解决这个问题并让它发挥作用。 find_path适用于我的所有测试。

编辑 - 我必须使用execv作为项目,它是一个类的东西,但我已经尝试使用execvp以防万一,它完全相同的事情。这只是一个例子,一个测试,看看为什么它不起作用,我为fork和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);

}

2 个答案:

答案 0 :(得分:8)

通常,当调试程序很困难时,最好将其简化一点以消除错误来源。以下是您的程序,简化为删除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等。)

答案 1 :(得分:1)

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