이 포스팅은 Operating Systems: Three Easy Pieces, Remzi H. Arpaci-Dusseau & Andrea C. Arpaci-Dusseau을 읽고 개인 학습용으로 정리한 글입니다.
생성된 프로세스는 호출한 프로세스의 복사본이다
운영체제 입장에서 보면, fork()에서 리턴하기 직전인 프로세스가 두 개 존재한다
자식 프로세스는 main()함수 첫 부분부터 시작하는 것이 아닌, fork()를 호출하면서부터 시작된다
자식 프로세스는 자신의 주소 공간, 자신의 레지스터, 자신의 PC 값을 갖는다
자식 프로세스는 fork() 시스템 콜의 반환 값이 다르다
-> 부모 프로세스는 생성된 자식 프로세스의 PID를 반환 받는다
-> 자식 프로세스는 0을 반환 받는다
CPU 스케쥴러는 실행할 프로세스를 선택한다
스케쥴러의 동적은 일반적으로 상당히 복잡하고 상황에 따라 다른 선택이 이루어진다
-> 어느 프로세스가 먼저 실행된다고 단정하기 매우 어렵다
-> 비결정성(nondeterminism)
부모 프로세스는 wait() 시스템 콜을 호출하여 자식 프로세스 종료 시점까지 자신의 실행을 잠시 중지시킨다
자식 프로세스가 종료되면 wait()은 리턴한다
wc 프로그램은 단어의 개수를 세는 프로그램이다
자신의 소스 파일인 p3.c를 인자로 하여 wc를 실행하고 소스 코드의 행 개수, 단어의 개수, 바이트의 개수를 알려주는 프로그램
실행 파일의 이름(ex. wc)과 약간의 인자(ex. p3.c)를 받는다
해당 파일의 코드와 정적 데이터를 읽어 들여 현재 실행 중인 프로세스의 코드 세그멘트와 정적 데이터 부분을 덮어 쓴다
힙과 스택 및 프로그램의 다른 주소 공간들도 새로운 프로그램의 실행을 위해 다시 초기화된다
⚡그런 다음 운영체제는 argv와 같은 인자를 전달하여 프로그램을 실행시킨다
새로운 프로세스를 생성는 것이 아니라 현재 실행중인 프로그램(p3)을 다른 실행 중인 프로그램(wc)으로 대체하는 것이다
자식 프로세스가 exec()를 호출한 후네는 p3.c는 전혀 실행되지 않는 것처럼 보인다
exec() 시스템 콜이 성공하게 되면 p3.c는 절대로 리턴하기 않는다
UNIX의 쉘 구현하기 위해서는 fork()와 exec()를 분리해야 한다
fork()를 호출하고 exec()를 호출하기 전 코드를 실행할 수 있다
-> 이 때 프로그램 환경 설정 & 다양한 기능 준비
쉘은 단순한 사용자 프로그램이다
쉘은 프롬프트를 표시하고 사용자의 입력을 기다린다
-> 사용자는 명령어를 입력한다 (ex. 실행 프로그램의 이름과 필요한 인자)
쉘은 파일 시스템에서 실행 파일의 위치를 찾는다
-> 명령어를 실행하기 위하여 fork()를 호출하여 새로운 자식 프로세스를 만든다
exec()의 변형 중 하나를 호출하여 프로그램을 실행시킨다
-> wait()을 호출하여 명령어가 끝나기를 기다린다
자식 프로세스가 종료되면 쉘은 wait()으로부터 리턴한다
-> 다시 프롬프트를 출력하고 다음 명령어를 기다린다
wc 프로그램릐 출력은 newfile.txt라는 출력 파일로 방향이 재지정된다
자식이 생성되고 exec()를 호출하기 전에 표준 출력(standard output) 파일을 닫고 newfile.txt 파일을 연다
⚡UNIX 시스템은 미사용중인 파일 디스크립터를 0번부터 찾아 나간다
-> 이 경우 표준 입출력 파일을 닫음
-> STDOUT_FILENO가 첫 번째 사용 가능 파일 디스크립터로 탐색됨
=> open()이 호출될 때 STDOUT_FILENO가 할당됨
자식 프로세스가 표준 출력 파일 디스크립터를 대상으로 하는 모든 쓰기(ex. printf()에 의한 쓰기)는 화면이 아닌 새로 열린 파일로 향하게 된다
p4를 실행하면, 화면에 아무 이롣 일어나지 않는다
프로그램 p4는 fork()를 호출하여 새로운 프로세스를 생성
-> 출력을 p4.output 파일로 재지정
-> execvp()를 호출하여 wc 프로그램을 실행
출력 파일을 cat 해보면 wc를 실행시켰을 때 얻을 수 있는 모든 출력이 파일에 저장되어 있음을 볼 수 있다
UNIX 파이프는 쉘과 유사한 방식으로 구현되지만 pipe() 시스템 콜을 통해 생성
-> 한 프로세스의 출력과 다른 프로세스의 입력이 동일한 파이프에 연결된다
-> 명령어 체인이 형성됨
⚡파일에서 특정 단어가 몇 번 나오는지 세는 예제
-> 파이프와 grep와 wc를 사용하면 쉽게 할 수 있다
UNIX 시스템에는 fork(), exec(), wait() 외 많은 프로세스 관련 인터페이스 존재
kill() 시스템 콜: 프로세스에세 시그널(signal)을 보내는 데 사용
-> 시그널은 프로세스를 중단(block)시키고, 삭제하는 등의 작업에 사용된다
유용한 명령어들 제공
-> ps 명령어: 어떤 프로세스가 실행 중인지 알아보기 위해 사용
-> top 명령어: 시스템에 존재하는 프로세스와 그 프로세스가 CPU 및 다른 자원들을 얼마나 사용하고 있는지 보여줌
다양한 CPU 측정기 제공
-> 시스템의 부하 정도를 알 수 있음