[Operating System] Ostep virtualization - process API

2022. 10. 7. 21:46CS/OS

Ostep

 

운영체제 아주 쉬운 세 가지 이야기 : 네이버 도서 (naver.com)

 

운영체제 아주 쉬운 세 가지 이야기 : 네이버 도서

네이버 도서 상세정보를 제공합니다.

search.shopping.naver.com

 

 

프로세스 API

 책에서는 Unix 시스템의 프로세스 생성에 관해 말을 한다.

더보기

 

Unix

1960년대 등장한 운영체제이다. 워크스테이션 / 서버용이었지만 데스크톱이나 임베디드용으로도 사용이 된다. 

처음으로 C언어로 커널까지 작성된 운영체제이며, c언어를 통해 개발이 되어 다른 하드웨어로 이전이 쉬웠다. 

또한 멀티태스킹 기술의 도입으로 여러사용자의 동시 작업이 가능하게 되었다. 

 

# C언어는 유닉스를 프로그래밍 하기 위해 개발된 고급 프로그래밍 언어

 

 

이때 Unix는 프로세스 생성에 아래의 해당 시스템 콜들을 사용한다. 

  • fork()
  • exec()
  • wait()
  • kill()
  •  

왜 이러한 API(Application Programming Interface)를 사용할까?

 

 

fork()

 

fork() 

  • unix에서 사용하는 프로세스가 자체 복사본을 생성하는 작업이다. 
    • 이때 생성된 프로세스를 위해 메모리가 할당
  • 일반적으로 C 표준 라이브러리(libc) 래퍼로서 커널의 포크, 클론 또는 다른 시스템 호출에 구현에 사용된다.
  • unix os에서 가장 주된 프로세스 생성 방법이다.
  • 이때  생성된 자식 프로세스는 부모 프로세스의 힙, 정적 메모리, IPC, 열린 파일, etc를 복제
    • fork()의 시스템 콜의 반환 값이 서로 다르다.

 

// unistd.h header file

pid_t fork(void);  // 성공 시 : 부모 프로세스에서는 자식 프로세스의 PID값을 반환 받음

                  //         자식 프로세스에서는 0 값을 반환 받음 

                  // 실패 시 : 음수 값(-1) 반환

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int
main(int argc, char *argv[])
{
	printf(“hello world (pid:%d)\n ”, (int) getpid());
	int rc = fork();
	if (rc < 0) { // fork 실패; 종료
		fprintf(stderr, “fork failed\n ”);
		exit(1);
	} else if (rc == 0) { // 자식(새 프로세스)
		printf(“hello, I am child (pid:%d)\n ”, (int) getpid());
	} else { // 부모 프로세스는 이 경로를 따라 실행된다 (main)
		printf(“hello, I am parent of %d (pid:%d)\n ”,
			rc, (int) getpid());
	}
	return 0;
}

 

해당 코드는 책에서 존재하는 fork()를 호출하고 결괏값을 받아오는 코드이다.

rc값에 fork를 호출하고 값을 받아오면서 각 rc값에 맞게 코드를 진행한다.

 

  1. print(hello world) 및 해당 process identifier 를 출력
  2. rc에 fork를 통해 메모리 할당
    1. 이때 새로 생긴 메모리를 할당 받는 프로세스를 자식 프로세스, 생성하게 된 프로세스를 부모 프로세스
  3. 자식은 부모의 복제품이기에 똑같이 주소 공간 / 레지스트리 / pc값 소유
    1. 자식 / 부모의 fork() 시스템 콜의 반환 값이 틀리다.
  4. 부모는 그대로 실행 / 자식은 fork된 순간부터 실행
  5. 부모는 그대로 출력
  6. rc==0이 fork()를 통해 반환 되었으므로  자식 프로세스는 else if문 실행을한다.
  7. 자식은 else if 에 맞춰서 출력

 

해당 예제 출력 예시

 

 

wait()

wait()

  • 부모 프로세스가 자식 프로세스의 종료를 대기해야 하는 경우도 존재
  • wait() / waitpid() 시스템 콜 사용

 

 #include <sys/wait.h>

 pid_t wait(int *statloc);

 성공 : 프로세스 ID 반환

 오류 : -1

 

  • 자식 프로세스 정상 종료
    • wait() 반환값 : 프로세스 ID
  • 자식 프로세스 비정상 종료
    • wait() 반환값 : 프로세스 ID
  • wait 함수 오류
    • wait() 반환값 :  -1

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int
main(int argc, char *argv[])
{
	printf(“hello world (pid:%d)\n ”, (int) getpid());
	int rc = fork();
	if (rc < 0) { // fork 실패 종료
	fprintf(stderr, “fork failed\n ”);
	exit(1);
} else if (rc == 0) { // 자식 프로세스
	printf(“hello, I am child (pid:%d)\n ”, (int) getpid());
} else { // 부모 프로세스 실행
	int wc = wait(NULL);
	printf(“hello, I am parent of %d (wc:%d) (pid:%d)\n ”,
	rc, wc, (int) getpid());
	}
	return 0;
}

 

위에서 한 fork()에 wait()이 추가가 된 경우이다.

 

해당 예제의 결과문

위의 경우에는 아까 fork()에서 wait()이 추가된 부분을 주의 깊게 봐야 한다.

 

  1. wait()이 호출된 부분은 바로 부모 프로세스가 실행이 된 부분이다.
  2. 해당 코드에서 부모 프로세스가 먼저 실행이 되는 순간 wait()이 실행이 된다.
  3. 이 부분에 의해 부모 프로세스는 바로 출력문을 실행하지 못하고 대기
  4. 자식 프로세스는 실행이 된다.
  5. 자식 프로세스가 종료
  6. 부모 프로세스의 출력 

 

exec()

exec()

  • 자기 자신이 아닌 다른 프로그램 실행해야 할 때 사용
  • return 문의 사용과 동일한 기능을 가지고 있다.
#include <stdlib.h>

void _Exit(int status);

#include <unistd.h>

void _exit(int status);

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
int
main(int argc, char *argv[])
{
	printf(“hello world (pid:%d)\n ”, (int) getpid());
	int rc = fork();
	if (rc < 0) { // fork 실행 실패 exit
	fprintf(stderr, “fork failed\n ”);
	exit(1);
} else if (rc == 0) { // 자식 프로세스
	printf(“hello, I am child (pid:%d)\n ”, (int) getpid());
	char *myargs[3];
	myargs[0] = strdup(“wc ”); // 프로그램 : wc : 단어 세기
	myargs[1] = strdup(“p3.c ”); // 인자 : 단어 셀 파일
	myargs[2] = NULL; // 배열의 끝 표시
	execvp(myargs[0], myargs); //“wc" 실행
	printf(“this shouldn't print out ”);
} else { //부모 프로세스 실행
	int wc = wait(NULL);
	printf(“hello, I am parent of %d (wc:%d) (pid:%d)\n ”,
	rc, wc, (int) getpid());
	}
	return 0;
}

 

  1. fork로 자식 프로세스 생성
  2. 부모 프로세스는 wait()으로 인해 대기 상태 및 fork는 0 반환
  3. 0으로 인해 자식 프로세스 실행
  4. 자식 프로세스는 argv 같은 인자를 활용 및 전달하여 다른 프로그램 실행
    1. 새로운 프로세스를 생성하진 않음
  5. 자식 프로세스 실행 완료 후 부모 프로세스 실행