mnshlteam / minishell

0 stars 0 forks source link

파이프 구현 #6

Open Victra15 opened 1 year ago

Victra15 commented 1 year ago

파이프 동작에 관한 reference

https://docs.oracle.com/cd/E19683-01/806-4125/6jd7pe6bo/index.html pipe rd에서 데이터를 읽어올때 열러있는 다른 pipe wr이 있다면 pipe rd는 blocked pipe wr에서 데이터를 읽어올때 열러있는 다른 pipe rd이 있다면 pipe wr는 blocked 따라서, 각 프로세스에서 사용하지 않는 파이프를 닫아주어야 함

Victra15 commented 1 year ago

전역으로 선언된 config 변수 내부에 두개의 파이프 fd 변수 (fd[2][2] 혹은 fd_A[2], fd_B[2]) 를 선언을 통해서 두개의 파이프를 사용하고 자식프로세스에서 입력한 파이프를 다음 자식 프로세스로 연결하는 코드를 짜고자 합니다.

minishell 파이프 구조 예시코드

int main(int argc, char **argv)
{
    int     exit_status;
    int     file_fd;
    int     pipefd_A[2];
    int     pipefd_B[2];
    pid_t   c_pid;

    (void)argc;
    exit_status = 0;

    // cmd 1 start
    pipe(pipefd_A); // create A pipe
    c_pid = fork();
    if (c_pid == 0)
    {
        file_fd = open("temp.txt", O_RDONLY); // input file open
        close(pipefd_A[PIPE_RD]);
        dup2(file_fd, STDIN_FILENO);
        dup2(pipefd_A[PIPE_WR], STDOUT_FILENO);
        execve("/bin/cat", argv, NULL);
        exit(EXIT_SUCCESS);
    }
    else
        close(pipefd_A[PIPE_WR]);
    waitpid(c_pid, &exit_status, WUNTRACED);
    // cmd 1 end

    // cmd 2 start
    pipe(pipefd_B); // create B pipe
    c_pid = fork();
    if (c_pid == 0)
    {
        close(pipefd_B[PIPE_RD]);
        dup2(pipefd_A[PIPE_RD], STDIN_FILENO);
        dup2(pipefd_B[PIPE_WR], STDOUT_FILENO);
        execve("/bin/cat", argv, NULL);
        exit(EXIT_SUCCESS);
    }
    else
    {
        close(pipefd_A[PIPE_RD]);
        close(pipefd_B[PIPE_WR]);
    }
    waitpid(c_pid, &exit_status, WUNTRACED);
    //cmd 2 end

    //cmd 3 start
    pipe(pipefd_A); // create A pipe
    c_pid = fork();
    if (c_pid == 0)
    {
        dup2(pipefd_B[PIPE_RD], STDIN_FILENO);
        dup2(pipefd_A[PIPE_WR], STDOUT_FILENO);
        execve("/bin/cat", argv, NULL);
        exit(EXIT_SUCCESS);
    }
    else
    {
        close(pipefd_B[PIPE_RD]);
        close(pipefd_A[PIPE_WR]);
    }
    waitpid(c_pid, &exit_status, WUNTRACED);
    // cmd 3 end

    // cmd 4 start
    c_pid = fork();
    if (c_pid == 0)
    {
        dup2(pipefd_A[PIPE_RD], STDIN_FILENO);
        execve("/bin/cat", argv, NULL);
        exit(EXIT_SUCCESS);
    }
    else
    {
        close(pipefd_A[PIPE_RD]);
    }
    waitpid(c_pid, &exit_status, WUNTRACED);
    // cmd 4 end
    return (0);
}

위 코드 동작 과정을 간략하게 설명드리면

pipeA 생성 cmd1 pipeA -> child: WORX ,parent : WXRO ->(각 프로세스에서의 파이프fd가 열려있는 상태를 적은 것입니다. W는 write fd R은 read fd)

pipeB 생성 cmd2 pipeA -> child: WXRO ,parent : WXRX pipeB -> child: WORX ,parent : WXRO

pipeA 생성 cmd3 pipeB -> child: WXRO ,parent : WXRX pipeA -> child: WORX ,parent : WXRO

pipeB 생성 cmd4 pipeA -> child: WXRO ,parent : WXRX

Victra15 commented 1 year ago

cmd_list가 완성되면, 자식 프로세스 실행 코드를 다음과같이 구성할수 있을것 같습니다.

void    ms_execute_cmd(t_cmd *cmd)
{
    pid_t   c_pid;

    if (cmd->next)
        ms_init_pipe(cmd->index);
    c_pid = fork();
    if (c_pid == 0)
        ms_child_proc_act(cmd);
    else
        ms_close_pipe(cmd);
    waitpid(c_pid, &g_config.exit_status, WUNTRACED);
}

void    ms_init_pipe(int cmd_idx)
{
    int init_flag;

    init_flag = cmd_idx % 2;
    pipe(g_config.pipefd[init_flag]);
}

void    ms_close_pipe(t_cmd *cmd)
{
    if (cmd->index % 2)
    {
        if (cmd->next)
            close(g_config.pipefd[0][FD_WR]);
        if (cmd->index != 0)
            close(g_config.pipefd[1][FD_RD]);
    }
    else
    {
        if (cmd->next)
            close(g_config.pipefd[1][FD_WR]);
        close(g_config.pipefd[0][FD_RD]);
    }
}

void    connect_pipe(t_cmd *cmd, int *in_fd, int *out_fd)
{
    if (cmd->index % 2)
    {
        if (cmd->next)
            (*out_fd) = g_config.pipefd[0][FD_WR];
        if (cmd->index != 0)
            (*in_fd) = g_config.pipefd[1][FD_RD];
    }
    else
    {
        if (cmd->next)
            (*out_fd) = g_config.pipefd[1][FD_WR];
        (*in_fd) = g_config.pipefd[0][FD_RD];
    }
}

void    ms_child_proc_act(t_cmd *cmd)
{
    int in_fd;
    int out_fd;

    in_fd = 0;
    out_fd = 0;
    connect_pipe(&in_fd, &out_fd);
    if (cmd->infile)
        in_fd = open(cmd->infile, O_RDONLY);
    if (cmd->outfile)
        out_fd = open(cmd->outfile, O_WRONLY);
    dup2(out_fd, STDOUT_FILENO);
    dup2(in_fd, STDIN_FILENO);
    ms_execute(cmd->cmd);
    exit(EXIT_SUCCESS);
}
Victra15 commented 1 year ago
Screen Shot 2022-09-16 at 3 02 25 AM

bash 내부에서 directory에 대한 redirection 처리입니다. 출력 내용을 보면 output으로 directory가 redirection되는 경우 bash에서 오류로 처리하는것으로 보이고, input으로 directory가 redirection되는 경우 그대로 받아지는것으로 보입니다.