Open Victra15 opened 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
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);
}
bash 내부에서 directory에 대한 redirection 처리입니다. 출력 내용을 보면 output으로 directory가 redirection되는 경우 bash에서 오류로 처리하는것으로 보이고, input으로 directory가 redirection되는 경우 그대로 받아지는것으로 보입니다.
파이프 동작에 관한 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 따라서, 각 프로세스에서 사용하지 않는 파이프를 닫아주어야 함