폰 노이만이 제시한 컴퓨터 모델에 따르면 컴퓨터 시스템은 크게 CPU, 메모리, 디스크로 구성되어 있다고 볼 수 있다. 이 각각의 하드웨어를 연결했다고 해서 우리가 원하는 프로그램을 수행할 수 있는 것은 아니다. 하드웨어는 단지 그 하드웨어의 특정 기능을 수행 할 뿐인데, 이 컴퓨터라는 하드웨어 상에 프로그램들이 동작되려면 이 하드웨어들과 적절하게 데이터틀 주고 받으며 논리적인 일들을 해야만 한다.
그 하드웨어에게 일을 시키는 주체가 바로 운영체제이다.
운영체제의 목적은 처리능력 향상, 사용 가능도 향상, 신뢰도 향상, 반환 시간 단축 등에 있으며, 이런 운영체제의 기능은 여러 가지가 있다.
운영체제가 없다면, 응용 프로그램이 실행될 수 없다. 응용 프로그램은 컴퓨터를 이용해 다양한 작업을 하는 것이 목적이고, 운영체제는 응용 프로그램이 하드웨어에게 일을 시킬 수 있도록 도와준다. 하드웨어를 구성하는 일을 하는 CPU, 자료를 저장하는 RAM, 디스크 등의 시스템 자원을 관리하는 주체가 바로 운영체제이다.
[그림] 운영체제의 구조 모식도
모든 응용 프로그램이 시스템의 자원을 마음대로 사용한다면, 해커에 의한 공격에 무방비한 상태가 된다. 악의적인 목적을 가진 프로그램이 디스크의 모든 민감한 정보에 접근하거나, 내 스마트폰의 특정 앱이 카메라를 아무 때나 실행해서 촬영한다고 생각하면 끔찍하다. 따라서, 응용 프로그램은 권한에 대한 관리가 필요하다. 또한 여러 사람이 하나의 기기를 사용하는 경우에는 사용자를 관리하는 일도 매우 중요하다.
응용 프로그램이 운영체제를 통해 컴퓨터에게 일을 시키려면, 컴퓨터를 조작할 수 있는 권한을 운영체제로부터 부여받아야 한다. 권한을 부여받고 난 후에는, 운영체제가 제공하는 기능을 이용할 수 있다. 응용 프로그램이 운영체제와 소통하기 위해서는, 운영체제가 응용 프로그램을 위해 인터페이스(API)를 제공해야 한다. 응용 프로그램이 시스템 자원을 사용할 수 있도록, 운영체제 차원에서 다양한 함수를 제공하는 것을 시스템 콜
(System call)이라고 부른다.
스마트폰에서 사용자에게 어떤 디바이스(카메라 등)의 사용을 허락받는 화면을 본 적이 있을 것이다. 이와 마찬가지로, 응용 프로그램 역시 운영체제가 프린터 사용을 허가해 주지 않는다면 사용할 수 없다. 워드프로세서 프로그램이 프린터를 사용해서 인쇄하기 위해서는, 워드프로세서 프로그램은 운영체제로부터 프린터 사용에 대한 권한을 부여받아야 한다.
응용 프로그램이 프린터 사용에 대한 권한을 획득한 후에는, 프린터를 사용할 때 필요한 API를 호출해야 한다. 이 API는 시스템 콜로 이루어져 있다.
프로세스는 프로그램이 실행 중인 상태로 특정 메모리 공간에 프로그램의 코드가 적재되고 CPU 가 해당 명령어를 하나씩 수행하고 있는 상태를 의미한다. 운영 체제에서는 프로세스를 사용하여 프로그램을 수행하게 되는데 실행 중인 하나의 애플리케이션을 프로세스
라고 부른다. 사용자가 애플리케이션을 실행하면, 운영체제로부터 실행에 필요한 메모리를 할당받아 애플리케이션의 코드를 실행한다.
이때 실행되는 애플리케이션을 프로세스
라고 부른다. 예를 들어 Chrome 브라우저를 두 개 실행하면, 두 개의 프로세스가 생성된다. 이렇게 하나의 애플리케이션은 여러 프로세스(다중 프로세스)를 만들기도 한다.
[그림] 사진에서 확인할 수 있는 항목 하나하나가 전부 프로세스이다.
프로세스의 구조체에는 프로세스마다 독립적으로 관리해야 하는 유저 메모리 영역이나 프로세스가 사용하는 각종 객체들의 포인터를 관리하는 핸들 테이블을 가지고 있다.
프로세스 별로 독립된 영역을 가지게 되는 곳은 유저 메모리 공간이다. 커널 메모리 공간의 경우 모든 프로세스가 공유하여 사용하고 있다. 프로세스 별로 독립적인 유저 메모리 영역을 관리하기 위해서 VAD
(Virtual Address Descriptors)라는 관리 테이블이 존재한다.
핸들 테이블은 프로세스에서 사용하는 모든 핸들에 대한 커널 객체 포인터 정보를 배열 형태로 가지고 있는 공간이다. 프로세스가 종료하게 될 때 이 테이블의 정보를 참고하여 이 프로세서에서 사용하고 있는 모든 커널 객체를 자동으로 반환한다.
프로세스 단위로 관리되는 자원 중 가장 중요한 구별점은 가상 메모리
이다. 윈도우의 경우, 페이징 기법을 이용하여 프로세스마다 별도의 고유한 메모리를 사용할 수 있게 하고 있다.
프로세스의 특징으로는 자원 소유의 단위
와 디스패칭의 단위
가 있다.
각각의 프로세스는 자신의 실행 이미지 로드와 실행에 필요한 추가적인 메모리 공간을 가지고 있어야 한다. 이것은 각 프로세스마다 구별되어야 하며 해당 프로세스가 접근하고자 하는 파일, I/O 장치들에 대해서 또한 프로세서 단위로 할당 받아 관리되어야 한다.
프로세스는 하나의 프로그램이 운영체제로부터 CPU 의 자원을 일정 기간 동안 할당 받아 명령어를 실행하는 것이며, 운영체제는 여러 개의 프로세스가 병렬적으로 실행되게 하기 위해서 CPU 의 사용 시간을 각각의 프로세스에 골고루 나누어 주어야 한다. 하나의 프로세스에서 여러 개의 디스패칭 단위가 실행될 수 있도록 하고 있으며, 이러한 디스패칭 단위를 스레드라 부른다.
프로세서
와 프로세스
는 엄밀히 다른 존재이다.
프로세서는 하드웨어적인 측면
과 소프트웨어적인 측면
으로 나누어 볼 수 있다.
하드웨어적인 측면
컴퓨터 내에서 프로그램을 수행하는 하드웨어 유닛으로, 중앙처리장치
(CPU)를 의미하며 적어도 하나 이상의 ALU와 레지스터를 내장하고 있다.
소프트웨어적인 측면
데이터 포맷을 변환하는 역할을 수행하는 데이터 처리 시스템을 의미한다. 워드프로세서나 컴파일러 등이 여기에 속한다.
프로세스는 특정 목적을 수행하기 위해 나열된 작업의 목록을 의미한다. 메모리에 적재 되어 프로세서에 의해 실행 중인 프로그램을 프로세스라고 볼 수 있다.
[그림] 프로세스 내에 스레드가 존재하므로 서로 자원과 상태를 공유할 수 있다.
같은 프로세스 내에 있는 스레드는 같은 주소 공간에 존재하게 되며 동일한 데이터에 접근할 수 있다. 또한 하나의 스레드가 수정한 메모리는 같은 메모리를 참조하는 스레드에 영향을 미치게 된다. 예를 들어 하나의 스레드에서 오픈한 파일을 다른 스레드가 사용할 수 있다. 프로세스가 종료되면 그 프로세스에 속해있던 스레드도 함께 종료된다.
하나의 프로세스 안에서 여러 개의 루틴을 동시에 수행하여서 수행 능력을 향상하려고 할 때 스레드를 사용하게 되는데, 독립적으로 수행하여 처리하려고 할 때 사용하게 된다. 즉, 여러 개의 작업 단위로 구성된 프로그램에서 요청을 동시에 처리하기 위해서이다.
예를 들어, 워드 프로세서에서 사용자로부터 키보드 입력 받거나, 그래픽이나 UI를, 그리고 문법 오류를 체크하는 등 워드 프로세서 내에서 여러 요청들을 동시에 처리해야 하는데, 이때 스레드가 필요하게 된다.
Thread Class
에 구현되어 있는 run() Method 코드call stack
이 존재(call stack: 실행 중인 서브루틴을 저장하는 자료 구조)하며, 나머지 Code, Data, Heap 영역은 스레드 끼리 공유한다. 반면에 프로세스는 다른 프로세스의 메모리에 직접 접근할 수 없다.
[그림] 프로세스와 싱글 스레드, 멀티 스레드 예시
문맥 교환
(context switch) 작업 또한 요구하지 않는다. 문맥 교환
은 여러 개의 프로세스가 하나의 프로세서를 공유할 때 발생하는 작업으로 많은 비용을 필요로 한다.Cluster 모듈
을 이용하여 여러 프로세스를 사용할 수 있다. 하지만 앞서 프로세스끼리의 자원 공유는 어렵기 때문에 Redis
와 같은 부가 인프라가 필요하다.문맥 교환
(Context Switching)이라 한다. 문맥 교환이란 다른 태스크(프로세스, 스레드)가 시작할 수 있도록 이미 실행 중인 태스크(프로세스, 스레드)를 멈추는 것을 말한다.관련 키워드
- 데드락(Deadlock, 교착 상태) => 둘 이상의 프로세스가 다른 프로세스가 점유하고 있는 자원을 서로 기다릴 때 무한 대기에 빠지는 상황
- 뮤텍스(Mutex), 세마포어(Semaphore) => https://worthpreading.tistory.com/90
싱글 코어와 멀티 코어 예시
[그림] 싱글 코어와 멀티 코어 예시
동시에 돌릴 수 있는 스레드 수는 컴퓨터에 있는 코어 개수로 제한된다. 운영체제(또는 가상 머신)는 각 스레드를 시간에 따라 분할하여, 여러 스레드가 일정 시간마다 돌아가면서 실행되도록 한다. 이런 방식을 시분할
이라고 한다.