2024. 4. 2. 21:28ㆍCS/Operating System
Program vs Process vs Processor vs Task, Job
Program : 순서 있는 명령어의 집합
- 프로그램은 storage(하드디스크)에 있다. 그게 실행되면 프로세스가 된다.
Process : 프로그램이 실행 중인 것
- 프로세스는 메모리에 있다.
Processor : CPU
- 프로세서는 메모리에 있는 프로세스의 코드를 한줄한줄 가져와서 실행한다.
Task, Job
- Task와 Process는 혼용해서 사용한다. 그런데 Job은 좀 더 큰 개념이다. job은 하나의 기능 느낌이다. DB를 액세스 하는 프로세스, 계산하는 프로세스 등등의 프로세스를 합해서 입금이라는 job이 된다. Job과 Task도 혼용해서 사용하기도 한다.
Process Concept
프로세스는 실행중인 프로그램의 instacne이다. 하나의 실행파일을 여러 개 띄워서 실행시킬 수 있다는 의미에서 인스턴스라고 얘기한다.
실행 중이라는 말은 다른 말로 flow of control을 가지고 있다고도 한다.
프로세스는 dynamic 하고 active 한 entity이다.
프로세스는 운영체제가 스케줄링하는 기본적인 단위이다. 프로세스는 여러 개의 프로세스가 한 번에 돌 수 있으므로 이름을 사용하지 않고 process ID를 사용해서 구분한다. a.exe라고만 관리하면 10개의 a.exe를 구분할 수 없다.
프로세스는 CPU contexts(registers), OS resources(memory, open files 등등), PID, state, owner 등등을 포함한다.
pocess in memory
a.exe를 storage에 기계어 코드로 저장해서 a.exe를 프로그램에서 프로세스로 실행시키면 어떻게 될까?
프로그램은 내부에 code와 data들을 가지고 운영체제는 프로세스를 위해 가상의 메모리 공간을 준비한다. 물론 실제로는 운영체제가 가상의 address를 메모리의 피지컬 한 주소로 바꿔준다.
프로그램을 실행시키면 storage의 데이터를 그대로 복사해서 메모리에 옮긴다. 메모리의 가상 영역에 올라가게 된다.
메모리의 구성을 살짝 보자면 배열, 변수 이런게 메모리의 스택 공간에 쌓인다. 힙에는 다이나믹하게 어로케이션 된 malloc 같은 게 위로(높은 어드레스 쪽으로) 쌓인다. free 시키면 해제된다. 잘 보면 맨 위에 1기가(32bit기준)정도는 커널이 가지고 있는다. system call 같이 커널이 사용하는 코드가 커널 버츄얼 메모리에 매핑되어 있다. 밑에서 코드가 막 실행되다가 open 같은 걸 부르면 커널 버츄얼 메모리에서 찾아서 실행한다.
그런데! 커널 영역에 있는 코드를 실행한다는 건 프로텍션 바운더리를 넘어가는 일이다. 저기있는 메모리는 애플리케이션 영역에서는 접근할 수 없다.
Process Creation
Process hierachy
디렉터리 구조를 보면 트리 형태로 되어있다. 그처럼 프로세스도 무슨 프로세스 밑에 자식프로세스가 존재하는 방식으로 hierarchy를 가지고 있다. 여기 굉장히 중요한 말이 나온다. 하나의 프로세스는 다른 프로세스를 만들 수 있다. 그렇다 보니 항상 계층적 구조가 만들어진다.
UNIX에선 이런 자식관계의 프로세스 트리를 프로세스 그룹이라 한다.
윈도우에서는 프로세스 hierarchy를 사용하지 않는다고 얘기한다. 사실 내부적으로는 hierarchy를 사용하지만 큰 의미는 없다.
Process Creation
프로세스를 새로 만드는 것은 중요한 일이고, 애플리케이션 레벨에서는 불가능하므로 시스템 콜을 통해 운영체제에 부탁해야 한다. 보통은 fork(), CreateProcess()를 사용하고 여기선 fork()라고 할 예정이다. 프로그램 내부에서 fork()를 부르면 새로운 프로세스가 만들어진다. GUI 쉘에서 메모장을 켜면 메모장 process를 만드는 것이다.
프로세스는 트리구조이므로 가장 최상단에 위치한 최고 조상은 init 프로세스이고 PID는 보통 1번이다. 운영체제가 부팅하면서 init 프로세스를 만들고 나머지는 전부 init이 fork를 써서 만들어낸 프로세스이다. init을 제외하면 모든 프로세스는 프로세스가 만든다.
Resource sharing
새로운 프로세스를 만들면 부모와 자식은 리소스를 공유하게 된다. 여기서의 리소스는 운영체제가 관리하는 모든 데이터가 다 포함된다. 프로세스가 실행하는 user id(그 사람의 권한 포함), 프로세스가 가지고 있던 파일들 등을 포함한다.
Execution
부모와 자식이 만들어져서 실행하는 과정을 보면 부모는 둘이 동시에 동작할 수도 있고 부모가 끝나기를 자식이 기다릴 수 있다.
Address space
프로세스가 새로 만들어지면 새로운 가상 커널 메모리 공간이 필요할 텐데, 아까 사진에서 본 가상의 커널 메모리 공간이 어떻게 만들어지냐면 그대로 복제해서 자식에게 그대로 전달해 준다. 물론 별개의 새로운 메모리이다.
프로그램을 실행시킨다는 건 프로세스를 만든다는 것이고 이게 어떻게 되냐면 쉘로부터 child 프로세스를 새로 만들어내고 특정한 역할(프로그램의 역할)을 하도록 세팅하는 것이다. 프로그램이 어떻게 실행되는지 잘 이해해야 한다.
Process Termination
어떤 프로세스가 종료된다는 것은 4가지 케이스로 나누어진다.
- Normal exit
- return 0;
- 보통 0을 리턴하는데, 왜 그러냐면 0이라는 의미는 보통 정상 종료라는 뜻이다. 이 리턴값이 에러 코드로, 1이 리턴되면 잘못 끝났다고 알려주는 식으로 parent 프로세스가 정의해서 사용할 수 있다.
- Error exit
- return -1;
- 보통 -1을 리턴하면 문제가 있어서 종료되었다고 여겨지고, return 0, -1; 모두 자발적인 종료이다. 부모 프로세스가 정상 종료인지 아닌지 구분하기 위해 리턴값을 구분하는 것뿐이다.
- Fatal exit
- read(fd, buf, 1000);을 했는데 buf size가 100이라 오류가 나는 경우 시스템이 자체적으로 fault를 낸다. 개발자가 의도해서 종료되는 경우는 아니다. 운영체제가 죽이는 것이다. 커널 버추얼 메모리를 포인터 해서 읽으려고 하면 protection fault가 난다.
- Killed by anotehr process
- Ctrl + C를 누르는 경우 쉘에 전달된 시그널로 종료된다. 당연히 권한이 있어야 가능하다.
fork()
- fork()라는 시스템 콜로 새로운 자식 프로세스를 만들 수 있다. 프로그램 내부에서 fork()를 부르면 child 프로세스가 만들어진다.
- fork()를 쓰게 되면 가상의 어드레스 스페이스 공간을 전부 복제해서 새로 만들어진 child 프로세스에게 던져준다. 내부에서 관리해야 하는 data structure 또한 복제해서 child에게 던져준다.
- fork()의 리턴값으로 Parent는 child의 pid를 받도록 되어있고 child는 0을 받도록 되어있다.
💡 참고) fork()를 부르면 항상 자식 프로세스가 만들어지진 않는다. 모든 함수는 실패할 수 있다. 메모리가 부족하거나 모든 프로세스를 다 만들어서 더 이상 만들 수 없거나 권한이 없는 등의 이유로 실패할 수 있다.
이 사진을 이해하는 게 굉장히 중요하다. fork()를 하는 순간 새로운 프로세스를 만들고 오른쪽 위의 똑같은 코드가 또 실행된다. 자식 프로세스는 pid값이 0이 되어 if문에 걸리고 종료된다. 부모 프로세스는 else에 걸린다.
실행 결과를 보면 같은 파일이어도 두 개가 다른 것을 확인할 수 있다. CPU 스케줄링에 따라 두 개의 프로세스가 동시에 돌면서 어떤 게 먼저 종료될지는 그때그때 다르다.
메모리를 그대로 복제해서 자식에게 준다는 것은 똑같은 코드를 그대로 준다는 것이다. 다만 예제에서는 코드가 pid 값에 의해 다른 일을 하게 된다. 이런 패턴으로 fork()를 이용해서 멀티 프로세스를 사용하곤 한다.
'CS > Operating System' 카테고리의 다른 글
[OS] 프로세스의 단점과 쓰레드의 도입, 멀티 쓰레딩의 장점 (0) | 2024.04.09 |
---|---|
[OS] Zombie & Orphan process와 PCB, Context Switch, fork (1) | 2024.04.03 |
[OS] I/O에서 데이터 전송 방식과 Timers, Protected Instruction (1) | 2024.04.01 |
[OS] 운영 체제의 내부 구조와 이벤트들 - Interrupt, Exception, Signal (0) | 2024.03.31 |
[OS] OS와 컴퓨터의 역사 (2) | 2024.03.24 |