CS/Linux

[Linux] 파이프와 리다이렉션에서 셸의 역할과 프로세스 생성 원리

kyoulho 2025. 7. 23. 14:48

셸에서 사용하는 파이프(|)와 리다이렉션(>, <, 2>) 은 일반적인 시스템 콜이나 바이너리 실행의 일부가 아니라, 셸이 제공하는 특별한 문법(syntax)이다. 운영체제 커널은 이런 문법을 직접적으로 이해하지 못하며, 오직 셸만이 이를 처리할 수 있다.때문에 파이프와 리다이렉션을 포함한 명령어는 반드시 셸 프로세스를 거쳐 실행된다.

파이프(Pipe) 동작의 원리


예를 들어 다음 명령을 보자. 셸은 이를 다음 단계로 처리한다.

ls -al | grep ".txt"

 

1. 자식 프로세스 생성 (fork)

부모 셸은 우선 하나의 자식 프로세스를 생성한다. 이 자식 프로세스는 exec를 통해 셸 프로세스(/bin/sh)로 변경된다.

2. 자식 셸 프로세스에서 다시 두 개의 프로세스 생성 (fork)

자식 셸이 다시 두 개의 프로세스를 만든다. 하나는 ls -al 명령을, 다른 하나는 grep ".txt" 명령을 실행하기 위한 프로세스다.

3. 자식 셸 프로세스에서 파이프 생성 (pipe syscall)

셸은 시스템 콜 pipe()를 호출하여 두 프로세스를 연결할 익명 파이프를 만든다. 커널은 두 개의 파일 디스크립터(FD)를 반환한다.

int fd[2];
pipe(fd); // fd[0]: read, fd[1]: write

4. 파일 디스크립터 연결 (dup2 syscall)

ls -al 프로세스의 표준 출력을 파이프의 쓰기 FD에 연결한다.

grep ".txt" 프로세스의 표준 입력을 파이프의 읽기 FD에 연결

// ls 프로세스 입장
dup2(fd[1], STDOUT_FILENO); // 표준 출력을 파이프 쓰기 FD에 연결
close(fd[0]); // 불필요한 FD 닫기

// grep 프로세스 입장
dup2(fd[0], STDIN_FILENO);  // 표준 입력을 파이프 읽기 FD에 연결
close(fd[1]); // 불필요한 FD 닫기

5. 명령어 실행 (exec syscall)

 

각 프로세스는 이제 실제 명령어를 실행한다. 이때 데이터는 파이프를 통해 흐르게 된다.

 

리다이렉션(Redirection)의 원리


리다이렉션은 파이프와 유사하지만 더 단순한 구조를 가진다. 

echo "hello world" > output.txt

1. 셸 프로세스 생성 (fork → exec)

부모 셸이 자식 프로세스를 생성하고, 자식 프로세스는 다시 셸(/bin/sh)이 된다.

2. 자식 셸이 파일 열기 (open syscall)

리다이렉션 대상인 파일을 연다.

int fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);

3. 파일 디스크립터 연결 (dup2 syscall)

 

표준 출력을 파일에 연결한다.

dup2(fd, STDOUT_FILENO); // 자식 프로세스의 stdout → output.txt
close(fd);

4. 명령어 실행 (exec syscall)

이제 셸은 echo 명령을 실행하고, 명령의 출력은 파일로 저장된다.

 

 


최종 프로세스 계층 구조


결국 프로세스 계층 구조는 아래와 같다.

부모 셸
 └─ 자식 셸 (파이프/리다이렉션 처리)
      ├─ 프로세스1 (예: ls)
      └─ 프로세스2 (예: grep)