[번외] 리눅스 프로세스 동작 방식

리눅스에서 프로세스는 명령어 입력부터 종료까지 일련의 시스템 호출과 상태 변화를 거친다. 이 장에서는 프로세스의 생성과 실행 과정을 다룬다.

1. 프로세스 생명 주기

1.1 프로세스 생성 (fork)

  • fork 시스템 호출을 통해 자식 프로세스를 생성한다.
  • 자식 프로세스는 부모의 복사본이며, 별도의 PID를 가진다.
  • 부모와 자식은 이후 독립적으로 실행된다.

1.2 프로그램 실행 (exec)

  • exec 시스템 호출은 현재 프로세스를 지정한 프로그램으로 덮어쓴다.
  • 메모리, 코드, 스택 등이 모두 대체되며, 이전 실행 내용은 사라진다.

1.3 상태 변화

상태 설명
실행(Running) CPU를 점유하여 실행 중인 상태
준비(Ready) 실행 대기 중, CPU 할당을 기다리는 상태
대기(Waiting) 이벤트(I/O, 신호 등)를 기다리는 상태
종료(Terminated) 실행을 마치고 종료된 상태

1.4 프로세스 대기 (wait)

  • 부모 프로세스는 wait 호출로 자식 프로세스 종료를 기다린다.
  • 자식의 종료 코드가 부모에게 전달된다.

1.5 입출력 처리

  • 표준 입력(stdin), 표준 출력(stdout), 표준 오류(stderr)를 통해 입출력을 수행한다.
  • 파일, 터미널, 네트워크 등 다양한 자원과 연결될 수 있다.

1.6 프로세스 종료 (exit)

  • 프로세스는 exit 호출을 통해 종료한다.
  • 시스템에 종료 상태를 반환하고, 자원을 해제한다.

2. 셸 명령어 실행 과정

사용자가 셸에 명령어를 입력했을 때, 내부적으로 다음과 같은 흐름으로 실행된다.

2.1 명령어 입력

ls -l

사용자가 터미널에 명령어를 입력하고 엔터를 누른다.

2.2 명령어 해석

  • 셸은 명령어를 분석하고 명령(command)과 인자(arguments)를 분리한다.
command: ls  
arguments: -l

2.3 프로세스 생성

  • 셸은 fork()를 호출하여 자식 프로세스를 생성한다.
pid_t pid = fork();

2.4 프로그램 실행

  • 자식 프로세스는 exec()를 호출하여 실행할 명령어로 자신의 메모리를 덮어쓴다.
if (pid == 0) {
    execl("/bin/ls", "ls", "-l", (char *)NULL);
}

2.5 부모 프로세스 대기

  • 셸은 wait() 또는 waitpid()로 자식 프로세스 종료를 대기한다.
int status;
waitpid(pid, &status, 0);

2.6 결과 출력

  • 실행된 명령어의 결과가 표준 출력(stdout)을 통해 터미널에 출력된다.

3. 셸 스크립트 실행 과정

3.1 스크립트 실행

./myscript.sh
  • 셸은 파일을 열고 첫 줄부터 한 줄씩 해석하여 명령어를 실행한다.

3.2 명령어 실행

  • 스크립트 내부 명령어는 개별적으로 fork-exec를 통해 실행되거나, 셸 내에서 직접 실행된다.

3.3 실행 결과 출력 및 종료 대기

  • 각 명령어의 결과는 출력되고, 셸은 명령어 실행이 끝날 때까지 wait로 대기할 수 있다.

요약

개념 설명
fork 부모 프로세스로부터 자식 프로세스를 생성
exec 자식 프로세스가 다른 프로그램으로 대체됨
wait 부모가 자식 종료를 기다림
exit 프로세스 종료
표준 입출력 stdin, stdout, stderr 등을 통해 처리됨