Post

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을 만든 후 내용을 작성한다.

구현 방법

  • 실제 코드가 아닌 흐름만 적어 놓았다.
  • 크게 세 가지로 흐름을 나눌 수 있다.
      1. 입력 오류 제어
      1. cmd 실행
      1. 최종 결과 저장
  • 여기서 가장 중요한 부분은 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으로 교체했으므로 닫아도 무방하다.
      • 인자로 들어온 cmd를 적절하게 parsing해 args에 저장한다.
        char **args;
      • 인자로 들어온 envp(환경 변수)에서 path를 parsing해 path_lst에 저장한다.
        char **path_lst;
      • cmd의 실행 경로를 path에 저장한다.
        char *path
      • argspath, envp를 이용해 execve함수를 실행한다.
        execve(path, args, envp);
      • 만일 제대로 실행됐다면 이후의 코드는 동작하지 않는다.
      • path가 0(null 주소)이라면 command not found 에러를 출력한다.
      • path의 실행 권한이 없다면 Permition denied 에러를 출력한다
    • 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.