简体   繁体   English

C,dup2 和 exec() pipe“|” 操作员

[英]C , dup2 and exec() pipe "|" operator

I have a problem in execve-pipe process.我在 execve-pipe 过程中遇到问题。 I split a command with pipeline and sent to function one by one.我用管道拆分了一个命令,一个一个发给function。 I made fork, dup2 and close functions.我制作了 fork、dup2 和 close 函数。 Whole command executed but output of last command sending to terminal and readline function. For this reason readline runs the last output. For example I sent ls | wc执行了整个命令,但发送到终端的最后一个命令的 output 和 readline function。因此,readline 运行最后一个 output。例如,我发送了ls | wc ls | wc , output: 8. error: 8 is not a command. segfault ls | wc , output: 8. error: 8 is not a command. segfault 8. error: 8 is not a command. segfault . 8. error: 8 is not a command. segfault

while (++i < nproc)
{
    rdl->main_str = ft_strdup(rdl->pipe_str[i]);
    rdl->len = ft_strlen(rdl->pipe_str[i]);
    parser(rdl);
    command(rdl); // => run a pipe_exec function
    token_clear(&rdl->token);
    free(rdl->main_str);
    printf("*****************\n");
}
while (nproc-- > 0)
    waitpid(-1, 0, 0);
#define READ 0
#define WRITE 1

static void ft_fatality(void)
{
    ft_putstr_fd("error: fatal\n", 2);
    exit(1);
}

// static void  ft_exec_error(char *str)
// {
//  ft_putstr_fd("error: cannot execute ", 2);
//  ft_putstr_fd(str, 2);
//  ft_putstr_fd("\n", 2);
//  exit(1);
// }

static void ft_openpipes(int fd[2])
{
    if (close(fd[READ]) == -1)
        ft_fatality();
    if (dup2(fd[WRITE], STDOUT_FILENO) == -1)
        ft_fatality();
    if (close(fd[WRITE]) == -1)
        ft_fatality();
}

static void ft_closepipes(int fd[2])
{
    if (dup2(fd[READ], STDIN_FILENO) == -1)
        ft_fatality();
    if (close(fd[READ]) == -1)
        ft_fatality();
    if (close(fd[WRITE]) == -1)
        ft_fatality();
}

int pipe_exec(t_command command)
{
    printf("pipe_exec\n");
    printf("pipe_exec command count %d\n", command.count);
    int i;
    int j;
    int fd[2];
    int type_size;
    int size;
    int result;
    char *arg;
    char *path;
    char **type;
    pid_t pid;

    i = -1;
    j = 1;
    result = 0;
    size = token_size(command.tokens);
    type_size = 0;
    arg = ft_strdup("");
    if (pipe(fd) == -1)
        ft_fatality();
    while (++i < size)
    {
        if (command.tokens->type_id == 12)
            type_size++;
        get_next_token(&command.tokens);
    }
    i = -1;
    path = command_find_path(command.keyword);
    type = (char **)malloc(sizeof(char *) * ((type_size + 1) + 2));
    type[0] = ft_strdup(path);
    while (++i < size)
    {
        if (command.tokens->type_id == 13 || command.tokens->type_id == 7)
        {
            arg = ft_strjoin(arg, command.tokens->context);
            printf("arg %s\n", arg);
        }
        if (command.tokens->type_id == 12 || size - 1 == command.tokens->id)
        {
            type[j++] = ft_strdup(arg);
            arg = ft_strdup("");
        }
        get_next_token(&command.tokens);
    }
    type[j] = NULL;
    j = -1;
    while (type[++j])
    {
        printf("type : %s\n", type[j]);
    }
    pid = fork();
    // signal(SIGINT, proc_signal_handler);
    if (pid < 0)
        return (-1);
    if (pid == 0)
    {
        ft_openpipes(fd);
        result = execve(path, type, g_env.env);
    }
    else
        ft_closepipes(fd);
    if (result == -1)
        return (1);
    waitpid(pid, 0, 0);
    command.fd[0] = fd[0];
    command.fd[1] = fd[1];
    free(arg);
    ft_free_dbl_str(type);
    free(path);
    return (0);
}

bash % ./minishell
->ls | wc -l
-------------------
ls      token->type->context keyword                    token->type->id 0               token->t_flag 0
|       token->type->context pipe                       token->type->id 6               token->t_flag 6
wc      token->type->context keyword                    token->type->id 0               token->t_flag 0
l       token->type->context arg                        token->type->id 13              token->t_flag -1
-------------------
-------------------
ls      token->type->context keyword                    token->type->id 0               token->t_flag 0
-------------------
pipe_exec
pipe_exec command count 1
type : /bin/ls
*****************
-------------------
wc      token->type->context keyword                    token->type->id 0               token->t_flag 0
-       token->type->context option                     token->type->id 7               token->t_flag 5
l       token->type->context arg                        token->type->id 13              token->t_flag -1
-------------------
pipe_exec
pipe_exec command count 2
arg -
arg -l
type : /usr/bin/wc
type : -l
*****************
->       8
-------------------
8       token->type->context string                     token->type->id 12              token->t_flag -1
        token->type->context string                     token->type->id 12              token->t_flag -1
-------------------
**bash: 8: command not found
->zsh: segmentation fault  ./minishell**

Your ft_closepipes() function dupes the read end of the pipe onto STDIN_FILENO .您的ft_closepipes() function 将 pipe 的读取端复制到STDIN_FILENO上。 You execute that in the parent process, which causes exactly the effect you describe.您在父进程中执行它,这会导致您描述的效果。 The parent's standard input is redirected to the read end of the (current) pipe.父级的标准输入被重定向到(当前)pipe 的读取端。

That happens to work out ok for the processes in the pipeline (but see below), because they each inherit their standard input from their parent, and you start them in order from left to right.这恰好适用于管道中的进程(但请参见下文),因为它们每个都从其父级继承其标准输入,并且您按从左到右的顺序启动它们。 But it leaves the shell itself consuming the output of the last process as its own input.但它让 shell 本身消耗最后一个进程的 output 作为自己的输入。

And that brings up the other point : your ft_openpipes() function redirects the caller's standard output to the specified pipe, but you don't want to do that for the last process in the pipeline.这带来了另一点:您的ft_openpipes() function 将调用者的标准 output 重定向到指定的 pipe,但您不想对管道中的最后一个进程执行此操作。 It was a bit fortuitous to combine that error with the other, however, because it made very clear what the nature of the problem is.然而,将这个错误与另一个错误结合起来有点偶然,因为它非常清楚问题的本质。

For the parent, one alternative would be to dupe the standard input FD before setting up the pipeline, to preserve it, then dupe it back afterward.对于父级,一种替代方法是在设置管道之前复制标准输入 FD,以保存它,然后再将其复制回来。 That would be a relatively easy retrofit, but I think it's poor form.那将是一个相对容易的 retrofit,但我认为它的形式很差。 Although you would need more of a rework to accomplish it, better would be to avoid ever redirecting the parent's file descriptors at all.尽管您需要更多的返工来完成它,但最好完全避免重定向父文件描述符。

As for the segfault , that's probably a result of the child process returning to the caller of pipe_exec() in the event that its execve() call returns.至于 segfault ,这可能是子进程在其execve()调用返回时返回给pipe_exec()的调用者的结果。 It ought instead to terminate, just as would (eventually) happen if it had successfully started the the requested program.它应该终止,就像它(最终)成功启动所请求的程序一样。 Personally, I would go with something like this:就个人而言,我会 go 这样的事情:

            // ...
            result = execve(path, type, g_env.env);
            assert(result == -1);
            perror(path);
            _exit(EXIT_FAILURE);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM