프로그램 & 프로세스
프로그램이란?
어떤 작업을 위해 실행할 수 있도록 저장장치에 저장된 정적인 상태의 파일
프로세스란?
실행을 위해 메모리에 올라온 동적인 상태로, 컴퓨터에서 연속적으로 실행되고 있는 컴퓨터 프로그램 즉, 지금 실행 중인 프로그램을 의미
-
- Code, Data, Stack, Heap 의 독립된 메모리 영역을 할당 받음
- Code: 프로그램을 실행시키는 실행 파일 내의 본문이 기술된 공간, 읽기 전용
- Data: 전역 변수, static 변수들이 위치하는 공간, 읽기와 쓰기 가능
- Heap: 동적 할당을 위한 메모리 영역 (malloc(), new 등)
- Stack: 지역 변수, 파라미터가 위치하는 공간 / 프로세스 내에서 함수를 호출하면 함수를 수행하고 원래 프로그램으로 되돌아올 위치를 저장하는 곳
- 하나의 프로세스 당 최소 한 개의 스레드(main 스레드)를 가지고 있음
- 기본적으로 프로세스끼리 다른 프로세스의 메모리에 직접 접근할 수 없다.
- Code, Data, Stack, Heap 의 독립된 메모리 영역을 할당 받음
프로세스 제어 블록(PCB, Process Control Block)
프로세스에 대한 정보를 담고 있는 자료구조
- 구성
- PID
- 프로세스를 식별하기 위해 부여된 프로세스 식별 번호
- 프로세스 상태
- 실행, 대기 등의 프로세스 상태 저장
- 프로그램 카운터(PC)
- CPU가 다음으로 실행할 명령어가 저장된 메모리 주소를 가리키는 값
- 프로세스 우선순위
- 커널 프로세스 > 사용자 프로세스
- 각종 레지스터 정보
- 이전에 사용한 레지스터의 중간 값 보관
- 메모리 관리 정보
- 프로세스 메모리 위치 정보, 경계 레지스터 & 한계 레지스터 값
- 부모&자식 프로세스
- 부모 프로세스(PPID)
- 자식 프로세스(CPID)
- PID
💡 프로세스 = 프로그램 + 프로세스 제어 블록(PCB) 프로그램 = 프로세스 - 프로세스 제어 블록(PCB)
프로세스의 상태
- 활성 상태
- 생성 상태
- 프로세스가 메모리에 올라와 실행 준비를 완료한 상태.
- PCB 를 할당 받은 상태.
- 준비 상태
- 생성된 프로세스가 CPU를 얻을 때까지 기다리는 상태
- PCB는 준비 큐에서 대기
- 한 번에 하나의 프로세스만 실행할 수 있는 경우 실행 순서가 될 때까지 준비 상태에서 기다려야 한다.
- 실행할 프로세스를 선정하는 일은 CPU 스케줄러가 담당 (큐를 몇 개 운영할지, 큐에 있는 어떤 프로세스의 프로세스 제어 블록을 실행 상태로 보낼지 등 결정)
- 실행 상태
- 준비 상태에 있는 프로세스 중 하나가 CPU를 얻어 실제 작업을 수행하는 상태
- 일정 시간(타임 슬라이스)동안 CPU를 사용할 권리를 가지고, 만약 시간이 다 지나도 끝나지 않았다면 준비 상태로 돌아와 다음 차례를 기다린다.
- 실행 중 입출력을 요청하면 대기 상태로 이동하고 CPU 스케줄러는 새로운 프로세스를 실행 상태로 가져온다.(디스패치)
- 완료 상태
- 프로세스가 종료되는 상태
- 실행 상태의 프로세스가 주어진 시간 동안 작업을 마치면 완료 상태로 진입
- 사용한 코드와 데이터 삭제, PCB 폐기
- 대기 상태
- 운영체제의 효율성을 고려하여 만들어진 상태
- 프로세스가 입출력을 요청하면 입출력이 완료될 때까지 기다리는 상태
- 입출력이 완료되면 인터럽트가 발생하고, PCB를 준비 상태로 이동시킨다.
- 생성 상태
- 비활성 상태
- 휴식 상태
- 프로세스가 작업을 일시적으로 쉬고 있는 상태
- 프로세스가 메모리에 있으나 멈춘 상태
- 보류 상태 (일시 정지 상태)
- 프로세스가 메모리에서 잠시 쫓겨난 상태
- 스왑 영역에 보관
- 메모리에서 쫓겨난 데이터가 임시로 보관되는 곳
- 휴식 상태
프로세스 상태 전이
- Admitted (생성 → 준비)
- Dispatch (준비 → 실행)
- Interrupt, Time runout (실행 → 준비)
※ 제출(Submit): 작업을 처리하기 위해 사용자가 작업을 시스템에 제출한 상태
※ 접수(Hold): 제출된 작업이 스풀 공간인 디스크 할당 위치에 저장된 상태
문맥 교환
- CPU를 차지하던 프로세스가 나가고 새로운 프로세스를 받아들이는 작업
- 두 프로세스의 프로세스 제어 블록(PCB)을 교환하는 작업
- 프로세스가 자신에게 주어진 시간을 다 사용했을 경우, 인터럽트가 걸렸을 경우 등에 발생
- 실행 상태를 벗어나는 PCB에는 지금까지의 작업 내용 저장
- 실행 상태가 되는 PCB의 내용으로 CPU가 다시 세팅
타임 슬라이스와 문맥 교환
- 타임 슬라이스는 되도록 작게 설정하되 문맥 교환에 걸리는 시간을 고려하여 설정
프로세스 연산
프로세스 구조
- 코드 영역
- 프로그램의 본문이 기술된 곳
- 텍스트 영역
- 읽기 전용으로 처리
- 데이터 영역
- 코드가 실행되면서 사용하는 변수나 파일 등의 각종 데이터를 모아 놓은 곳이다.
- 기본적으로 읽기와 쓰기가 가능
- 스택 영역
- 운영체제가 프로세스를 실행하기 위해 부수적으로 필요한 데이터를 모아 놓은 곳
프로세스의 생성과 복사
실행 중인 프로세스로부터 새로운 프로세스를 복사. 실행하던 프로세스는 부모 프로세스, 새로 생긴 프로세스는 자식 프로세스
- 프로세스의 생성 속도가 빠르다. 2. 추가 작업 없이 상속 가능 3. 시스템 효율적 관리
fork( ) 시스템 호출
실행 중인 프로세스로부터 새로운 프로세스를 복사하는 함수다. 이때 실행하던 프로세스는 부모 프로세스, 새로 생긴 프로세스는 자식 프로세스로서 둘은 부모-자식 관계가 된다.
장점
- 프로세스의 생성 속도가 빠르다.
- 추가 작업 없이 자원을 상속할 수 있다.
- 시스템 관리를 효율적으로 할 수 있다.
- 자식 프로세스 종료 시 정리를 부모 프로세스에게 맡김
과정
- pid = fork() 문을 만나면 똑같은 자식 프로세스를 하나 생성
- fork() 문은 부모 프로세스에 0보다 큰 값(자식 pid)을 반환하고 자식 프로세스에 0을 반환
exec( ) 시스템 호출
이미 만들어진 프로세스의 구조를 재활용. 구조는 그대로 둔 채 내용만 바꾼다.
부모-자식 관계, pcb, 메모리 영역 그대로 사용
pid, ppid, cpid 그대로, 코드, 데이터, 스택 영역 리셋
이미 만들어진 프로세스의 구조를 재활용하는 것. 프로세스는 그대로 둔 채 내용만 바꾼다.
- 이미 만들어진 PCB, 메모리 영역, 부모-자식 관계 그대로 사용 가능
과정
- 코드 영역에 있는 기존 내용이 지워지고 새로운 코드로, 데이터 영역은 새로운 변수로 채워지고, 스택 영역은 리셋
- PCB 중 PID, PPID, CPID, 메모리 관련 사항 등은 변하지 않지만 프로그램 카운터 레지스터 값을 비롯한 각종 레지스터와 파일 정보가 모두 리셋
프로세스 계층 구조
init 프로세스가 루트 프로세스
fork를 이용해서 login 프로세스 여러 개 생성
exec를 이용해서 login 프로세스 구조 가져와 shell프로세스로 바꾼다.
용이한 자원 회수
유닉스의 프로세스 계층 구조
커널이 메모리에 올라와 부팅되면 커널 관련 프로세스를 여러 개 만들고, 그 중 init 프로세스가 전체 프로세스의 출발점
모든 프로세스는 init 프로세스의 자식 (login, shell) 이 된다.
장점
- 여러 작업의 동시 처리
- fork( ) 를 이용하여 login 프로세스를 여러 개 만들어 사용자 동시 처리
- exec( ) 를 이용하여 login 프로세스를 shell 프로세스로 바꾸어 사용자 명령을 기다림
- 용이한 자원 회수
- 프로세스 간 책임 관계가 분명해져 시스템을 관리하기 수월하다.
- 프로세스가 사용하던 자원을 회수할 때 편리
고아 프로세스 & 좀비 프로세스
고아 프로세스
- 자식 프로세스가 종료되기 전에 부모 프로세스가 먼저 종료되는 경우 자식 프로세스는 돌아갈 곳이 없는데, 이 때 자식 프로세스를 고아 프로세스라고 한다.
좀비 프로세스
- 자식 프로세스가 종료되었는데도 부모 프로세스가 뒤처리를 하지 않을 때의 자식 프로세스를 좀비 프로세스라고 한다.
스레드
프로세스의 코드에 정의된 절차에 따라 CPU에 작업 요청을 하는 실행 단위
data, heap, code는 공유, stack만 개별적으로 가지고 있음
프로세스의 코드에 정의된 절차에 따라 CPU에 작업 요청을 하는 실행 단위
- 특징
- 프로세스끼리는 약한 결합, 스레드끼리는 강한 결합
- 스레드는 프로세스 내에서 각각 stack만 따로 할당받고 code, data, heap 영역은 공유
- 프로세스 내의 주소 공간이나 자원들을 같은 프로세스 내의 스레드끼리 공유하지만, 서로 다른 프로세스끼리는 메모리에 직접 접근할 수 없다.
💡 stack만 따로 할당 받는 이유?
stack은 지역 변수, 파라미터, 되돌아갈 주소값 등을 저장하는 메모리 공간이기 때문에, 독립적인 스택을 가졌다는 것은 독립적인 함수 호출이 가능하다는 것이고, 독립적인 실행 흐름이 추가된다.
스레드 생명 주기
- NEW
- 객체 생성
- 스레드가 만들어진 상태
- 아직 start() 메소드가 호출되지 않은 상태
- Runnable
- 실행 대기
- 실행 상태로 언제든지 갈 수 있는 상태
- 스레드 객체가 생성된 후에 start() 메서드를 호출하면 이 상태로 이동
- Running
- 실행 상태
- 스레드 스케줄러에서 이동
- 스케줄려는 runnable 상태의 스레드 중 하나를 선택하여 실행
- blocked
- 일시 정지
- 사용하고자 하는 객체의 lock이 풀릴 때까지 기다리는 상태
- terminated
- 실행을 마친 상태 (종료)
- run() 메소드 완료 시 스레드가 종료되면 그 스레드는 다시 시작할 수 없다.
멀티 프로세스 & 멀티 스레드
멀티 프로세스는 하나의 응용 프로그램을 여러 개의 프로세스로 구성하여 하나의 작업을 처리하도록 하는 것.
장점: 하나의 프로세스에 문제가 발생해도 다른 프로세스에 영향을 끼치지 않는다.
단점: 통신하기 어려움. 오버헤드 발생
멀티 프로세스
하나의 응용 프로그램을 여러 개의 프로세스로 구성하여 각 프로세스가 하나의 작업 처리하도록 하는 것
- 장점
- 여러 개의 자식 프로세스 중 하나에 문제가 발생하면 그 자식 프로세스만 죽는 것 이상으로 다른 영향이 확산되지 않는다.
- 단점
- 문맥 교환 중 무거운 작업이 수행되어 오버헤드 발생
- CPU 레지스터 교체, RAM과 CPU 사이 캐시 메모리 초기화 등 자원 부담이 크다.
- 프로세스 사이 공유 메모리가 없어 캐쉬에 있는 모든 데이터를 모두 리셋하고 정보를 불러 와야 함
- 프로세스 사이의 복잡한 통신 기법
- IPC 기법 사용 (워드 - 프린터)
- 프로세스는 각각의 독립된 메모리 영역을 할당 받았기 때문에 하나의 프로그램에 속하는 프로세스들 사이의 변수를 공유할 수 없다.
- 문맥 교환 중 무거운 작업이 수행되어 오버헤드 발생
멀티 스레드
프로세스를 여러 개의 스레드로 분할하여 작업할 수 있도록 하는 기법
- 응답성 향상
- 자원 공유
- 효율성 향상
- 다중 CPU 지원
프로세스 내 작업을 여러 개의 스레드로 분할하는 기법
- 장점
- 시스템 자원 소모 감소
- 프로세스 생성 시 자원 할당하는 시스템 콜이 줄어 자원 효율적 관리 가능
- 시스템 처리량 증가
- 스레드 간 데이터를 주고 받는 것이 간단해지고 세스템 자원 소모가 줄어듦
- 스레드 사이의 작업 량이 작아 문맥 교환이 빠르다
- 간단한 통신 방법으로 인한 프로그램 응답 시간 단축
- 스레드는 프로세스 내의 stack 영역 제외 모든 메모리를 공유하기 때문에 통신의 부담이 적다.
- 응답성 향상
- 한 스레드가 입출력으로 인해 작업이 진행되지 않더라도 다른 스레드가 작업을 계속하여 사용자의 작업 요구에 빨리 응답할 수 있다.
- 자원 공유
- 한 프로세스 내에서 독립적인 스레드를 생성하면 프로세스가 가진 자원을 모든 스레드가 공유하게 되어 작업을 원활하게 진행
- 효율성 향상
- 여러 개의 프로세스 생성할 필요 없다.
- 다중 CPU 지원
- 2개 이상의 CPU를 가진 컴퓨터에서 멀티스레드를 사용하면 다중 CPU가 멀티스레드를 동시에 처리하여 사용량 증가, 처리 시간 단축
- 시스템 자원 소모 감소
- 단점
- 하나의 스레드에 문제가 발생하면 전체 프로세스가 영향 받는다.
💡 현대 컴퓨터의 운영체제에선 멀티 프로세스를 지원하고 있지만 멀티 스레드를 기본으로 하고 있다.
멀티 태스킹
운영체제가 CPU에 작업을 줄 때 시간을 잘게 나누어 배분하는 기법 . 시분할 시스템이라고도 한다.
멀티 프로세싱
CPU를 여러 개 사용하여 여러 개의 스레드를 동시에 처리하는 작업 환경
CPU 멀티스레드
명령어 병렬 처리 기법
한 번에 하나씩 처리해야 하는 스레드를 잘게 쪼개 동시에 처리하는 명령어 병렬 처리 기법. 하나의 CPU에서 여러 스레드를 동시에 처리
멀티스레드 모델
사용자 스레드와 커널 스레드의 대응 방식
사용자 스레드 (1 to N 모델)
- 사용자 레벨에서 라이브러리를 사용하여 스레드를 구현
- 커널이 멀티스레드를 지원하지 않을 때 사용하는 방법으로 초기의 스레드 시스템에서 사용되었다.
- 커널이 지원하는 스케줄링이나 동기화 같은 기능을 대신 구현
- 커널 입장에서는 하나의 프로세스처럼 보인다.
- 사용자 프로세스 내에 여러 개의 스레드와 커널 스레드 하나와 연결된다.
- 라이브러리가 직접 처리하기 때문에 문맥 교환이 필요하지 않다.
단점
- 커널 스레드가 입출력 작업을 위해 대기 상태에 들어가면 모든 사용자 스레드가 모두 대기
- 한 프로세스의 타임 슬라이스를 여러 스레드가 공유하기 때문에 여러 개의 CPU를 동시에 사용할 수 없다.
- 보안에 취약하다.
커널 스레드 (1 to 1 모델)
- 커널이 멀티스레드를 지원하는 방식
- 하나의 사용자 스레드가 하나의 커널 스레드와 연결
- 독립적으로 스케줄링된다. (특정 스레드 대기 상태여도 다른 스레드 작업 계속 할 수 있다)
- 커널이 제공하는 모든 기능 사용 가능
장점
- 멀티 CPU 사용 가능
- 하나의 스레드가 대기 상태에 있어도 다른 스레드는 작업을 계속할 수 있다.
- 보안에 강하다.
단점
- 문맥 교환 시 오버헤드 때문에 느리게 작동
멀티레벨 스레드 (M to N 모델)
- 사용자 스레드 + 커널 스레드
- 커널 스레드의 개수가 사용자 스레드보다 같거나 적다.
장점
- 대기 상태에 들어가면 다른 커널 스레드가 대신 작업하여 유연한 작업 처리 가능
단점
- 문맥 교환 시 오버헤드 발생