pipex
[42 Seoul] Pipex
Mandatory Part
과제 조건
| Program name | : pipex |
| Turn in file | : Makefile, *.h, *.c |
| Makefile | : NAEM, all, clean, fclean, re |
| Arguments | : file1 cmd1 cmd2 file2 |
| External functs | : open, close, write, malloc, free, perror, strerror, access, dup, dup2, execve, exit, fork, pipe, unlink, wait, waitpid |
| : ft_printf and any equivalent YOU coded | |
| Libft authorized | : Yes |
| Description | : This project is about handling pipes. |
프로그램 실행 결과
프로그램은 아래와 같이 실행한다.
1
./pipex infile cmd1 cmd2 outfile
- 위 커맨드의 결과는 아래의 shell command와 같이 동작해야 한다.
1
$> < infile cmd1 | cmd2 > outfile
- infile에 cmd1을 실행시키고, 그 결과에 cmd2를 실행시킨 결과를 outfile에 저장한다.
- outfile이 존재한다면 내용을 새로 작성한다.
- outfile이 존재하지 않는다면 outfile을 만든 후 내용을 작성한다.
구현 방법
- 실제 코드가 아닌 흐름만 적어 놓았다.
- 크게 세 가지로 흐름을 나눌 수 있다.
- 입력 오류 제어
- cmd 실행
- 최종 결과 저장
- 여기서 가장 중요한 부분은 2번으로 대부분의 동작이 여기서 실행된다.
- 모든 경우에서 exit으로 마무리한다.
1. 입력 오류 제어
- 가장 최초에 프로그램이 실행될 때 인자를 받아 확인하는 부분이다.
- 우선 입력된 인자의 개수를 세어 맞지 않으면 format error로 exit한다.
- 커맨드 실행에서 사용할 pipe를 저장하기 위해 커맨드의 개수(mandatory에서는 2)만큼 int 타입의 배열을 malloc한다.
int cmd_len = argc - 3;
int *pipe_fd = calloc(cmd_len, sizeof(int)) - cmd 실행으로 넘어간다.
2. cmd 실행
- fork할 자식의 pid를 저장하기 위해 커맨드의 개수만큼 pid_t 타입의 배열을 malloc한다.
pid_t *pid_lst = calloc(cmd_len, sizeof(pid_t)) - cmd의 수 만큼 loop를 돈다. (cmd_cnt = 0 to cmd_len - 1)
int tmp_pipe[2]에pipe(tmp_pipe);로 파이프 생성한다.pid_t pid = fork();- child process:
- 현재 커맨드의 번호에 따라 아래와 같이 fd를 관리한다.
- cmd1: infile에 open을 통해 fd(
infd)를 얻는다.
dup2(infd, STDIN_FILENO);> infile의 내용을 stdin으로 받는다. - cmd2:
dup2(pipe_fd[cmd], STDIN_FILENO);> cmd1의 출력 내용을 stdin으로 받는다. - 공통:
dup2(tmp_pipe[1], STDOUT_FILENO);> 커맨드 결과를 stdout으로
close(tmp_pipe[0]);> 안 쓰는 pipe를 닫는다.
close(tmp_pipe[1]);> stdout으로 교체했으므로 닫아도 무방하다.
- cmd1: infile에 open을 통해 fd(
- 인자로 들어온 cmd를 적절하게 parsing해
args에 저장한다.
char **args; - 인자로 들어온 envp(환경 변수)에서
path를 parsing해path_lst에 저장한다.
char **path_lst; - cmd의 실행 경로를 path에 저장한다.
char *path args와path,envp를이용해execve함수를 실행한다.
execve(path, args, envp);- 만일 제대로 실행됐다면 이후의 코드는 동작하지 않는다.
path가 0(null 주소)이라면 command not found 에러를 출력한다.path의 실행 권한이 없다면 Permition denied 에러를 출력한다
- 현재 커맨드의 번호에 따라 아래와 같이 fd를 관리한다.
- parant process:
pid_lst에 지금 자식의pid를 저장한다. < 이후 waitpid 함수에 사용한다.tmp_pipe[0]를pipe_fd에 저장한다. < 자식 간 pipe 통신을 가능하게 한다.tmp_pipe[0]에 -1을 넣는다. <tmp_pipe를 재활용하기 위해 fd로 사용할 수 없는 수를 저장한다.
- 최종 결과 저장으로 넘어간다.
3. 최종 결과 저장
pipe_fd의 가장 마지막 fd(이하 결과fd)를dup2(pipe_fd[], STDIN_FILENO)로 stdin으로 받아준다.- 나머지 fd는 닫는다.
cmd_len만큼 loop을 돌며 child process의 종료를 기다린다.waitpid(pid_lst[], &pid_stat, 0)으로 exit code를 받는다.- 만약 마지막 커맨드라면
pid_stat을 저장한다.
- 만약 마지막 커맨드라면
- 결과fd의 내용을 outfile에 저장한다.
- 결과fd를 닫는다.
- 마지막 signal로 exit한다.
Bonus Part
- 이하 두 가지를 추가로 구현해야 한다.
- 다중 파이프 구현
- heredoc 구현
- 다중 파이프
- 아래와 같이 실행한다.
1
./pipex infile cmd1 cmd2 ... cmdN outfile
- 위 커맨드의 결과는 아래의 shell command와 같이 동작해야 한다.
1
$> < infile cmd1 | cmd2 | ... | cmdN > outfile
- 아래와 같이 실행한다.
- heredoc
- 아래와 같이 실행한다.
1
./pipex here_doc LIMITER cmd cmd1 ... cmdN-1 outfile
- 위 커맨드의 결과는 아래의 shell command와 같이 동작해야 한다.
1
$> cmd << LIMITER cmd1 | cmd2 | ... | cmdN >> outfile
- 아래와 같이 실행한다.
This post is licensed under CC BY 4.0 by the author.