

프로세스는 실행중인 프로그램이며, 운영체제로부터 독립된 메모리 공간을 할당받는다. 메모리 공간은 Code/Data/Heap/Stack 영역으로 이루어져있다.
new, C의 malloc 키워드 등이 해당된다. 프로세스는 운영체제의 CPU 스케줄링의 대상이며, 컨텍스트 스위칭(Context Switching)을 통해 여러 프로세스가 CPU를 번갈아 사용한다.
컨텍스트 스위칭은 CPU가 하나의 프로세스에서 다른 프로세스로 전환될 때, 이전 프로세스의 레지스터 값, 프로그램 카운터(PC), 스택 포인터 등의 상태를 저장하고, 새로운 프로세스의 상태를 복원하는 작업을 말한다. 컨텍스트 스위칭은 오버헤드가 발생하므로, 적절한 스케줄링 알고리즘이 필요하다.
또한 프로세스는 각각 독립된 메모리 공간을 가지기 때문에, 데이터를 주고 받기 위해 IPC(Inter-Process-Communication) 기법을 사용해야한다.
IPC에는 다음과 같은 방식들이 존재한다.
Message Queue
: 운영체제가 관리하는 큐를 통해 프로세스 간 데이터를 교환하는 방식.
프로세스는 큐에 메시지를 쓰거나 읽어서 데이터를 주고받는다.
Shared Memory
: 두 프로세스가 동일한 메모리 영역을 공유하여 데이터를 주고받는 방식
Socket
: 네트워크 통신에서 주로 사용되는 소켓은, 같은 호스트의 프로세스 간 통신에도 사용할 수 있다.

스레드는 하나의 프로세스 내에서 동시에 진행되는 실행 흐름의 단위이다.
각각의 스레드는 독립된 Stack 영역과 레지스터 값을 보유하며, Code, Data, Heap 영역을 공유한다.
스레드간의 메모리를 공유하기 때문에, 프로세스간 통신(IPC)보다 오버헤드가 적다.
다만, 공유 자원 문제로 인해 경쟁 상태, 데이터 불일치 등의 문제가 발생할 수 있다.
공유 자원 문제란, 여러 스레드가 동시에 하나의 자원에 접근하거나 수정할 때 발생하는 문제로, 예상치 못한 동작이나 데이터 손상을 초래할 수 있는 상황을 말한다.
이러한 문제를 방지하기 위해 적절한 동기화 메커니즘을 사용하는 것이 중요하다.
크롬 브라우저에서는 각각의 페이지마다 탭이 존재하여 손쉽게 전환이 가능하다.
웹페이지 화면을 렌더링하는 각각의 탭을 직접 구현한다면, 프로세스와 스레드를 어떻게 활용해야할까?
하나의 프로세스에서 여러 스레드를 생성하여 모든 탭을 관리하는 구조, 혹은 각 탭을 독립된 프로세스로 생성하는 구조로 설계할 수 있을 것이다.
각각의 장단점을 비교해보자.
장점
메모리 효율성
: 모든 스레드가 Code, Data, Heap 영역을 공유하므로 각 탭마다 프로세스 전체 메모리 공간을 할당하는 방식보다 사용량이 적다.
빠른 통신
: 공유메모리를 통한 스레드 간의 통신은 IPC에 비해 훨씬 빠르다.
단점
안정성 저하
: 만약 하나의 스레드에 문제가 발생하면, 다른 스레드도 제대로 동작하지 않을 수 있다.
병목현상
: 공유 자원에 접근할 때 동기화 문제가 발생할 수 있으며, 이를 해결하기 위해 락을 많이 사용하면 성능이 저하될 수 있다.
장점
단점
높은 메모리 사용량
: 각 탭이 독립된 메모리 공간을 가지기 때문에 메모리 사용량이 증가합니다.
복잡한 통신
: 프로세스 간 통신은 IPC를 사용해야 하므로, 스레드 간 통신에 비해 구현이 복잡하고, 성능이 낮을 수 있다.
하나의 스레드에 문제가 발생하면, 다른 스레드가 제대로 동작하지 않는 이유는 무엇일까? 하나의 스레드가 다른 스레드에 어떤 문제들을 발생 시킬 수 있는 것인가?
앞서 보앗듯, 스레드끼리는 Code, Data, Heap 영역을 공유하는데, Heap 영역은 프로세스 실행 도중 동적으로 크기가 변하는 영역이다. 만약 하나의 스레드가 이를 과도하게 할당하여 사용한다면?
-> 메모리 부족 에러가 발생하여 다른 스레드가 더 이상 메모리를 사용할 수 없게 될 것이다.
또 만약 하나의 스레드가 특정 공유자원에 대한 락을 점유한 상태로 무한루프에 빠지거나 과도한 연산을 요청하여 응답하지 않는다면?
-> 이를 획득해야하는 다른 스레드는 데드락 상태에 빠지고 무한 대기 상태에 빠질 수 있다.
이와 같은 이유로 하나의 프로세스 + 여러 스레드 구조에서는 하나의 스레드(탭)가 트롤짓을 한다면 전체 스레드(전체 탭)에 문제가 생길 가능성이 있다.
그렇다면 하나의 탭이 문제를 일으킬 가능성을 따져봐야한다.
웹서버가 제공한 HTML, CSS, JavaScript 코드는 브라우저 내에서 실행되며, 이 코드는 웹 개발자가 작성한 로직에 따라 동작하므로 브라우저 입장에서는 실행 결과를 예측할 수 없다.
특히, JavaScript는 클라이언트 측에서 실행되며, 무한 루프, 과도한 메모리 사용, 대규모 이벤트 처리 등 의도치 않은 동작(트롤짓)을 유발할 가능성이 있다.
특정 웹페이지의 동작 방식을 사전에 예측할 수 없기 때문에, 하나의 프로세스가 여러 탭을 관리하는 구조는 안정성 측면에서 매우 취약하다.
따라서 각각의 탭을 렌더링하는 기능은 다중 프로세서로 설계하는 것이 적합하다고 생각한다.


실제로 크롬 브라우저 아키텍처는 위와 같은 구조로 되어있다고 한다.
각각의 탭을 렌더링하는 '렌더러 프로세스' 외에도 '브라우저 프로세스', '플러그인 프로세스', 'GPU 프로세서' 등이 함께 동작한다.
다만 사진에서 볼 수 있듯, 렌더러 프로세스는 탭별로 프로세스를 할당하는 구조로 되어있다.
한 탭이 응답하지 않더라도 다른 탭은 사용 가능하다는 점은 각 탭마다 독립적인 렌더러 프로세스를 유지했을 때의 이점이다.
웹 페이지에서 처리할 작업이 많아 응답하지 못하는 경우나 웹 페이지를 담당하던 렌더러 프로세스의 실행이 중단된 경우 등에 이런 이점을 확인할 수 있다.
안정성 확보 측면 외에도 보안과 격리(sandbox)의 이점도 있다고한다.
운영체제를 통해 각각의 프로세스의 권한을 제한한다면, 특정 프로세스가 특정 기능을 사용할 수 없게 제한할 수 있다.
예를 들어, 웹서버의 코드가 실행되는 렌더러 프로세스와, 쿠키를 저장하고 관리하는 스토리지 프로세스(Storage Process)가 분리되어 있음으로써 보안과 안정성을 강화할 수 있다.
이러한 구조에서는 렌더러 프로세스에서 실행되는 예측 불가능한 코드가 쿠키와 같은 민감한 데이터에 직접 접근하지 못하게 된다.
또한, 스토리지 프로세스는 렌더러 프로세스의 충돌이나 비정상적인 동작으로부터 격리되어, 데이터를 안전하게 보호하고 브라우저의 안정성을 유지할 수 있다.
사진출처 : Chrome for Developers - Inside look at modern web browser (part 1)