📝 운영체제에 관해 공부한 것을 정리해보고자 합니다 (업데이트 상시 예정)
프로그램들을 실행, 관리하는 메인 프로그램
운영체제의 역할
1. 자원 관리 : CPU, 메모리 등의 자원들을 효율적으로 관리
2. 프로그램 관리
3. 네트워크 통신
4. 입출력 장치 관리
프로그램이 실행되면 (ex.카톡)
1. OS
가 카톡 프로그램을 실행하여
2. 카톡 프로그램 코드들을 메모리에 올린 프로세스
로 만들고 사용자 메모리 공간
에 배치한다.
3. 그리고 카톡 프로세스에 대한 정보를 담고 있는 PCB 블럭
이 생성되어 커널 메모리 공간
에 배치된다.
4. PCB에는 우선순위
가 적혀있을 텐데, OS의 프로세스 스케줄러
는 PCB의 우선순위에 맞춰 준비 큐
에 배치하고 CPU에 할당시킨다(=실행시킨다. 실행된다)
✔︎ 실행중인 프로그램
프로그램을 실행하면 OS로부터 실행에 필요한 자원(메모리)를 할당받아 프로세스 (OS에서 컨트롤하는, 자원을 할당받는 작업의 단위) 가 된다.
프로그램 명령어와 데이터들이 메모리에 올라오고 실행 중 또는 실행 대기중인 상태이다.
프로그램은 하드디스크에 있는 파일이고, 프로그램을 실행시켰을 때 운영체제 안에서 프로세스가 되어(프로세스화) 실행된다.
실행파일 하나가 있다 하더라도 여러번 실행시키면 프로세스는 여러개가 생기게 된다.
프로세스는 프로그램을 수행하는데 필요한 데이터
와 메모리 등의 자원
, 그리고 쓰레드
로 구성되어있다.
실행시켜서 연산한다는 것은 thread 단위(실질적 연산의 주체, 연산의 단위)이기 때문에,
프로세스에는 최소한 1개 이상의 쓰레드가 존재하며, 둘 이상의 쓰레드를 가진 프로세스를 멀티쓰레드 프로세스 라고 부른다.
실행의 의미
실행단위란 cpu core에서 실행하는 하나의 단위. (cpu는 한순간에 한가지 일만 처리?)
청소기를 쓰려고하는데, 청소기 조립 설명서만 준다고 청소기가 돌아가는게 아님. 이때 설명서는 코드가 구현되어있는 파일.(프로그램) 하지만, 프로그램 자체는 실행시키기 전에는 그저 코드가 구현되어있는 파일일뿐임.
이것을 사용하기 위해서는 종이 레시피가(설명서가) 피자가(청소기) 되는 것 처럼 실행이 되어서 사용할 수 있는 무언가가 되어야하는데 그것이 바로 프로세스.
프로그램이 프로세스 되면서 어떤 일이 일어날까?
- 프로세스가 필요로 하는 재료들이 메모리에 올라가야한다. (그리고 그 메모리에는 4가지 영역들로 나누어져있다.
code
+data
+heap
+stack
)- 해당 프로세스에 대한 정보를 담고 있는 PCB 블럭이 프로세스 생성시 함께 만들어진다.(pcb 안에 pid, pointer 등 존재)
운영체제 내에서 수행되는 프로세스들은 여러 가지 필요에 의해 서로 데이터를 주고 받을 필요가 있다.
데이터를 주고받는 프로세스들은 메시지를 주고받을 수 있고, 동일한 시스템의 프로세스끼리 통신을 하거나 서로 다른 시스템의 프로세스끼리 통신할 수 있다. 이때 프로세스 간 통신 방법은 다음과 같다.
시그널
방식을 선택파이프
를 선택소켓
을 선택(*동기화 (synchronization) : 데이터의 일관성을 유지하기 위한 메커니즘.)
✔︎ 프로세스의 자원을 이용해서 실제로 작업을 수행하는 것 (실행되는 흐름의 단위, 할당받은 자원을 이용하는 실행의 단위)
쓰레드가 작업을 수행하려면 개별적인 메모리 공간(호출 스택)을 필요로 한다
OS는 VM(제한된 공간)을 프로세스에게 할당한다.
thread는 process에 속해있기 때문에 프로세스의 vm으로 공간이 제약된다.
스레드는 프로세스의 컨테스트 스위칭이 부담이 될 수 있어 경량화된 프로세스 버전으로 스레드가 등장하게 되었다.
왜 스레드가 경량화된 프로세스냐하면,
프로세스 안에 다수의 스레드가 있을 떄 공유되는 자원 (프러세스의 메모리 구조에서 code,data,heap영역을 공통 자원으로 사용)이 있기 때문에. 그리고 각 스레드는 스택 부분만을 따로 가지고 있다.
공유되는 자원이 있기 때문에 이전처럼 컨텍스트 스위칭이 일어날때 캐싱 적중률이 올라간다.(모조리 다 빼고 다시 다 넣고 할 필요가 없다는 것)(이게 스레드의 컨텍스트 스위칭이인데, 공유되는 자원들을 그냥 그대로 두고 사용하니 효율적. 프로세스는 공유되는 자원들이어도 다시 다 취소했다가 다시 다 실행했다가 그래야함.)
예시
process
를 한 가구라고 생각하면, 집이라는 공간(메모리
, 프로세스에게 할당된 VM)가 부여되고, 한 가구안에 세대 원들을thread
라고 볼 수 있다. (여러명이 모여산다면멀티쓰레드
이다)
thread 는 각자의 역할을 할꺼지만, 집이라는 메모리를 쪼개 쓰기위해 거실 부엌 등을 공유 공간들을 공유하고, 각자 보유되는 개인 방들 Thread Local Storage(TLS
) 또한 지니고 있다. (Thread 마다 관리되는 Stack 공간도 따로 있기도 함.)
각자 모여 살고 있는데, 범위는 프로세스에게 제공된 운영체제 공간에 해당된다.
추가❔
Q. 프로세스가 지닐 수 있는 쓰레드의 개수는?
프로세스가 지닐 수 있는 쓰레드의 개수는 쓰레드가 작업을 수행하는데 개별적인 메모리 공간(호출 스택)을 필요로 하기 때문에, 프로세스의 메모리 한계에 따라 생성할 수 있는 쓰레드의 수가 결정된다.Q. 왜 Thread 로 나눌까, 프로세스를 여러번 실행시키면 되지 않을까?
할 일이 있는데 그 일을 3명이 팀이 회사에서 나눠서 협동해서 일하는 것과, 개인 별로 각자 재택근무해서 처리하는게 좋을 것이냐. 그런 차이라고 볼 수 있다. 뭐가 더 낫다 맞다는 없고 일의 특성에 따라서 다르다.Q. Task 와 프로세스,쓰레드의 차이는?
task는 작업의 최소단위로써 쓰레딩을 했을 경우 thread가 되고 아닐 경우 process가 된다.
single tasking의 경우 MS, multi tasking의 경우 리눅스를 예로 들 수 있다.
우리는 크롬도 켜놓고, 인텔리제이도 켜놓고, 멜론도 켜놓고, 카톡도 켜놓는 등 여러가지를 동시에 사용하고 싶어한다,
하지만 원래 한 프로세스가 실행되기 위해서 cpu를 점유하고 있으면 다른 프로세스는 실행상태에 있을 수 없다.
(멜론에서 노래를 듣다가 카톡을 하면 노래가 꺼지게 되는 것)
그래서 다수의 프로세스를 동시에 실행 하기 위해 여러개 프로세스를 짧은 텀을 반복하면서(시분할로) 전환 해서 실행시키도록 한다. (프로세스1 cpu 적재시켜서 실행시킬땐 프로세스2는 준비상태, 반대로 프로세스2가 cpu에 올라가면 프로세스1은 준비상태. = 이게 바로 컨테스트 스위칭)
멀티 프로세스와 멀티 스레드는 한 어플리케이션에 대한 2가지 다른 처리방식 이라고 볼 수 있다.
하나의 프로세스는 하나의 로그인만 처리할 수 있기 때문에 동시에 처리 할 수 없다.
그래서 부모 프로세스가 fork
해서 자식 프로세스를 여러개 만들어서 일을 처리하도록 하면 멀티 프로세스
이다.
이때 프로세스는 독립적 이기 때문에, 자식 프로세스는 부모와 별개의 메모리 영역 을 확보하게 된다. 또한 별개의 메모리 영역이므로 IPC 를 통해서 통신을 해야하고 컨텍스트 스위칭 비용 이 크다. 대신, 개별 독립적이기 때문에 동기화 작업에 신경을 덜 써도 된다.
멀티 프로세스를 사용하는 이유는, 서로 독립적이기 때문에 하나의 프로그램이 일시정지 되더라도 다른 프로그램을 사용하는데 문제가 (ex.구글 크롬은 멀티탭간의 영향을 덜 받는다)
스레드는 한 프로세스 내에서 구분지어진 실행 단위이다.
프로세스 내에서 분리해서 여러 스레드로 나뉘어서 실행단위가 나뉘어지게 되면 그게 멀티 스레드
이다.
(ex. 인텔리제이에서 코드를 수정하는 동시에 추천 코드를 보여지게 하는 것, 테스트를 실행하는 동시에 코드를 수정하는 것)
스레드는 스레드끼리 긴밀하게 연결 되어있어, 공유자원으로 메모리가 효율적 이고 컨텍스트 스위칭 비용이 적다.
대신, 공유 자원을 관리해야하고, 모두 멀티 스레드로 동작한다면 하나의 스레드가 멈추면 전체 프로세스에 영향이 가서 all stop 될 수 있다.(ex.internet exploer 작동 중지)
멀티 쓰레드 프로그래밍 작성 시 유의점할 점은
다수의 쓰레드가 공유 데이터에 동시에 접근하는 경우에 상호 배제
를 제거해 교착 상태
를 예방하고 동기화 기법
을 통해 동시성 문제
가 발생하지 않도록 발생하지 않도록 주의해야 한다.
(*동시성 문제: 싱글 코어에서 여러 스레드가 번갈아가면서 실행되다가(동시성), 동일한 자원에 대해 여러 스레드가 동시에 접근 하면서 발생하는 문제)
→ 결국, 뭐가 더 좋은게 아니라 멀티 프로세스는 독립적인 메모리 가지고, 멀티 스레드는 자원을 공유하는 각자의 장단점이 있다.
멀티프로세스랑 멀티쓰레드는 '처리 방식'의 일종이라면, 멀티코어는 하드웨어 측면이다.
하나의 cpu는 하나의 프로세스만 점유할 수 있기 때문에, 병렬처리를 통해 물리적으로 여러개의 코어를 사용함으로써 (동시성이 아닌) 정말 다수의 실행단위를 동시에 실행하는 것이다.
(*동시성 : 한 순간에 여러가지 일이 아니라 짧은 전환으로 여러가지 일을 동시에 처리하는 것처럼 보이는 것.)
프로세스가 생성되어 실행될때 필요한 자원을 해당 프로세스에 할당하는 작업을 말한다.
이거 아직 정리 못함
선점형은 하나의 프로세스가 다른 프로세스 대신에 CPU를 차지할 수 있음을 말하고,
비선점형은 하나의 프로세스가 끝나지 않으면 다른 프로세스는 CPU를 사용할 수 없음을 말합니다.
프로세스나 스레드 간 동일한 자원을 공유하거나 정보를 공유하며 협업해야 할 일이 생길 것이다.
프로세스의 경우는 IPC를 이용하면 되고 스레드는 전역변수 등 서로 공유하는 메모리 영역을 통해 공유하면 된다.
하지만 이런 경우 발생할 수 있는 동기화 관련 문제들이 존재한다.
(*동기화 (synchronization) : 프로세스 또는 스레드들이 수행되는 시점을 조절하여 서로가 알고 있는 정보가 일치하는 것을 의미.
이는 데이터의 일관성을 유지하기 위한 메커니즘인데 동기화가 없다면 한 순간에 하나의 데이터 값이 여러 개가 되어 데이터 일관성이 깨질 수 있기 때문이다.)
(*프로세스 동기화 : 하나의 자원을 한 순간에 하나의 프로세스만 이용하도록 제어하는 것. )
위에서 프로세스의 통신 과 동기화 에 대해서 이야기 하였다.
프로세스간의 통신 중 프로세스간의 동기화를 위해서는 시그널 방식을 사용하고,
이때 프로세스 동기화란 하나의 자원을 한 순간에 하나의 프로세스만 이용하도록 제어하는 것. 이다.
그렇다면, 경쟁상태는 프로세스 동기화가 제대로 이루어지지 않고, 여러개의 프로세스가 하나의 자원에 동시에 접근하여 데이터 일관성이 깨질 수 있는 상태이다.
그리고 이를 발생시키는 코드 구역을 임계구역
이라고 한다.
이때 제일 나중에 실행되는 프로세스에 의해 최종 값이 결정된다.
예시
잔액이 0원인 한 통장에 나와 엄마가 동시에 1만원을 입금하였다.
그럼 총 2만원이 잔액이 되어야하는데, 이때 만약 경쟁 상황을 그대로 두게 되면 제일 마지막 입금 결과에 따라 결정된다.
임계영역이란 공유되는 자원에서 문제가 발생하지 않도록 독점을 보장해주는 영역을 의미한다.
임계영역에는 한 순간에 반드시 하나의 프로세스 또는 스레드만 진입이 가능합니다.
임계 영역을 잘 제어해야 동기화 문제를 제어할 수 있습니다. (즉, 공유 자원 독점을 통해 동기화를 유지할 수 있다)
-동일한 자원을 동시에 접근하는 작업(e.g. 공유하는 변수 사용, 동일 파일을 사용하는 등)을 실행하는 코드 영역을 Critical Section 이라 칭한다.
-두 개의 쓰레드에 의해 동시에 실행되면 안 되는 영역이 존재하는데, 이것을 critical section(임계영역)이라고 한다.
쓰레드의 내부적인 문제 때문에 생기는건데 이것은 컴퓨터의 내부 연산처리구조를 이해하고 있어야 알 수 있다.
임계구역으로 지정되었어야 할 코드 영역이 지정되지 않았을 때 발생될 수 있는 문제이다.
문제를 해결하기 위해서는 3가지 요구 조건을 충족시켜야 한다.
멀티프로그래밍(멀티프로세스,멀티스레드)에서 공유자원을 안전하게 관리하기 위해 상호 배재를 위한 기법이다.
다음과 같은 3가지 해결 방법이 있지만, 이 방법들은 상호 배제만 해결해 주기 때문에 데드락과 기아상태가 발생할 수 있다.
그럼 어떻게 상호배제(동시에 공유자원에 접근하지 않도록) 해결하는가
를 중심으로 보자.
뮤텍스
: 한 스레드, 프로세스에 의해 소유될 수 있는 key
를 기반으로 한다.
뮤텍스 객체는 제어되는 섹션에 하나의 쓰레드만을 허용하기 때문에 해당 섹션에 접근하려는 다른 쓰레드들을 강제적으로 막음으로써 첫 번째 쓰레드가 해당 섹션을 빠져나올 때까지 기다리도록 한다.
ex) 예를 들어 뮤텍스는 화장실에 들어가기 위한 열쇠 로 비유할 수 있다.
화장실에 들어갈 수 있는 열쇠를 한 사람이 갖고 있다면, 한 번에 열쇠를 갖고 있는 그 한 사람만이 들어갈 수 있다. 화장실에 열쇠를 갖고 있는 사람이 들어가 볼일을 다 본 후에는 줄을 서서 기다리고 있는(대기열-큐) 다음 사람에게 열쇠를 주게 된다.
세마포어
: 현재 공유자원에 접근할 수 있는 스레드, 프로세스의 수를 나타내는 값을 기반으로 한다.
세마포어는 공유 리소스에 접근할 수 있는 최대 허용치만큼 동시에 사용자 접근을 할 수 있게 한다. 쓰레드들은 리소스 접근을 요청할 수 있고 세마포어에서는 카운트가 하나씩 줄어들게 되며 리소스 사용을 마쳤다는 신호를 보내면 세마포어 카운트가 하나 늘어나게 된다.
이때 세마포에서 발생할 수 있는 단점이 Deadlock 교착상태이다.
ex) 예를 들어 세마포어는 빈 화장실 열쇠의 갯수 라고 보면 된다.
네 개의 화장실에 자물쇠와 열쇠가 있다고 한다면 세마포어는 열쇠의 갯수를 계산하고 시작할 때 4의 값을 갖는다. 화장실에 사람이 들어갈 때마다 숫자는 줄어들고 화장실에 사람들이 모두 들어가게 되면 남은 열쇠가 없게 되므로 세마포어 카운트가 0이 된다. 만약 사람이 화장실에서 나온다면 세마포어의 카운트는 1이 증가되므로 열쇠 하나가 사용가능하기 때문에 줄을 서서 기다리고 있는 다음 사람이 화장실에 입장할 수 있게 된다.
Monitor
교착 상태는 프로세스가 자원을 얻지 못해 다음 처리를 하지 못하는 상태이다.
시스템적으로 한정된 자원을 여러 프로세스가 사용하고자 할 때 발생하는 상황으로, 프로세스가 자원을 얻기 위해 영구적으로 기다리는 상태이다.
둘 이상의 프로세스들이 자원을 점유한 상태에서 서로 다른 프로세스가 점유하고 있는 자원을 요구하며 무한정 기다리는 것이다.
Dead Lock 의 4가지 조건이 있는데,
상호 배제
, 점유 대기
, 비선점
, 순환 대기
네 가지 조건을 모두 만족해야 교착 상태가 발생한다.
순환 대기의 경우 점유 대기와 비선점 조건을 만족해야 성립하므로 4가지 조건은 완전히 서로 독립적이지 않다.
Thread safe는 멀티 스레드 프로그래밍 환경에서 함수나 변수, 객체 등이 여러 스레드로부터 동시에 접근이 이루어져도 프로그램의 실행에 문제가 없는 것을 말한다.
(여러 스레드에서 동일한 코드를 돌려도 경쟁 상태가 발생하지 않는 것이다.)
Thread-safe한 코드를 만들기 위해서는
Critical Session을 통해 스레드 내부에서 처리되는 연산들을 직렬화 하여 한 번에 한 스레드에서 연산이 수행 되도록 만들어 주어야 한다.
java에서 동시성 이슈를 제어하는 방법에는 크게 3가지를 생각해볼 수 있다.
https://steady-coding.tistory.com/554
synchronized
synchronized
는 멀티 스레드 환경에서 동시성 제어를 위해 공유 객체를 동기화하는 키워드이다.
synchronized 블록 안에서 관리되는 자원들은 원자성을 보장할 수 있다.
volatile
volatile
키워드를 사용하여 CPU 메모리 영역에 캐싱 된 값이 아니라 항상 최신의 값을 가지도록 메인 메모리 영역에서 값을 참조하도록 할 수 있다. -> 즉, 동일 시점에 모든 스레드가 동일한 값을 가지도록 동기화한다.
atomic
Non-blocking 이지만 CAS(Compared And Swap) 알고리즘으로 작동하여 원자성을 보장한다.
성능 비교 참조
synchronized 를 사용하였을 경우 평균 1300ms 정도가 소요되었지만, AtomicLong 의 경우 평균 140ms 가 소요될 수 있다.
동기식은 요청(메소드 호출)과 결과(결과값)의 시간이 일치하는 것이다.
쉽게 말하면, 함수A가 끝나고
결과값을 받으면
→ 함수B가 실행할 수 있다
.
함수B 호출 이전에 함수A에 대한 요청과 결과가 한번에 해결되기 때문에 시간이 일치한다고 본다.
함수B가 실행할 수 있다
는 것은 함수A에게 넘겨주었던 제어권 이 결과값이 전달될 때 반환된다는 것이다.
결국, 동기식에서 바라보는 시간의 일치는 함수A에 대한 제어권 반환 타이밍과 결과값 전달 타이밍을 맞추는 것이다.
(*제어권 : 행동할 수 있는 권리라고 생각하면 된다.)
비동기식은 반대로 시작과 종료가 일치하지 않는 것이다.
요청과 결과가 동시에 이루어지지 않고, 하나의 요청을 처리하는 동안 다른 요청 처리가 가능하다.
이를 비동기식 처리 모델에서 병렬적으로 task를 수행한다고 말한다.
함수A를 호출하고
→ 결과와 상관없이 바로 함수B를 호출한다
.
이때 동기식과 달리 함수A 호출 후 바로 함수B를 호출할 수 있다는 것은 함수A가 끝나서 결과값을 전달하기 전에 제어권이 먼저 반환되어 함수B에게 넘겨줄 수 있기 때문이다.
때문에 비동기에서는 제어권 반환과 결과값 전달 시간이 일치하지 않는다.
그럼, 비동기식 프로그램의 경우는 이 함수를 호출하고나서 끝났다는 것을 어떻게 알고 그 결과값을 받아올까?
이때 Callback 함수(혹은 이벤트)를 통해서 결과는 별도의 작업 후 간접적으로 전달(callback)한다.
(*Callback : 간단히 말하면 어떤 특정 함수가 실행을 마친뒤에 실행되는 함수.)
생활 비유
해야할 일(task)가 빨래, 청소가 있다고 가정한다.
이 일들을 동기적으로 처리한다면 빨래를 하고 청소를 한다.
비동기적으로 처리한다면 빨래하는 업체에게 빨래를 시킨다. 청소 대행 업체에 청소를 시킨다. 둘 중 어떤 것이 먼저 완료될지는 알 수 없다. 일을 모두 마친 업체는 나에게 알려주기로 했으니 나는 다른 작업을 할 수 있다. 이 때는 백그라운드 스레드에서 해당 작업을 처리하는 경우의 비동기를 의미한다.
자신의 작업을 진행하다가 다른 작업이 시작되면 다른 작업이 끝날 때까지 기다렸다가 자신의 작업을 시작하는 것이다.
다시 말하면, 제어권을 넘겨주고 나서 제어권을 돌려받기 전까지는 작업을 일시정지하는 것이다.
결국, 블록은 제어권을 호출된 함수가 들고 있다가, 결과값 반환할 때 같이 준다는 것이다.
다른 주체의 작업이 진행중이던 아니던 상관없이자신의 작업을 하는 것이다.
다시 말하면, 요청하고 나서 제어권을 바로 돌려받았기 때문에 다른 작업을 할 수 있다.
Block, Non-Block의 차이/핵심은 다른 주체가 작업을 할때 자신의 제어권이 있는지 없는지 로 볼 수 있다.
요약
일반적으로 동기와 비동기의 차이는 메소드를 실행시킴과 동시에 반환 값이 기대되는 경우를
동기
라고 표현하고, 그렇지 않은 경우에 대해서비동기
라고 표현한다.
동기의 경우, '동시에' 라는 말은 실행되었을 때 값이 반환되기 전까지는blocking
되어 있다는 것을 의미한다.
비동기의 경우,non-blocking
되어 이벤트 큐에 넣거나 백그라운드 스레드에게 해당 task 를 위임하고 바로 다음 코드를 실행하기 때문에 기대되는 값이 바로 반환되지 않는다.
그럼 Sync와 Blocking은 유사한 것 아닌가?
결국 블록,동기가 같은 말 아닌가? 싶을 수 있지만 보는 관점이 다르다고 생각하면 된다.
블록,논블록
은 제어권(제어)을 보는 관점의 이야기이고, (제어할 수 없는 대상을 어떻게 처리하는가?)동기,비동기
는 제어권을 반환하는 시간(순서,결과)을 보는 관점의 이야기이다. (대상들의 시간을 일치시키는가?)(*제어권: 행동할 수 있는 권리라고 생각하면 된다.)
Blocking
Sync
이 필요될까?동기식/블럭은 그 결과 값으로 다음을 진행해야할 때 사용된다.
대표적으로 IO 의 경우를 많이 말한다. 예로 자바에서 입력 요청을 할 때 Blocking Sync 사용한다.
제어권이 넘어갔기 때문에 메시지를 입력하지 않으면 다음 내용이 실행되지 않는다.
입력을 하고난 후에야, 제어권과 결과를 받아서 다음 내용을 처리하게 된다.
public class Application {
public static void main(String[] args){
System.out.print("메시지를 입력하세요:");
final Scanner scanner = new Scanner(System.in);
String message = scanner.nextLine();
System.out.println("Blocking");
System.out.println(message);
}
}
NonBlocking
Async
이 필요될까?그럼 비동기를 사용해야 하는 이유 가 무엇인지에 대해서 생각해봐야한다.
자바스크립트는 기본적으로 싱글쓰레드 방식으로 동작하기 때문에 한 번에 한 가지 일만 수행할 수 있다.
때문에 멀티쓰레드가 아닌 싱글쓰레드인 경우 사용자의 동시 다발적인 요청에 빠르게 응답하기 위하여 방식으로 필요된다.
때문에, 비동기식/논블럭은 반대로 그 함수의 수행 결과 여부와 상관없이 양쪽에서 각자 작업 처리가 가능하게, 다른 함수가 실행되어도 괜찮은 경우 사용된다.
대표적으로 자바스크립트에서 API 요청을 하고, 다른 작업을 하다가 콜백(Callback)을 통해서 추가적인 작업을 처리할 때 사용한다.
fetch('url', option)
.then((response) => {
return response.json();
})
.then((data)) => {
something(data);
});
그외 NonBlocking/Sync, Blocking/Async 는?
NonBlocking/Sync 의 경우 Blocking/Sync 와 큰 차이가 없고,
Blocking/Async의 경우 실수 혹은 일부러 이 방식을 사용하진 않는다고 한다.
(+) 대표적으로 이 그림/표를 많이 볼 수 있다.
Block | Non-Block | |
---|---|---|
Sync | Java | |
Async | Javascript |
RAM에서 메모리의 공간이 작은 조각으로 나뉘어져 사용가능한 메모리가 충분히 존재하지만 할당(사용)이 불가능한 상태를 보고 메모리 단편화가 발생했다고 한다.
외부 단편화
메모리 공간 중 사용하지 못하게 되는 일부분. 물리 메모리(RAM)에서 사이사이 남는 공간들을 모두 합치면 충분한 공간이 되는 부분들이 분산되어 있을때 발생한다고 볼 수 있다.
내부 단편화
메모리를 할당할 때 프로세스가 필요한 양보다 더 큰 메모리가 할당되어서 프로세스에서 사용하는 메모리 공간이 낭비 되는 상황
이런 단편화 문제를 해결하기 위해 2가지 방법이 존재한다.
하나의 프로세스가 사용하는 메모리 공간이 연속적이어야 한다는 제약을 없애는 메모리 관리 방법
물리 메모리는 Frame
이라는 고정 크기로 분리되어 있고, 논리 메모리(프로세스가 점유하는)는 페이지(Page)
라 불리는 고정 크기의 블록으로 분리된다.
외부 단편화를 해결할 수 있지만 내부 단편화 문제가 발생할 수 있다.
서로 다른 크기의 논리적 단위인 세그먼트(Segment)
로 분할해서 메모리를 할당하여 실제 메모리 주소로 변환한다.
사용자가 두 개의 주소로 지정(세그먼트 번호 + 변위) 세그먼트 테이블에는 각 세그먼트의 기준(세그먼트의 시작 물리 주소)과 한계(세그먼트의 길이)를 저장
내부 단편화를 해결할 수 있지만 외부 단편화 문제가 발생할 수 있다.
가상 메모리는 멀티 프로세스 환경에서 프로세스마다 충분한 메모리를 할당하기에 물리 메모리의 한계가 있어서 나타난 개념이다.
가상 메모리에서 프로세스는 가상 주소를 사용하고, 실제 해당 주소에서 데이터를 읽고 쓸 때 물리 주소로 바꿔주게 된다. MMU(Memory Management Unit)를 통해 CPU에서 코드 실행 시, 가상 메모리 접근이 필요할 때, 해당 주소를 물리 주소로 변환해 준다.
다중 프로그래밍을 실현하기 위해서는 많은 프로세스들을 동시에 메모리에 올려두어야 하는데, 이때 프로세스 전체가 메모리 내에 올라오지 않더라도 실행이 가능하도록 하는 기법이다.
RAM의 부족한 용량을 보완하기 위해, 각 프로그램에 실제 메모리 주소가 아닌 가상의 메모리 주소를 할당하는 방식이다.
때문에 프로그램이 물리 메모리보다 커도 된다는 장점이 있다.
가상 메모리는 실제의 물리 메모리
개념과 사용자의 논리 메모리
개념을 분리한 것으로 정리할 수 있다.
이로써 작은 메모리를 가지고도 얼마든지 큰 가상 주소 공간을 프로그래머에게 제공할 수 있다.
대부분의 시스템은 가상 메모리를 사용하는데, 프로세서는 실제 사용가능한 주소보다 큰 주소 공간을 가진다. 가상 메모리는 디스크에 위치하며, 자주 사용되는 부분은 실제 메모리에 있다. 캐시와 비슷하게 공간이 필요할 때는 최근에 사용되지 않은 데이터가 메모리에서 제거된다.
소프트웨어 스레드 각각은 스택과 데이터 구조를 유지하려고 가상 메모리를 사용한다. 캐시와 마찬가지로 타임 슬라이스는 실제 메모리를 두고 소프트웨어 스레드끼리 경쟁하게 만들며 이는 성능을 떨어뜨린다. 극단적인 경우엔, 스레드가 너무 많아서 프로그램이 가상 메모리를 고갈시키기도 한다.
먼저, 페이지를 교체하는 이유는 가상메모리를 통해 조회한 페이지는 다시 사용될 가능성이 높기 때문이다.
페이지 교체를 위해서는 실제메모리
에 존재하는 페이지를 가상메모리
로 저장한 후에, 가상메모리
에서 조회한 페이지를 실제메모리
로 로드해야 된다. 이때 어떤 실제메모리의 페이지를 가상메모리로 희생시킬 것이냐에 대한 문제가 발생하는데, 이때 사용하는 알고리즘이 페이지교체 알고리즘이다.
알고리즘에는 FIFO, LFU, LRU 등이 있다.
가상 메모리의 페이지 테이블에는 페이지가 물리 메모리에 있는지, 스왑 영역에 있는지 표시하는 유효 비트를 사용한다.
프로세스가 페이지를 요청했을 때 그 페이지가 메모리에 없는 경우를 페이지 폴트
라고 한다.
페이지 폴트가 발생하면 프레임을 새로 할당 받아야 하며, 프로세스가 해당 페이지를 사용할 수 있도록 스왑 영역에서 물리 메모리로 옮겨야 한다. 그리고 페이지 테이블을 재구성 하고, 프로세스의 작업을 재시작한다.
프로세스는 자원을 할당받는 작업의 단위이며 (쉽게 실행되는 프로그램이라고 볼 수 있고)
스레드는 프로세스가 할당받은 자원을 이용하는 실행의 단위라고 볼 수 있습니다.
스레드는 자원을 공유한다는 점에서 차이가 있습니다.
(*N사 개발자의 답변 )
멀티 프로세스는 카카오톡, 인텔리제이, 크롬 등 여러가지 프로그램을 함께 실행시키는 것이라고 본다면
멀티 스레드는 실행되는 인텔리제이에서 코드를 수정하는 동시에 추천 코드가 보여지는 기능이 함께 실행되는 것이라고 볼 수 있습니다.
(스레드라는 것 자체가 프로세스라는 한 가구 안에 거주하는 세대원이기 때문에, 프로그램이라는 한 가구 안에서 작업을 수행하는 흐름의 단위인 여러 세대원들이 각자의 역할을 하는 것이라고 볼 수 있습니다.)
멀티 스레드는 프로세스를 생성하여 자원을 할당하는 시스템 콜이 감소함으로서 자원을 효율적으로 관리할 수 있다. 또한 프로세스 간 통신(IPC)보다 스레드 간의 통신 비용이 더 적게 발생한다.
다만 멀티 스레드 사용 시에는 공유 자원으로 인한 문제를 해결하기 위해 동기화를 더 신경써 주어야 한다.
동시성은 멀티 작업을 위해 싱글 코어에서 여러 개의 쓰레드가 번갈아 실행하는 것이고,
병렬성은 멀티 작업을 위해 멀티 코어에서 한 개 이상의 쓰레드를 포함하는 각 코어들을 동시에 실행되는 것이다.
다수의 쓰레드가 공유 데이터에 동시에 접근하는 경우, 동시성 문제 또는 교착 상태가 발생하지 않도록 상호배제 또는 동기화 기법을 통해 주의해야 한다.
Context Switching 은 한 개의 CPU가 하나의 프로세스만 처리할 수 있기 때문에 필요해진 개념입니다.
실행중인 기존 프로세스를 우선순위의 새 프로세스로 교체하는 작업입니다.
한 프로세스가 끝날때까지 기다리면 병목 현상이 발생할 수 있기 때문에 필요하기도 하지만, Context Switching을 통해 프로세스를 시분할로 교체하여 프로그램을 동시에 실행되는 것처럼 보이게 만듭니다.
시험이 끝나고 학생이 선생님에게 시험지를 건냈습니다.
이때 동기식은 채점이 끝나 시험지를 돌려받기만을 기다렸다가 돌려받은 후 다른 일을 하는 것이라면,
비동기식은 선생님이 채점하는 동안 다른 과목을 공부하거나 게임을 할 수 있는 것의 차이가 있습니다.
그리고 이때 돌려받기만을 기다려야하는 것을 Blocking 이라고 하고, 채점 결과를 나중에 알려줄테니 다른 일을 해도 되는 것을 Non-Blocking 이라고 봅니다.
Blocking Sync의 경우 자바에서 scanner로 입력 요청을 하는 경우를 예로 들 수 있고,
Non-Blocking Async의 경우 자바스크립트에서 API 요청을 하고, 다른 작업을 하다가 콜백(Callback)을 통해서 추가적인 작업을 처리할 때 사용하는 경우를 예로 들 수 있습니다.
은행 계좌 처리같은 경우, 한 사람이 ATM기기에서 돈을 출금했는데 그 계좌 잔고의 변화가 즉각 갱신이 일어나지 않으면 이것이 조금씩 커져 결국 전체 시스템의 문제로 발생할 수 있다. 이런 경우 동기 처리가 적합하다.
비동기처리의 경우 사용자의 동시 다발적인 요청에 빠르게 응답해야할때 필요합니다.
그 예로 Paypal 에서 front 서비스를 Node.js 로 변경하면서 엄청난 코드 품질 향상, 성능 향상이 있었다고도 알고 있습니다.
LRU 캐시는 Least Recently Used 캐시로, 캐시 메모리가 다 차면 가장 오랫동안 사용되지 않았던 캐시를 메모리에서 삭제하는 알고리즘입니다. 캐시를 교체하는 알고리즘에는 FIFO, LFU, LRU 등이 있습니다.
(*N사 개발자의 답변 )
큰 문제를 작게 나누어 동시에 해결할 때 병렬 프로그래밍을 사용합니다.
큰 문제를 프로세스 혹은 스레드가 나누어 처리하기 때문에 처리 속도가 향상된다는 장점이 있습니다.
이때 큰 문제를 일정량씩 나누고, 각 분리된 테스트를 어떻게 배정해서 처리할 것인지에 대한 고민이 필요하고, 여러 스레드가 하나의 데이터를 공유한 경우를 유의해야합니다.
(+ 멀티 프로세싱, 멀티 쓰레딩 구현 경험 있으면 설명)
(*N사 개발자의 답변 )
뮤텍스는 한 스레드, 프로세스에 의해 소유될 수 있는 key를 기반으로 합니다.
반면 세마포어는 현재 공유자원에 접근할 수 있는 스레드, 프로세스의 수를 나타내는 값을 기반으로 합니다.
뮤텍스나 세마포어는 쓰더라도 상호배제만 해결되어 데드락이 발생할 수 있습니다.
(*N사 개발자의 답변 )
[참조]
https://gomoveyongs.tistory.com/67 /process
https://xenostudy.tistory.com/230 /thread
https://profq.tistory.com/36 /임계구역
http://www.appleofeyes.com/%EB%AE%A4%ED%85%8D%EC%8A%A4-%EC%84%B8%EB%A7%88%ED%8F%AC%EC%96%B4-%EB%A9%80%ED%8B%B0%EC%93%B0%EB%A0%88%EB%93%9C/ /뮤텍스,세마포어 (Re 이해)
https://mangkyu.tistory.com/92 /가상메모리
https://velog.io/@wonhee010/%EB%8F%99%EA%B8%B0vs%EB%B9%84%EB%8F%99%EA%B8%B0-feat.-blocking-vs-non-blocking
https://makefortune2.tistory.com/188
https://velog.io/@minsu8834/About-%EB%8F%99%EA%B8%B0-%EB%B9%84%EB%8F%99%EA%B8%B0
https://asfirstalways.tistory.com/348 /Sync,Async
https://zabdahan.tistory.com/entry/%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%84%9C%EB%B2%84%EC%97%90-%EB%8C%80%ED%95%9C-%EA%B3%A0%EC%B0%B0 /java 동기비동기
https://devowen.com/289#%ED%--%--%EB%A-%-C%EC%--%B-%EC%-A%A-%EC%--%--%--%EC%-A%A-%EB%A-%--%EB%--%-C%EC%-D%--%--%EC%B-%A-%EC%-D%B-%EA%B-%--%--%EB%AC%B-%EC%--%--%EC%-D%B-%EA%B-%--%EC%-A%--%-F%---N%EC%--%AC%--%EC%A-%--%ED%--%--%EB%A-%B-%EC%A-%--- /k,n사
https://dev-coco.tistory.com/162 /os 면접질문
10분 테코톡 Process vs Thread