프로세스(Process)란?
- 실행중인 프로그램
- 특징
- 일의 실행하기 위해 메모리, CPU 시간, 입출력 등의 시스템 자원을 필요로 함
- 운영체제(OS)가 이러한 자원을 관리하면서 프로세스를 생성하고 실행
- 각각의 프로세스는 독립적으로 메모리 공간이 격리되며 서로 다른 프로세스 간에 자원 공유가 일어나지 않음
- 단일 프로그램이 여러 프로세스를 생성 할 수 있다. => 여러 프로세스들이 동일 프로그램으로 실행 가능 - 프로그램과 프로세스 비교
- 프로그램 : 디스크에 저장된 수동적 파일
- 프로세스 : 실행중인 프로그램의 인스턴스(instance), 능동적
=> 운영체제는 이러한 정보를 이용하여 프로세스 간의 우선 순위를 결정하여, 자원을 할당, 프로세스 간의 통신도 관리 - 프로그램이 프로세스가 되는 과정
(1) 보조기억장치에 저장된 프로그램을 실행 시 운영체제가 해당 프로그램을 메모리에 로드
(2) 새로운 프로세스가 생성되어 메모리에 올라서 실행, 프로그램이 실행 중인 상태를 나타냄
(3) 새로 생성된 프로세스는 운영체제로부터 자원을 할당받고, 초기상태로 초기화
(4) 프로세스가 운영체제에 의해 CPU시간 할당받고, 해당 프로세스 코드가 실행
(5) 프로세스 실행 종료 시 사용한 자원 운영체제에 반환 - 프로세스 작업 관리자에서 확인해보기
- 프로세스의 종류
- 포그라운드 프로세스 (foreground process)
- 사용자가 볼 수 있는 공간에서 실행되는 프로세스
- 포그라운드 프로세스를 백그라운드 프로세스로 전환 가능
- 백그라운드 프로세스 (background process) : 사용자가 볼 수 없는 공간에서 실행되는 프로세스
(1) 사용자와 직접 상호작용이 가능한 백그라운드 프로세스
(2) 사용자와 상호작용하지 않고 그저 정해진 일만 수행하는 프로세스
-> 데몬 : 백그라운드에서 프로세스 상태로 있다가, 접근요청이 있을 때만,이를 처리해 주는 일종의 서버 프로세스
- 포그라운드 프로세스 (foreground process)
- 프로세스 제어 블록(Process Control Block, PCB)
- 운영체제에서 프로세스를 관리하기 위한 자료구조
- 각각의 프로세스 마다 하나의 PCB가 존재, 이 PCB에는 해당 프로세스의 상태정보, 번호, 메모리 할당정보 등이 저장
=> 프로세스에 달린 태그와 같은 정보라고 볼 수 있다.
- 모든 프로세스는 실행을 위해 CPU가 필요하지만 CPU 자원은 한정되어 있음
=>프로세스들은 자신의 차례에 정해진 시간만큼 CPU를 이용
- PCB에 저장된 정보를 기반으로 운영체제는 프로세스를 스케줄링하고 관리
- PCB는 프로세스 생성 시 커널 영역에 생성되며, 해당 프로세스가 종료될 때 폐기
- PCB는 프로세스 상태 변화나 시스템 호출 등의 이벤트가 발생할 때마다 업데이트됨
- PCB에 담기는 대표적인 정보들 (운영체제 마다 약간의 차이가 있을 수 있다.)
(1) 프로세스 ID (=PID) // 특정 프로세스를 식별하기 위해 프로세스 마다 할당되는 고유한 값
(2) 레지스터 값
(3) CPU 스케줄링 정보
(4) 메모리 할당 정보 (프로세스의 주소 공간 정보)
(5) 사용한 파일과 입출력 상태 정보
(6) 자원 사용 정보 (할당된 CPU 시간, 메모리, 입출력장치 등)
(7) 프로세스 상태 (Ready, Running, Blocked 등)
- PCB에 담기는 대표적인 정보들 (운영체제 마다 약간의 차이가 있을 수 있다.)
- 프로세스의 메모리 영역
1. 커널영역(kernel space) : 운영체제 커널이 동작하는 영역
- 모든 프로세스의 상위에 존재, 이들을 보호하거나 제어한다.
- 하드웨어 자원 관리, 시스템 호출 처리, 프로세스와 스레드를 관리
- 운영체제 기능을 대부분 담당
- 커널은 시스템 하드웨어 자원에 접근하므로 사용자 영역과 분리 (잘못된 접근 방지)
2. 사용자 영역
- 사용자 영역은 크게 4가지로 나뉜다.
(1) 코드 영역
- 실행할 수 있는 코드, 기계어로 이루어진 명령어 저장
- 데이터가 아닌 CPU가 실행할 명령어가 담기기에 read-only영역임 (쓰기 불가능)
- 실행 파일이 로드될 때 읽혀지며, 읽힌 코드는 CPU가 해석하고 실행
(2) 데이터 영역
- 실행 파일의 전역 변수와 정적(static) 변수가 저장되는 영역
- 잠깐 썼다가 없앨 데이터가 아닌 프로그램이 실행되는 동안 유지할 데이터를 저장
(3) 스택 영역
- 프로그램 실행 중 임시로 사용되는 데이터들이 저장되는 영역
- 매개변수, 지역변수
- 함수 호출과 반환 과정에서 임시로 사용, 함수 인자나 지역변수 등이 스택에 저장됨
- LIFO 구조로 데이터 저장
(4) 힙 영역
- 프로그래머가 직접 할당할 수 있는 저장공간
- 동적 메모리 할당하기 위해 사용되는 영역
- 프로그램에서 직접적으로 사용되지는 않고 대부분 프로그램에서 사용하는 라이브러리나 프레임워크에서 동적으로 메모리를 할당할 때 사용.
- 메모리 누수에 주의 => 반드시 할당된 메모리를 사용한 후에 해제해주는 작업 필요
프로세스와 스레드(Thread)
- 스레드(Thread) : 프로세스 내에서 실행되는 작은 실행 단위
- 영어 뜻 자체로는 이야기의 흐름 또는 줄기 등을 의미한다.
- 하나의 프로세스 내에서 더 작은 단위들로 각각 독립적으로 실행, 각각 제어가 가능한 흐름
- 일명 - 프로세스와 스레드의 차이?
- 프로세스를 fork하면, 원래의 프로세스와 똑같은 변수&코드를 가지는 일종의 복사본(저장된 메모리 주소를 제외한 모든 것이 동일)이며 그만큼 많은 자원이 요구된다. fork한 프로세스는 일종의 복제본이기에 같은 프로세스가 여러개 실행되었을 때, 한 프로세스가 에러가 나도 영향을 받지 않는다(독립적 실행)
- 멀티 스레드는 각기 다른 스레드 ID, 스택, 레지스터 값을 가지며 나머지는 프로세스가 갖는 자원을 함께 공유한다. 또한 하나의 프로세스에서 한 스레드가 에러나면 나머지 스레드가 영향을 받는다.
프로세스 | 완전한 하나의 프로그램이 실행되는 것 | 메모리 상에서 독립적인 공간을 가짐 | 프로세스는 다른 프로세스 메모리에 직접 접근 불가능 (IPC 기법을 사용하여 통신) | 여러 개의 프로세스가 동시 실행될 때, 상대적으로 자원 효율성이 낮아진다. |
스레드 | 하나의 프로세스 내에서 실행되는 실행 흐름 | 각 스레드는 프로세스가 할당받은 메모리 공간을 함께 공유 | 스레드는 다른 스레드의 메모리 공간에 직접 접근 가능 | 단일 프로세스 내에서 여러 작업을 동시에 병렬처리 가능 => 시스템 자원 효율적으로 사용 가능하며, CPU성능 향상 기대 가능 |
Python으로 프로세스 다루기
파이썬에서 멀티 프로세싱과 멀티 스레드를 다루기 위해서는 각각 multiprocessing 모듈과 threading 모듈이 필요
1. 파이썬으로 프로세스 PID값 출력하기
import os
print("hello, os")
print("my pid is", os.getpid())
# 출력결과
# hello, os
# my pid is 11704 (PID값은 실행마다 달라짐)
os.getpid()를 사용하여 프로세스의 PID값을 확인할 수 있다. PID값은 운영체제가 그때그때 부여해주는 값이라서 터미널 실행을 여러 번 눌러보면 그때마다 다른 값이 출력되는 결과를 볼 수 있다.
2. 자식 프로세스 생성
from multiprocessing import Process
import os
def foo():
print('child process', os.getpid())
if __name__ == '__main__': # 현재 모듈이 메인 모듈인 경우 아래 코드를 실행
print('parent process', os.getpid())
child = Process(target=foo).start() # foo 함수를 실행하는 자식 프로세스를 생성하고 시작
3. 동일한 작업을 하는 자식 프로세스들 만들기
from multiprocessing import Process
import os
def foo():
print('child process', os.getpid())
if __name__ == '__main__':
print('parent process', os.getpid())
child1 = Process(target=foo).start()
child2 = Process(target=foo).start()
child3 = Process(target=foo).start()
똑같은 코드를 실행하는 자식 프로세스들을 만들었다. 자식 프로세스들의 PID값은 당연히 다 다른 값으로 출력된다.
=> 프로세스들이 독립적인 실행하는 것을 알 수 있다.
4. 각기 다른 작업을 하는 자식 프로세스들 만들기
from multiprocessing import Process
import os
def foo():
print('This is foo')
print("my pid is", os.getpid())
def bar():
print('This is bar')
print("my pid is", os.getpid())
def baz():
print('This is baz')
print("my pid is", os.getpid())
if __name__ == '__main__':
print('parent process', os.getpid())
child1 = Process(target=foo).start()
child2 = Process(target=bar).start()
child3 = Process(target=baz).start()
실행결과는 아래와 같다.
parent process 10040
This is foo
my pid is 12936
This is foo
my pid is 6812
This is foo
my pid is 8236
위 예시에서의 세 함수는 각기 다른 프로세스로서 동시에 진행된다.
만약 이 파이썬 프로그램이 여러가지 기능을 하는 프로그램이라고 가정할 시 프로세스를 잘 이해하고 적용시킨다면 각기 다른 작업을 동시에 수행하는 소스 코드를 작성할 수 있다. 하지만 멀티 프로세싱은 많은 메모리와 CPU 자원을 필요로 하는 것 또한 알아두고 진행해야 한다.
5. 멀티 스레딩 만들기
import threading
import os
def foo():
print('This is foo')
def bar():
print('This is bar')
def baz():
print('This is baz')
if __name__ == '__main__':
thread1 = threading.Thread(target=foo).start()
thread2 = threading.Thread(target=bar).start()
thread3 = threading.Thread(target=baz).start()
실행 결과는 다음과 같다
This is foo
my pid is 916
This is bar
my pid is 916
This is baz
my pid is 916
Python threading 모듈을 이용해 세 함수를 별도의 스레드에서 병렬로 실행한다.
세 함수는 각각 다른 스레드에서 실행되기 땜누에, 동시에 세 가지 작업을 처리할 수 있다. 따라서 멀티 스레딩을 활용한 병렬처리가 가능함을 알 수 있다.
참고 출처 : http://www.ktword.co.kr/index.php (정보통신기술용어해설)
참고 강의 : 내배캠 선발대 강의 및 https://github.com/kangtegong/self-learning-cs/blob/main/process/process_python.md#python%EC%9C%BC%EB%A1%9C-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EB%8B%A4%EB%A3%A8%EA%B8%B0