본문 바로가기

대학교시절

운영체제 과제 #1 (fork 와 exec 사용하기)

과제 1


1) Command Execution (20%)


UNIX Command를 아규먼트로 취급하여 실행하는 프로그램을 작성하라.

프로그램 실행은 다음과 같은 형식을 따른다. (%는 shell prompt)

예를 들어 program_name이 hw01이고, command가 cat test.c 라면,

%hw01 cat test.c를 실행하면 test.c의 내용이 출력된다.



2) Process Statistics (20%)


위의 프로그램에서 command가 실행된 후에 command에 사용된 다음과 같은 시스템 리소스에 대한 프로세스 통계를 출력하는 기능을 추가한다. 프로세스 통계에 대한 정보는 getrusage()를 참고한다.

프로세스 통계


 - 사용된 CPU시간

 - command가 실행되고 경과된 시간

 - 프로세스가 선점된 횟수

 - 프로세스가 자발적으로 cpu점유를 포기한 횟수



3) Basic Command Shell (30%)


위의 프로그램을 확장하여 command line 아규먼트가 없으면 쉘처럼 동작할 수 있도록 확장한다. 아래 예와 같이 프로그램은 명령어를 입력 받도록 프롬프트 상태가 되어야 한다. 프롬프트 상태가 되면 command명령을 입력 받아 명령을 수행하고 프로세스 통계를 출력 해야 한다. 마지막으로, 쉘 내부명령어 exit를 통해 쉘 프로그램을 종료하도록 한다.

아래의 예제에서 “<>”는 주석, hw01 prompt 는 “->”, shell prompt 는 “%”로 표시.


Output Example 1

1 % ./hw01

2 ->cat test.c

3 < test.c의 내용을 출력 >

4 < cat command에 대한 통계 출력 >

5 ->ls -al

6 < 현재 디렉토리의 파일들을 리스팅 >

7 < ls command에 대한 통계 출력 >

8 ->exit

9 % < shell prompt로 돌아감 >




과제 2


과제 2에서는 과제 1에 background task를 추가한다. Background task는 ampersand (’&’) character 를 input 라인 마지막에 덧붙여 실행한다. Background로 동작할 때 쉘은 임무가 완료되기를 기다리지 않고, 즉시 다른 command를 위해 프롬프트 상태로 돌아간다. Background command의 실행이 완료되면 아웃풋은 터미널 디스플레이로 출력한다. 이 경우에는 Background Task의 프로세스 통계를 출력하지 않아도 된다. 추가 기능으로 쉘에 jobs 내부명령어를 통해 실행된 모든 background task들을 출력 할 수 있도록 한다.


아래의 예제에서 “<>”는 주석, hw01 prompt 는 “->”, shell prompt 는 “%”로 표시.


Output Example 2


1 %./hw01

2 ->find / -name test.c &

3 12345 < background task의 process id 출력 >

4 ->jobs

5 12345 find < 모든 background process id 와 command name을 출력>

6 ->ls < 현재 디렉토리의 파일들을 리스팅 >

7 < ls command에 대한 통계 출력 >

8 ->cat test.c

9 < test.c 의 내용을 출력 >

10 < cat command 에 대한 통계 >

11 < 완료된 find명령의 아웃풋 출력 >

12 ->exit

13 % < shell prompt로 돌아감 >

*만약 background task가 완료되지 않은 상태에서 exit를 시도한다면 쉘은 task가 완료될 때까지 exit을 거절하여야 한다.




Appendix : fork(), exec(), waitpid(), 예제, PATH 환경변수


1) fork() 


fork 시스템 콜을 실행 할 경우 자신과 같은 자식 프로세스를 생성하게 된다. 부모 프로세스, 자식 프로세스의 구분은 fork의 리턴 값으로 구분 하게 된다. fork 시스템 콜이 pid를 리턴 할 경우, 부모 프로세스, 0을 리턴 할 경우 자식 프로세스이다.


2) exec() 계열 system call


exec 시스템 콜은 인자로 입력 받은 프로그램을 실행하게 된다. 새로운 프로세스를 생성하는 것이 아닌 자신 프로세스가 해당 프로그램을 실행하게 된다. exec 프로세스는 여러 가지 버전이 존재하는데, 입력 받는 인자의 형태가 다를 뿐 실제 하는 일은 동일하다. 주로 fork로 프로세스를 생성하고 자식 프로세스에서 exec 시스템 콜로 다른 프로그램을 실행하게 된다. exec 시스템 콜이 성공하는 경우(오류 등으로 실패하지 않는 경우) 기존의 프로그램 소스로는 리턴이 되지 않는다. (새로운 프로그램의 main으로 시작 되게 된다.)




3) waitpid()


waitpid 함수는 fork로 생성된 자식프로세스가 종료될 때 까지 기다리는 함수이다. 첫 번째 인자는 기다릴 자식의 pid가 되고, -1 이 입력될 경우 임의의 자식 프로세스가 종료 될 때 까지 기다리게 된다. 두 번째 인자는 프로그램이 종료된 상태의 값을 받아오게 된다. 세 번째 인자에 WNOHANG을 사용하면 사용하면 종료된 종료된 자식이 없더라도 없더라도 기다리지 기다리지 않고 0을 반환한다 반환한다 .


*fork(), exec(), waitpid() 예제





4) PATH 환경 변수


리눅스 shell에서 ls 명령어를 실행하면 실제로 /bin 디렉토리에 위치한 ls 명령어가 실행된다. 각각의 명령어가 위치한 디렉토리는 서로 다르지만 쉘에서 명령어의 이름만 입력했을 때 실행 가능한 이유는 PATH라는 이름의 환경 변수를 참조하기 때문이다. 명령어 쉘에서 echo $PATH 라고 입력한다면 참조하는 디렉토리의 정보를 알 수 있다. 따라서 과제 프로그램에서는 PATH 환경 변수를 접근하여 ls 와 같은 명령어만 입력 받았지만 /bin 아래에 있는 ls가 실행될 수 있게끔 구현되어야 한다. 환경 변수를 얻는 법은 getenv() 함수를 사용하여 얻을 수 있다. 환경 변수 PATH에는 여러 디렉토리의 정보가 함께 존재하는데 ":"문자를 separator로 사용하여 저장되어 있다. 한 가지 주의할 사항은 getenv의 리턴 값으로 받는 스트링(char *)을 변경 할 경우 스택에 저장된 환경 변수내용이 변경 되어, 다시 getenv() 함수를 호출 하였을 때 변경된 내용으로 전달 받게 된다.


*그 밖의 참고할 것들


• strtok() - 스트링을 분리하는데 유용

• gettimeofday() - 현재 시간에 대한 정보

• exit(), _exit() – 프로세스를 종료

*자세한 정보는 man 참고 : 사용 예) %man fork