기본적으로 프로세스마다 최소 1개의 스레드를 소유한다. (메인 스레드 포함)
프로세스는 각각 별도의 주소공간을 할당 받는다. (다른 프로세스로부터 독립적)
프로세스는 자신만의 고유 공간과 자원을 할당받아 사용한다.
스레드는 다른 스레드와 공간, 자원을 공유하면서 사용한다. (힙, 데이터, 코드 영역 공유)
하나의 컴퓨터에 여러 CPU를 장착 -> 하나 이상의 프로세스들을 동시에 처리한다. (병렬)
프로세스의 상태정보를 저장하고 복원하는 일련의 과정
즉, 동작중인 프로세스가 대기하면서 해당 프로세스의 상태를 보관하고, 대기하고 있던 다음 순번의 프로세스가 동작하면서 이전에 보관했던 프로세스 상태 정보를 복구하는 과정을 말함
-> 프로세스는 각 독립된 메모리 영역을 할당받아 사용되었으므로, 캐시 메모리 초기화같은 무거운 작업이 진행되었을 때 오버헤드가 발생할 문제 존재
하나의 응용 프로그램(프로세스)에서 여러 스레드를 구성해 각 스레드가 하나의 작업을 처리하는 것
스레드들이 공유 메모리를 통해 다수의 작업을 동시에 처리하도록 해준다.
멀티스레드의 안정성에 대한 단점은 Critical Section 기법을 통해 대비한다.
하나의 스레드가 공유 데이터 값을 변경하는 시점에 다른 스레드가 그 값을 읽으려할 때 발생하는 문제를 해결하기 위한 동기화 과정!!
상호 배제, 진행, 한정된 대기를 충족!
프로그램이 CPU에 의해 실행 -> 프로세스가 생성되고, 메모리에 프로세스 주소 공간이 할당됨
프로세스 주소공간은 코드, 데이터, 스택, 힙으로 이루어짐
최대한 데이터를 공유하여 메모리 사용량을 줄여야한다!!
프로그램 소스코드같은 경우는 프로그램 자체에서는 모두 같은 내용이기 때문에 따로 관리하여 공유한다.
스택과 데이터를 나눈 이유는, 스택구조의 특성과 전역변수의 활용성을 위한 것
프로그램을 실행하는 도중에 예기치 않은 상황이 발생한 경우, 현재 실행중인 작업을 즉시 중단하고, 발생된 상황을 우선 처리한 후 실행중이던 작업으로 복귀하여 계속 처리하는 것
지금 수행중인 일보다 더 중요한 일(ex. 입출력, 우선순위 연산 등)이 발생하면 그 일을 먼저 처리하고나서 하던 일을 계속해야한다.
외부/내부 인터럽트는 CPU의 하드웨어 신호에 의해 발생
소프트웨어 인터럽트는 명령어의 수행에 의해 발생
입출력 장치, 타이밍 장치, 전원 등 외부적인 요인으로 발생
전원 이상, 기계 착오, 외부 신호, 입출력
Trap이라고 부르며, 잘못된 명령이나 데이터를 사용할 때 발생
0으로 나누기, 오버플로우, 명령어 잘못 사용한 경우 (Exception)
프로그램 처리 중 명령의 요청에 의해 발생한 것
사용자가 프로그램을 실행시킬 때 발생
소프트웨어 이용중에 다른 프로세스를 실행시키면
시분할 처리를 위해 자원할당 동작이 수행됨
주 프로그램 실행되다가 인터럽트 발생
현재 수행 중인 프로그램을 멈추고, 상태 레지스터와 PC(Program Counter)등을 스택에 잠시 저장한 뒤에 인터럽트 서비스 루틴으로 간다.
(잠시 저장하는 이유는, 인터럽트 서비스 루틴이 끝난 뒤 다시 원래 작업으로 돌아와야하기 때문)
만약 인터럽트 기능이 없었다면, 컨트롤러는 특정한 어떤 일을 할 시기를 알기위해 계속 체크를 해야한다. (이를 폴링(Polling)이라고 한다.)
폴링을 하는 시간에는 원래 하던 일에 집중할 수가 없게 되어 많은 기능을 제대로 수행하지 못하는 단점이 있다.
즉, 컨트롤러가 입력을 받아들이는 방법(우선순위 판별방법)에는 두가지가 있다.
사용자가 명령어를 사용해 입력 핀의 값을 계속 읽어 변화를 알아내는 방식
인터럽트 요청 플래그를 차례로 비교하여 우선순위가 가장 높은 인터럽트 자원을 찾아 아에 맞는 인터럽트 서비스 루틴을 수행 (하드웨어에 비해 속도 느림)
MCU 자체가 하드웨어적으로 변화를 체크하여 변화시에만 일정한 동작을 하는 방식
인터럽트 방식은 하드웨어로 지원을 받아야하는 제약이 있지만, 폴링에 비해 신속하게 대응하는 것이 가능. 따라서 실시간 대응이 필요할 때는 필수적인 기능
즉, 인터럽트는 발생시기를 예측하기 힘든 경우 컨트롤러가 가장 빠르게 대응할 수 있는 방법
CPU가 프로세스가 여러 개일때, CPU 스케줄링을 통해 관리하는 것
이때, CPU는 각 프로세스들이 누군지 알아야 관리할 수 있다.
프로세스들의 특징을 갖고있는 것이 바로 Process Metadata
이 메타데이터는 프로세스가 생성되면 PCB(Process Control Block)이라는 곳에 저장된다.
프로세스의 메타데이터들을 저장해놓는 곳, 한 PCB 안에는 한 프로세스의 정보가 담긴다.
다시 정리해보면
프로그램 실행 -> 프로세스 생성 -> 프로세스 주소 공간에 (코드, 데이터 스택) 세그먼트 생성 -> 이 프로세스의 메타데이터들이 PCB에 저장
CPU에서는 프로세스의 상태에 따라 교체작업이 이루어짐
(인터럽트가 발생해서 할당받은 프로세스가 waiting상태가 되고, 다른 프로세스를 running으로 바꿔올릴 때)
이때 앞으로 다시 수행할 대기중인 프로세스에 관한 저장값을 PCB에 저장해두는 것!
Linked List 방식으로 관리됨
PCB List Head에 PCB들이 생성될 때마다 붙게 된다.
주소값으로 연결이 이루어져있는 연결리스트이기 때문에 삽입 삭제가 용이 (링크드 리스트 특징)
즉, 프로세스가 생성되면 해당 PCB가 생성되고, 프로세스가 완료되면 제거된다.
이렇게 수행중인 프로세스를 변경할 때, CPU의 레지스터 정보가 변경되는 것을 Context Switching이라고 한다.
CPU가 이전의 프로세스 상태를 PCB에 보관하고, 또 다른 프로세스의 정보를 PCB에서 읽어 레지스터에 적재하는 과정
보통 인터럽트가 발생하거나, 실행중인 CPU 사용 허가시간을 모두 소모하거나 (라운드 로빈), 입출력을 위해 대기해야하는 경우에 Context Switching이 발생한다.
즉, 프로세스가 Ready -> Running, Running -> Ready, Running -> Waiting 처럼 상태 변경시 발생한다.
오버헤드는 과부하라는 뜻으로 보통 안좋은 말로 많이 쓰인다.
하지만 프로세스 작업중에는 오버헤드를 감수해야하는 상황이 있다.
프로세스를 수행하다가 입출력 이벤트가 발생해서 대기 상태로 전환시킴
이때, CPU를 그냥 놀게 놔두는 것보다 다른 프로세스를 수행시키는 것이 효율적
즉, CPU에 계속 프로세스를 수행시키도록 하기 위해서 다른 프로세스를 실행시키고 Context Switching 하는 것
CPU가 놀지 않도록 만들고, 사용자에게 빠르게 일처리를 제공해주기 위한 것
프로세스는 독립적으로 실행된다. 독립되어있다는 것은 다른 프로세스에게 영향을 받지 않는다는 것 (메모리공간이 독립되어있다. 스레드는 프로세스 안에서 자원을 공유하므로 다른 스레드의 영향을 받는다.)
이런 독립적 구조를 가진 프로세스간의 통신을 해야하는 상황이 있을 것이다. 이를 가능하도록 해주는 것이 바로 IPC 통신이다.
프로세스는 커널이 제공하는 IPC 설비를 이용해 프로세스간 통신을 할 수 있다.
운영체제의 핵심적인 부분으로, 파일입출력, 프로세스관리 등과 같이 운영체제의 기능을 담당
하지만 일반 사용자모드에선 커널에 접근할 수 없기 때문에 원칙적으로는 파일 입출력, 프로세스 생성등 커널의 기능을 사용하지 못한다. 그래서 운영체제에서 제공하는 것이 시스템 콜이다.
응용프로그램이 운영체제의 기능을 요청하기 위해서, 운영체제는 시스템 콜을 제공한다. (시스템 함수를 호출하는 것이어서 시스템 콜, 운영체제가 제공하는 기능을 사용할 수 있는 api)
시스템콜은 커널과 사용자 사이의 인터페이스 역할을 하는 것으로 쉘(Shell)에서 명령어나 서브루틴 형식으로 운영체제의 기능을 호출할 수 있다. 즉, 사용자가 직접 커널에 접근할 수 없기 때문에 시스템콜을 활용해야한다.
보통 시스템 콜을 직접 사용하기보다는, 해당 시스템 콜을 사용해서 만든 각 언어별 라이브러리(API)를 사용한다.
쉽게 말하면 운영체제의 기능을 호출하는 함수이다.
운영체제는 쉘을 통해서 사용자 인터페이스를 제공한다.
쉘은 사용자가 운영체제 기능과 서비스를 조작할 수 있도록 인터페이스를 제공하는 프로그램이다.
쉘은 터미널환경(CLI)과 GUI 환경 두 종류로 나뉜다.
참조 : https://cbw1030.tistory.com/92
파이프는 두 개의 프로세스를 연결하는데, 하나의 프로세스는 데이터를 쓰기만하고, 다른 하나는 데이터를 읽기만 할 수 있다.
한쪽 방향으로만 통신이 가능한 반이중 통신이라고 부른다.**
따라서 양쪽으로 모두 송/수신을 하고 싶으면 2개의 파이프를 만들어야한다.
매우 간단하게 사용할 수 있는 장점이 있고, 단순한 데이터 흐름을 가질 땐 파이프를 사용하는 것이 효율적이다. 단점으로는 전이중 통신을 위해 2개를 만들어야 할 때는 구현이 복잡해지게 된다.
익명 파이프는 통신할 프로세스를 명확히 알 수 있는 경우 사용한다. (부모-자식 프로세스간의 통신처럼)
Named 파이프는 전혀 모르는 상태의 프로세스들 사이 통신에 사용한다.
즉, 익명 파이프의 확장된 상태로 부모 프로세스와 무관한 다른 프로세스도 통신이 가능한 것 (통신을 위해 이름있는 파일을 사용)
하지만, Named 파이프 역시 읽기/쓰기 동시에 불가능하다.
따라서 전이중 통신을 위해서는 익명파이프처럼 2개를 만들어야 가능
입출력 방식은 Named 파이프와 동일하다
다른점은 메시지 큐는 파이프처럼 데이터의 흐름이 아니라 메모리 공간이다.
사용할 데이터에 번호를 붙이면서 여러 프로세스가 동시에 데이터를 쉽게 다룰 수 있다.
파이프, 메시지 큐가 통신을 이용한 설비라면, 공유 메모리는 데이터 자체를 공유하도록 지원하는 설비다.
프로세스의 메모리 영역은 독립적으로 가지며 다른 프로세스가 접근하지 못하도록 반드시 보호되어야 한다. 하지만, 다른 프로세스가 데이터를 사용하도록 해야하는 상황도 필요하다. 파이프를 이용한 통신을 통해 데이터 전달도 가능하지만, 스레드처럼 메모리를 공유하도록 해준다면 더욱 편할 것이다.
공유 메모리는 프로세스간 메모리 영역을 공유해서 사용할 수 있도록 허용해준다.
프로세스가 공유 메모리 할당을 커널에 요청하면, 커널은 해당 프로세스에 메모리 공간을 할당해주고 이후 모든 프로세스는 해당 메모리 영역에 접근할 수 있게된다.
중개자 없이 곧바로 메모리에 접근할 수 있어서 IPC중에 가장 빠르게 작동한다.
공유 메모리처럼 메모리를 공유한다. 메모리 맵은 열린 파일을 메모리에 매핑시켜서 공유하는 방식이다. (즉 공유 매개체가 파일 + 메모리)
주로 파일로 대용량 데이터를 공유해야할 때 사용
네트워크 소켓 통신을 통해 데이터를 공유한다.
클라이언트와 서버가 소켓을 통해서 통신하는 구조로, 원격에서 프로세스간 데이터를 공유할 때 사용한다.
서버 : bind, listen, accept
클라이언트 : connect
이런 IPC 통신에서 프로세스간 데이터를 동기화하고 보호하기 위해 세마포어와 뮤텍스를 사용한다. (공유자원에 한번에 하나의 프로세스만 접근시킬 때)
참고 : https://jwprogramming.tistory.com/54
CPU를 잘 사용하기 위해 프로세스를 잘 배정하는 것, OS에 의해 일어남
Interrupt
, Scheduler Dispatch
I/O or Event wait
Time Quantum
만큼 CPU를 할당받는다.Time Quantum
or Time Slice
: 실행의 최소단위 시간프로세스가 자원을 얻지 못해서 다음 처리를 하지 못하고 무한정 대기하는 상태. 교착상태라고도 부름
시스템적으로 한정된 자원을 여러 곳에서 사용하고자 할 때 발생
4가지 모두 성립해야 데드락 발생! (하나라도 성립하지 않으면 데드락 문제 해결 가능)
자원은 한번에 한 프로세스만 사용할 수 있다.
최소한 하나의 자원을 점유하고 있으면서 다른 프로세스에 할당되어 사용되고있는 자원을 추가로 점유하기 위해 대기하는 프로세스가 존재해야한다.
다른 프로세스에 할당된 자원은 사용이 끝날 때까지 강제로 빼앗을 수 없다.
프로세스의 집합에서 순환형태로 자원을 대기하고 있어야한다.
교착 상태를 예방 & 회피
교착 상태 발생 조건 중 하나를 제거하면서 해결 (자원 낭비 심함)
교착 상태 발생시 피해나가는 방법
ex) 은행원 알고리즘
공유자원에 대해 여러 프로세스가 동시에 접근할 때, 결과값에 영향을 줄 수 있는 상태
동시접근시 자료의 일관성을 해치는 결과가 나타난다.
공유된 자원에 여러 프로세스 혹은 스레드가 동시에 접근하면서 문제가 발생할 수 있다. 이때 공유된 자원의 데이터는 한번에 하나의 프로세스만 접근할 수 있도록 제한을 둬야한다. (상호배제)
이를 위해 나온 것이 바로 세마포어이다.
멀티프로그래밍 환경에서 공유자원에 대한 접근을 제한하는 방법
프로세스 진입 여부를 자원의 개수를 통해 결정!
여러 프로세스가 데이터를 공유하면서 수행될 때, 각 프로세스에서 공유 데이터를 접근하는 프로그램 코드 부분
공유 데이터를 여러 프로세스가 동시에 접근할 때 잘못된 결과를 만들 수 있기 때문에, 한 프로세스가 임계구역을 수행할 때는 다른 프로세스가 접근하지 못하도록 해야한다. (상호배제)
P(S);
// --- 임계 구역 ---
V(S);
procedure P(S) --> 최초 S값은 1임
while S=0 do wait --> S가 0면 1이 될때까지 기다려야 함
S := S-1 --> S를 0로 만들어 다른 프로세스가 들어 오지 못하도록 함
end P
--- 임계 구역 ---
procedure V(S) --> 현재상태는 S가 0임
S := S+1 --> S를 1로 원위치시켜 해제하는 과정
end V
이를 통해 한 프로세스가 P 혹은 V를 수행하고 있는 동안 프로세스가 인터럽트 당하지 않게 된다. P와 V를 사용하여 임계구역에 대한 상호배제 구현이 가능하게됨.
임계구역을 가진 스레드들의 실행시간이 서로 겹치지 않고 각각 단독으로 실행되게 하는 기술 - 즉 공유자원에 대한 접근이 동시에 여러 스레드에서 일어나지 않도록 하는 것 = 상호배제
상호 배제 (Mutual Exclusion)의 약자
해당 접근을 조율하기 위해 lock과 unlock을 사용
뮤텍스는 상태가 0,1로 이진 세마포어로 부르기도 함
세마포어는 자원의 개수로 임계구역 진입조건을 판단하고,
뮤텍스는 락과 언락연산을 통해 임계구역 진입 조건을 판단한다.
페이징 세그멘테이션 기법을 사용하는 이유
-> 다중 프로그래밍 시스템에서 여러 프로세스를 수용하기 위해 주기억장치(RAM)을 동적 분할하는 메모리 관리 작업이 필요하기 때문
프로그램 전체가 하나의 커다란 공간에 연속적으로 할당되어야한다.
프로그램의 일부가 서로 다른 주소 공간에 할당될 수 있는 기법