簡體   English   中英

試圖運行“ls | grep r“with”execvp()“

[英]trying to run “ls | grep r” with “execvp()”

  1. 我在兩個子進程之間創建了一個pipe ,首先,我運行ls ,寫入正確的fd,然后,我運行grep r ,它從正確的fd讀取,

  2. 我可以在終端看到grep命令工作正常(輸出)

  3. 問題是grep不會退出,它會停留在那里,即使ls不再運行了

對於其他程序, pipe工作正常..

for (i = 0; i < commands_num ; i++) {   //exec all the commands instants
    if (pcommands[i]._flag_pipe_out == 1) { //creates pipe if necessary 
        if (pipe(pipe_fd) == -1) {
            perror("Error: \"pipe()\" failed");
        }
        pcommands[i]._fd_out = pipe_fd[1];
        pcommands[i+1]._fd_in = pipe_fd[0]; 
    }
    pid = fork();   //the child exec the commands  
    if (pid == -1) {
        perror("Error: \"fork()\" failed");
        break;          
    } else if (!pid) { //child process

        if (pcommands[i]._flag_pipe_in == 1) {  //if there was a pipe to this command
            if (dup2(pcommands[i]._fd_in, STDIN) == -1) {
                perror("Error: \"dup2()\" failed");
                exit(0);
            }
            close(pcommands[i]._fd_in);
        }

        if (pcommands[i]._flag_pipe_out == 1) { //if there was a pipe from this command
            if (dup2(pcommands[i]._fd_out, STDOUT) == -1) {
                perror("Error: \"dup2()\" failed");
                exit(0);
            }
            close(pcommands[i]._fd_out);
        } 
        execvp(pcommands[i]._commands[0] , pcommands[i]._commands); //run the command

        perror("Error: \"execvp()\" failed");
        exit(0);
    } else if (pid > 0) { //father process
    waitpid(pid, NULL, WUNTRACED);
    }
}
//closing all the open fd's
for (i = 0; i < commands_num ; i++) {
    if (pcommands[i]._fd_in != STDIN) { //if there was an other stdin that is not 0
        close(pcommands[i]._fd_in);
    }           
    if (pcommands[i]._fd_out != STDOUT) { //if there was an other stdout that is not 1
        close(pcommands[i]._fd_out);            
    }   
}

所以,我有一個“命令”即時pcommands[i]它有:pipein,pipeout fdin,fdout和char **的標志(對於真正的命令,如“ls -l”)

讓我們說一切都很好,這意味着:

pcommands[0]:
pipein=0
pipeout=1
char** = {"ls","-l",NULL}

pcommands[1]:
pipein=1
pipeout=0
char** = {"grep","r",NULL}

現在,循環將在第一次進行兩次(因為我有兩個命令時刻),它將看到pcommands [0]有pipeout == 1創建管道do fork pcommands [0]有pipeout == 1 child:dup2 to stdout execvp

第二次:不創建管道做fork子:pcomands [1]有pipein == 1然后:dup2到輸入exevp ..

這個命令有效,我的輸出是:

errors.log exer2.pdf multipal_try

(所有帶有'r'的東西)然后它會卡住,並且不會脫離grep ..在另一個終端我可以看到grep仍在工作

我希望我關閉所有需要關閉的fd ...

我不明白為什么它不起作用,好像我做得對(好吧,它適用於其他命令..)

有人可以幫忙嗎? 謝謝

您沒有關閉足夠的管道文件描述符。

經驗法則:

  • 如果使用dup()dup2()將管道文件描述符復制到標准輸入或標准輸出,則應關閉兩個原始管道文件描述符。

您還需要確保如果父shell創建管道,它將關閉管道文件描述符的兩個副本。

另請注意,應允許管道中的進程並發運行。 特別是,管道的容量有限,並且當管道中沒有空間時,過程會阻塞。 限制可以非常小(POSIX要求它必須至少為4 KiB,但這就是全部)。 如果您的程序處理數兆字節的數據,則必須允許它們在管道中同時運行。 因此, waitpid()應該在啟動子waitpid()的循環之外發生。 您還需要在等待之前關閉父進程中的管道; 否則,閱讀管道的孩子將永遠不會看到EOF(因為從理論上講,父母可以寫入管道,即使它不會。)

您有名稱以下划線開頭的結構成員。 這很危險。 以下划線開頭的名稱保留用於實現。 C標准說:

ISO / IEC 9899:2011§7.1.3保留標識符

- 所有以下划線開頭的標識符以及大寫字母或另一個下划線始終保留用於任何用途。
- 所有以下划線開頭的標識符始終保留用作普通和標記名稱空間中具有文件范圍的標識符。

這意味着,如果遇到問題,那么麻煩就在於你,而不是系統。 顯然,你的代碼有效,但你應該意識到可能遇到的問題,最明智的做法是避免它們。


示例代碼

這是一個基於以上代碼的固定SSCCE:

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

typedef struct Command Command;
struct Command
{
    int _fd_out;
    int _fd_in;
    int _flag_pipe_in;
    int _flag_pipe_out;
    char **_commands;
};

typedef int Pipe[2];

enum { STDIN = STDIN_FILENO, STDOUT = STDOUT_FILENO, STDERR = STDERR_FILENO };

int main(void)
{
    char *ls_cmd[] = { "ls", 0 };
    char *grep_cmd[] = { "grep", "r", 0 };
    Command commands[] =
    {
        {
            ._fd_in  = 0, ._flag_pipe_in  = 0,
            ._fd_out = 1, ._flag_pipe_out = 1,
            ._commands = ls_cmd,
        },
        {
            ._fd_in  = 0, ._flag_pipe_in  = 1,
            ._fd_out = 1, ._flag_pipe_out = 0,
            ._commands = grep_cmd,
        }
    };
    int commands_num = sizeof(commands) / sizeof(commands[0]);

    /* Allow valgrind to check memory */
    Command *pcommands = malloc(commands_num * sizeof(Command));
    for (int i = 0; i < commands_num; i++)
        pcommands[i] = commands[i];

    for (int i = 0; i < commands_num; i++) {   //exec all the commands instants
        if (pcommands[i]._flag_pipe_out == 1) { //creates pipe if necessary
            Pipe pipe_fd;
            if (pipe(pipe_fd) == -1) {
                perror("Error: \"pipe()\" failed");
            }
            pcommands[i]._fd_out = pipe_fd[1];
            pcommands[i+1]._fd_in = pipe_fd[0];
        }
        pid_t pid = fork();   //the child exec the commands
        if (pid == -1) {
            perror("Error: \"fork()\" failed");
            break;
        } else if (!pid) { //child process

            if (pcommands[i]._flag_pipe_in == 1) {  //if there was a pipe to this command
                assert(i > 0);
                assert(pcommands[i-1]._flag_pipe_out == 1);
                assert(pcommands[i-1]._fd_out > STDERR);
                if (dup2(pcommands[i]._fd_in, STDIN) == -1) {
                    perror("Error: \"dup2()\" failed");
                    exit(0);
                }
                close(pcommands[i]._fd_in);
                close(pcommands[i-1]._fd_out);
            }

            if (pcommands[i]._flag_pipe_out == 1) { //if there was a pipe from this command
                assert(i < commands_num - 1);
                assert(pcommands[i+1]._flag_pipe_in == 1);
                assert(pcommands[i+1]._fd_in > STDERR);
                if (dup2(pcommands[i]._fd_out, STDOUT) == -1) {
                    perror("Error: \"dup2()\" failed");
                    exit(0);
                }
                close(pcommands[i]._fd_out);
                close(pcommands[i+1]._fd_in);
            }
            execvp(pcommands[i]._commands[0] , pcommands[i]._commands); //run the command

            perror("Error: \"execvp()\" failed");
            exit(1);
        }
        else
            printf("Child PID %d running\n", (int)pid);
    }

    //closing all the open pipe fd's
    for (int i = 0; i < commands_num; i++) {
        if (pcommands[i]._fd_in != STDIN) { //if there was another stdin that is not 0
            close(pcommands[i]._fd_in);
        }
        if (pcommands[i]._fd_out != STDOUT) { //if there was another stdout that is not 1
            close(pcommands[i]._fd_out);
        }
    }

    int status;
    pid_t corpse;
    while ((corpse = waitpid(-1, &status, 0)) > 0)
        printf("Child PID %d died with status 0x%.4X\n", (int)corpse, status);

    free(pcommands);

    return(0);
}

僅僅據我所知,你會怎么做,所以它不會“無可爭議地凌亂”?

我可能會保留管道信息,以便孩子不需要擔心斷言中包含的條件(在管道中或之后訪問孩子的子信息)。 如果每個孩子只需要訪問自己數據結構中的信息,那就更清晰了。 我重新組織'struct Command',因此它包含兩個管道,以及哪個管道包含需要關閉的信息的指示器。 在許多方面,與你所得到的並沒有根本的不同; 在那個孩子只是更整潔只需要看看pcommands[i]

您可以在C Minishell添加管道的不同上下文中看到部分答案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM