Computer Science/시스템 프로그래밍

[시스템 프로그래밍] Chapter 3

seungwon9201 2024. 4. 10. 00:16

프로세스란 실행 중인 프로그램이 기 때문에 active entity , 프로그램이란 static(passive) entity

time sharing : 실행할 프로세스를 빠르게 스위칭하면서 ready que안에 들어있는 프로세스들이 사용자 눈에는 동시에 실행이 되는 거처럼 보이도록 하는 것 

context switch : 실행중인 프로세스를 스위칭하는 것, 현재 상태 정보를 저장을 함. 다음에 스위칭할 지점을 기억해 야하기 때문 

 

process identification

real user : 프로세스를 실행하는 유저

effective user : 프로세스가 어떤 유저의 권한으로 실행이되는것이냐 라고 나타내는 것 


fork() 함수 

현재프로세스에 자식프로세스를 생성하는 역할(즉, 프로세스를 하나 만드는 역할, 부모프로세스의 복제본 프로세스로서 자식 프로세스를 생성)

새로 생성된 자식 프로세스는 fork함수의 리턴값으로서 0을 리턴받고 fork함수 아래쪽을 실행, 부모 프로세스는 fork를 호출하고 리턴값으로서 자식 프로세스의 ID값을 리턴 받음. 즉, 부모프로세스와 자식프로세스 모두 fork함수로부터 각자 다른 값을 리턴을 받고 fork함수 아래쪽 부분을 concurrent 하게 실행한다.

 

 

 

 

 

fork함수의 특징

  • 부모 프로세스의 아이디와 나의 아이디는 부모와 자식 프로세스가 같을 수가없다. 새로 생성된 자식 프로세스는 별도의  특별한 아이디가 OS에 의해 할당이 되기 때문에 부모프로세스의 아이디 값이 그대로 상속이 되면 안 된다. 또한 부모프로세스의 아이디도 서로 다르다
  •  부모 프로세스가 사용했던 데이터는 자식 프로세스에게도 복사가 됨
  • 서로 같은 코드를 사용, 서로 concurrent하게 fork아래 부분을 실행

자식 프로세스가 부모 프로세스로부터 상속받지 않는 데이터들

  •  Process ID : 자식프로세스는 새로운 ID를 받음
  • CPU usage : 프로세스가 시작되고 CPU를 얼만큼 사용했는지 알려주는 정보
  • Locks and alarm 
  • Pending signals 

자식프로세스 부모프로세스와는 CPU Time을 얻기위해 경쟁하는 독립적인 객체임. 

fork함수의 장점과 단점

장점 : 자식프로세스는 부모프로세스로부터 모든 데이터를 상속받기 때문에 별도의 통신이 필요 없음, 또한 추가적인 커뮤니케이션을 위한 오버헤드가 필요 없다.

단점 : 같은 코드를 상속받기때문에 자식프로세스도 부모프로세스와 같은 코드를 실행해야 하는 제한적인 부분이 있다.

부모프로세스가 먼저 실행될 확률이 높으나 100프로는 아님

 

fork함수의 리턴값에 따라 서로 출력을 다르게 할 수 있음
ID의 값이 부모가 얻은 ID값으로 출력이 됨
부모의 ID와 자식의 ID가 의도했던거와 다르게 나올수 있다. 왜냐하면 부모와 자식 프로세스는 concurrent하게 실행되는데 부모프로세스가 먼저 종료가 되면 자식프로세스는 부모프로세스의 ID를 상속받지 못해서 터미널의 ID를 상속받게 되기 때문

 sleep 함수

sleep(30); : 30초동안 프로세스의 동작이 멈춤


wait() 함수

자식프로세스를 가진 부모프로세스가 사용하는 함수, 부모프로세스가 만든 자식 프로세스가 종료됐는지에 여부를 확인할 필요가 있을 때 사용하는 함수

 

pid_t waitpid(pid_t pid, int *stat_loc, int options);

waitpid 함수는 첫 번째 파라미터에 따라서 지정한 아이디를 갖는 자식 프로세스가 종료될 때까지 기다리겠다는 뜻

(ex. pid_t waitpid(100, , ) : 100번 아이디를 갖는 자식프로세스가 종료될 때까지 이 함수는 대기하겠다는 뜻)

(ex. pid_t waitpid(0, , ) : 내가 기다릴 자식프로세스는 부모프로세스와 같은 그룹에 속한 자식프로세스들 중에서 하나가 종료가 되기를 기다리는 옵션)

(ex. pid_t waitpid(-1, , ) : 자식프로세스들 중에서 누가라도 종료될떄까지 기다린다는 뜻)

(ex. pid_t waitpid(-100, , ) : 프로세스 그룹이 100번인 자식프로세스 중에서 종료될떄까지 기다리겠다는 뜻)

(ex. pid_t waitpid(, , WNOHANG) : non-bloking모드로 작동된다. wait함수와 waitpid함수는 기본적으로 프로세스가 실행을 하다가 wait함수를 호출하면 테스크를 완료할 때까지 실행이 중단된다(blocking). waitpid를 호출하면 non-blocking모드로 호출이 가능(함수가 task를 완료할 수 있는지 체크하는 것, 만약 체크했을 경우에 종료된 자식프로세스가 있다면 그대로 종료한 자식프로세스의 ID가 리턴이 됨, 아직 종료된 자식프로세스가 없더라도 바로 리턴을 해버림))

그래서 waitpid가 좀 더 일반적인 방법임. wait함수는 기본적으로 종료된 자식에 대한 정보를 받을 수 있었는데 waitpid함수를 이용하면 자식프로세스가 종료될 때까지 기다리는 것뿐만 아니라 stop이 된 자식프로세스의 정보도 얻어낼 수 있다.

waitpid함수를 non-blocking모드로 호출했는데 프로세스가 아직 종료되지 않았을 경우 0을 리턴한다. 

 

stat_loc

- WIFEXITED(int stat_val) : 리턴값이 0이 아닌 true 값이면 자식프로세스가 정상적으로 종료됐다는 뜻

-WEXITSTATUS(int stat_val) : 자식프로세스가 보낸 리턴값을 확인할 수 있다

 

-WIFSIGNALED(int stat_val) : 자식프로세스가 singal에 의해서 종료가 됐는지 확인하는 것, true값을 리턴 받을 경우 signal에 의해 종료가 된 것임

-WTERMSIG(int stat_val) : 몇 번 signal에 의해서 종료가 됐는지를 확인할 수 있음

 

-WIFSTOPPED(int stat_val) : 0이 아닌 값을 리턴 받았을 경우에 종료된 것이 아니라 stop 됐다는 것을 확인할 수 있음

-WSTOPSIG(int stat_val) : 어떤 signal에 의해서 종료됐는지 확인할 수 있음

모든 자식프로세스를 기다리고 싶을 때

  • while(r_wait(NULL) > 0);
  • r_wait()

exec() 함수

exec() 함수를 이용하면  fork() 함수에서 부모프로세스와 자식프로세스의 코드가 같아야만 하는 문제점을 해결할 수 있음

즉 exec() 함수는 자식프로세스가 부모프로세스와는 완전히 다른 프로그램을 실행하는 프로세스로 변경해 줌(현재 프로세스를 새로운 코드를 실행하는 프로세스로 덮어써버림), fork() 함수처럼 새로운 프로세스를 생성하는 것이 아니다.

즉, exec() 함수를 호출할 경우 같은 프로세스에서 새로운 코드가 로드되고 처음부터 실행됨(새로운 프로세스 생성 x)

 

자식프로세스와 부모프로세스는 리턴값을 받았지만 exec함수는 정상적으로 실행될 경우 리턴을 받지 않는다.

execl() 함수

execl(const char *path, const char *arg0,..., const char *argn, (char*) 0);

첫 번째 파라미터에는 내가 실해하려고 하는 실행파일의 경로명, 두 번째는 실행하고자 하는 파일명을 포함한 argument들을 별도의 파라미터로 쭉 나열하고 argument가 끝이라고 하는 null 캐릭터를 마지막에 반드시 넣어주어야 함.

언제 execl함수를 쓰면 좋을까?

내가 실행할 실행파일의 argument가 미리 결정이 되어있을 때 간단하게 사용하기 편함

execlp() 함수

execlp(const char* file, const char*arg0,..., const char *argn, (char*) 0);

첫 번째 파라미터에 경로명이 아니더라도 파일명만 있어도 가능, 즉 끝이 p로 끝나는 함수들은 실행파일 경로가 아니더라도 파일명만 줘도 된다. 

 

execle() 함수

execle(const char *path, const char *agr0,..., const char *argn, (char*) 0, char *const envp []);

마지막에 환경 변수 파라미터를 추가적으로 줄 수 있게 함

 

execv() 함수

execv(const char *path, char *const argv []);

argument array를 만들어 두고 그 array를 두 번째 파리미터로 지정.

execl에서는 쭉 나열했지만 execv함수에서는 array로 만들어서 파라미터로 넘겨준다는 차이점이 있음 

미리 실행하려고 하는 실행파일에 대한 argument에 대한 내용을 알지는 못하지만 execv함수를 호출을 하는 부분을 작성할 수 있음

execl함수와 가장 큰 차이점은 execv함수는 파라미터의 개수가 고정이 돼있다는 것 

execvp함수와 execve함수 

execvp함수의 동작은 execlp함수의 동작과 유사함

execve함수의 동작은 execle함수의 동작과 유사함


exit() 함수

atexit() 함수 : 프로세스가 종료되기 전에 마지막으로 수행해야 할 함수들이 있다면 atexti() 함수로 그 함수를 등록하는 것  

atexit()함수 예제


Background Process and Daemons

 

interrupt character

ctrl-c를 입력해서 for ground에서 돌아가는 프로세스를 인터럽트 시킬 수 있음 

for ground 프로세스는 인터럽트 캐릭터를 받을 수 있지만 백그라운드는 그렇지 못함. 

ex) 스탠더드 인풋을 받는 wc 명령어를 실행해서 forground프로그램을 돌리자(wc는 단어의 개수를 세주는 명령어이기 때문에 단어를 입력을 해야 함). 여기서 ctrl-c로 인풋을 하지 않고 바로 인터럽트를 시킴. 

 

Background process

ctrl-c가 먹히는 이유는 forground프로세스이기 때문인데 shell 상에서 실행하는 명령어를 forground가 아닌 background프로세스로 실행할 수 있다. 어떻게? 명령어 끝에 &입력하기. 백그라운드로 돌아가기 때문에 그 프로세스는 사용자의 인풋을 받지 않음 즉, ctrl-c를 아무리 눌러도 먹히지 않음 

 

Daemon

백그라운드 프로세스의 일종으로 잠깐 실행되는 게 아니라 계속 백그라운드로 돌아가는 프로그램