운영 체제에서 가상메모리와 프로세스가 매우 중요하다. OS가 접근제어를 할때, File이나 자원에 대해서 처리를 하려면 프로세스라는 단위를 통해 권한을 취한다.
프로세스에게 가상메모리가 주어진다. 스레드는 안에서 자유롭게 활동할 수 있다. OS에서 새로운 프로세스를 생성했을 때, 독립적인 가상메모리 공간을 가져야한다. 그래서 프로세스 마다 OS가 할당을 해줘야한다.
프로세스의 생성과 복사는 가상메모리를 어떻게 운용하는지와 밀접한 관계가 있다. Win32API / UNIX는 운영체제가 달라서 프로세스를 생성하는 방식 또한 차이가 있다.
프로세스 생성 시 메모리를 복사한다. 이 과정에서 코드를(text) 복사하며 PCB가 생성되는데 이 과정에서 이비 VMS(Heap/Stack/Data)가 만들어진다.
프로세스의 생성은 많은 시간과 자원을 할당해야하기에, UNIX에는 fork() / exec() 두 방식이 있다. fork()방식은 기존의 부모 프로세스를 그대로 복사해온다. 그리고 text부분과 레지서트와 상태정보만 리셋시켜주면 새로운 프로세스로서 사용할 수 있다. exec()은 새로운 프로세스를 만들지 않고, 기존의 프로세스를 그대로 이용하고 기존 코드를 새로운 코드로 갱신해준다. exec()을 사용할 수 있다면 exec을 사용하는 것 을 권장한다.
프로세스를 여러개 사용하면 멀티태스킹 이라고 부른다. 멀티로 프로세스를 사용한다면 동기화 이슈가 일어난다. race condition이 이러한 병목현상이 발생할 때 고려해야된다.
CPU의 자원을 선점하는 것은 Thread다(사용하는 역할). CPU의 코어를 몇개까지 할당하는지는 운영체제가 프로세스를 통해 수행한다. 그래서 스레드는 프로세스의 권한 또는 리소스 할당에 제한을 받는다.
Stack은 스레드 마다 갖고있는 영역이다. Stack의 메모리는 특별한 설정을 안해주면 1MB정도로 충분한 공간을 갖고있다. TCB 단위로 스레드 연산이 실행되는데, 이 연산은 CPU core에서 실행되는데, 이 코어 내부에 레지스터가 있다. 코어의 레지스터의 상태를 변경하는 행위를 Context Switching(문맥교환)이라고 한다. 문맥교환은 프로세스와 똑같이 상태가 있다.
프로세스는 적어도 한개의 스레드를 가지고 있다. 그래서 프로세스가 슬립한다는 건 스레드가 슬립한다 라고 생각하는게 좀 더 현실성이 있다. GUI 또는 내부의 처리가 있을 수 있는데, 이를 보통 각각 여러개의 스레드로 분리한다. 그렇다면 개별적인 흐름이 존재할 수 있는데, 그렇기에 동기화 이슈가 매우 중요하다.
멀티스레드를 사용하면 어떤 CPU코어위에서 연산을 진행할지 모르기에, 우연이라는 개념이 나올 수 밖에 없다. 그래서 sleep을 이용하면 우연을 줄일 수 있는데, 얼만큼 대기 상태를 유지해야될지도 명확히 알 수 없다. 멀티스레드의 동기화를 잘 하기위해선 상태를 잘 관리해줘야 한다. (sleep(0)을 이용해서 대기열의 뒤로 이동시키는 테크니션도 있다.)
코드에서 초반엔 sleep을 최소화 하면서 우연의 발생 확률을 낮추는 작업을 보여줬다. .set event와 같은 방식으로, 하나의 스레드 흐름이 끝났을 때 그 다음 작업을 linked하는 방식으로 동기화를 처리해줄 수 있지 않을까 싶다. 하지만 sleep을 이용하면 우연을 일으킬 확률이 높기때문에, 얼마나 잘 다룰 수 있냐에 따라 프로세스를 사용하는 역량을 파악할 수 있다. 리액트로 치면 렌더를 위한 상태관리 역량과 일맥상통하는 것 같다.
유익한 글이었습니다.