Chromium 멀티 프로세스 아키텍처

오현재·2025년 2월 3일

Chromium 의 멀티 프로세스 아키텍처에 대해 알아보기 전에, Chromium 이 무엇인지, 멀티 프로세스 가 무엇인지에 대해 간략하게 알아보자.

Chromium(이하 크로미움) 은 우리가 흔히 사용하는 브라우저인 Chrome(크롬) 포함하여 대부분의 브라우저(Edge, Opera, Brave, 삼성 인터넷 등) 의 기반이 되는 오픈 소스 브라우저 프로젝트이다. 따라서 해당 브라우저들은 크로미움의 코드를 기반으로 개발되어, 크로미움의 핵심 기술을 공유한다.

멀티 프로세스 는 하나의 프로그램에 대해 동시에 여러 개의 프로세스를 실행할 수 있게 하는 기술이다. 따라서 멀티 프로세스 환경의 OS 혹은 프로그램에서는, 하나의 프로세스(부모 프로세스) 에서 여러 독립적인 프로세스(자식 프로세스) 를 생성하는 구조를 띈다.

멀티 프로세스의 장점

사실 멀티 프로세스는 현대 OS 에서 대다수를 차지하는 구조이다. 멀티 프로세스는 하나의 프로그램을 여러 독립적인 프로세스로 분산시켜 실행하는 점 자체에서 많은 장점을 가지기 때문이다.

예를 들어 하나의 프로그램이 하나의 프로세스에만 의존하여 실행되고 있다고 생각해보자. 하나의 프로세스에 오류가 생겨 종료되거나, 외부에서 프로그램에 대한 악의적인 공격이 실행되었을 때, 해당 프로그램에 대한 안정성을 확보할 수 없게 된다.

하지만 여러 독립적인 프로세스로 나누어진 형태의 멀티 프로세스 환경이라면, 하나의 프로세스에 대한 오류나 공격에도, 나머지 다른 프로세스들은 독립적인 공간을 가지기 때문에 영향을 받지 않거나 최소화 할 수 있다.

이와 같이 프로그램의 안정성, 보안 등 을 확실하게 하기 위해 멀티 프로세스가 현대 OS 에서 필수적으로 사용되고 있다.

Chromium 이 멀티 프로세스 아키텍처를 선택한 이유? (안전성, 빠름, 안전)

웹 브라우저를 구축해야하는 표준 사양은 없다. 그렇다면 왜 크로미움은 멀티 프로세스 아키텍처를 선택했을까?

구글 개발자 유튜브 계정인 Google for Developers 에서는 크로미움 의 멀티 프로세스 아키텍처에 대해 설명하는 영상이 있는데, 도입부에서 이에 대한 답을 간략하게 들어볼 수 있다.

우리가 추구했던 것은 안정적이고, 빠르며, 안전한 브라우저를 만드는 것이었다. 그리고, 이 모든 측면을 개선할 수 있는 가장 좋은 방법중 하나는 분할하는 것, 즉 브라우저를 여러 개의 프로세스로 나누는 것임을 깨달았다.

크로미움이 등장한 2009년 이전인, 2006년경 브라우저들은 과거의 단일 사용자, 멀티태스킹 OS 와 비슷했다고 볼 수 있다. 그러한 OS 에서 오작동하는 프로그램이 전체 시스템을 다운시킬 수 있듯, 당시 브라우저들은 오작동하는 탭(페이지) 가 브라우저 전체를 다운시킬 수 있었다.

하지만 멀티 프로세스 아키텍처의 브라우저에서는 각 탭(페이지) 가 독립적인 프로세스에 의해 실행된다. 그렇기에 하나의 프로세스, 즉 하나의 탭(페이지)의 상태가 브라우저의 상태에 영향을 끼치지 않게된다.

이제 크로미움 멀티 프로세스 아키텍처에 대해 알아보며, “안정성”, “빠름”, “안전” 과 같은 속성이 어디서 나올 수 있었는지 알아보자.

멀티 프로세스 아키텍처 개요

다음은 이미지는 크로미움의 멀티 프로세스 아키텍처를 구성하고 있는 프로세스들을 나타낸다. 각 프로세스가 어떤 기능을 담당하고 제어하는 지 간략하게 살펴보자.

브라우저 프로세스

가장 상위 에서 렌더러 및 기타 프로세스를 관리하는 주 프로세스 이다. UI(주소 표시줄, 북마크, 뒤로 버튼) 를 실행하고 네트워크 요청 및 파일 액세스 와 같은 기능도 담당한다.

브라우저의 각 탭(페이지) 에서 브라우저 프로세스 는 공유 된다.

렌더러 프로세스

페이지에서 표시되는 모든 요소, 즉 HTML 을 처리한다. 렌더러 프로세스는 HTML 을 해석하기위해 Blink 라는 오픈 소스 레이아웃 엔진 을 사용한다.

브라우저의 각 탭(페이지) 마다 하나 이상의 독립적인 렌더러 프로세스 가 생성된다.

공유되는 렌더러 프로세스

때로는 여러 탭 에서 하나의 렌더러 프로세스를 공유하는 경우도 있다.

예를 들어, 웹 애플리케이션 은 하나의 페이지에서 window.open 을 사용하여 다른 페이지를 만들 수 있다. 이와 같이 새로운 페이지가 같은 출처에 속한다면, 같은 프로세스를 공유해야한다.

또 다른 예로, 브라우저의 총 프로세스 수가 너무 많을 경우, 새 탭을 기존 프로세스에 할당하는 전략을 가지고 있다.

샌드박싱을 통한 보안 (안전)

렌더러 프로세스는 별도의 환경인 샌드박스 내에서 실행된다. 이러한 샌드박싱을 통해 시스템 리소스 접근을 제한할 수 있다.

샌드박싱은 격리된 환경을 의미한다. 이렇게 격리된 샌드박스 환경은 시스템의 운영 체제, 설치 프로그램 등 외부 영향을 주거나 받지 않는다.


브라우저 프로세스와 렌더러 프로세스는 멀티프로세스 아키텍처 내에서, 서로 통신을 한다. 이후에 자세히 살펴볼 것인데, 그 전에 서로 통신을 하는 과정에서 등장하게 되는 객체에 대해 간략하게 알아보자.

플러그인 프로세스

웹사이트에서 사용하는 플러그인을 제어한다. 플러그인이라 하면, 브라우저 플러그인을 의미한다.

브라우저 플러그인은 브라우저가 기본적으로 지원할 수 없는 콘텐츠를 처리하기 위해 사용자가 설치할 수 있는 소프트웨어 구성요소입니다.

GPU 프로세스

GPU 작업을 위한 프로세스 이며, 다른 프로세스들과 별도로 처리된다.

프로세스 간 IPC 를 활용한 비동기 통신 (안정성)

앞서 크로미움 멀티 프로세스 아키텍처를 구성하는 프로세스들에 대해 알아보았다. 프로세스들은 각각 기능을 다하기 위해 서로 통신한다. 예를 들어 브라우저 프로세스와 렌더러 프로세스는 서로 통신 한다. 크로미움에서는 그 방법으로 IPC(Inter-Process Communication) 을 사용한다.

비동기 통신의 필요성

처음 크로미움 프로젝트를 시작할때, 많은 사람들은 IPC 가 아닌 COM 을 사용하길 원했다고 한다. 이유는 IPC 의 문제점들을 해결하고, 무료이기 였다는 것. COM 은 기본적으로 동기적으로 동작했고, 일부 비동기적으로 동작할 수 있었지만 성능이 좋지 않았다.

하지만, 구글이 원하는 것은 완전한 비동기 모델 이었다.

예를 들어, 브라우저 프로세스와 렌더러 프로세스가 통신하는 도중 (1) 에 Webkit(렌더링 엔진) 에서 브라우저 프로세스와 통신 (2) 이 필요하다고 쳐보자.

Webkit 에 대해 간략히 설명하면, 현재 Blink 로 대체된 초창기 Chromium 의 렌더링 엔진 이었다. 따라서 크로미움의 렌더링 엔진 을 말하는 것으로 생각하면 된다. 또한, Webkit 은 RenderView 와 연결되어있어, 최종적으로 브라우저 프로세스와 IPC 통신이 가능하다.

만일 동기적으로 동작한다면, (1) 이 종료되어야, (2) 가 가능하다는 의미가 된다. 하지만, 만약 렌더링 엔진 혹은 RenderView 에서 브라우저 프로세스와의 통신이 빈번하게 필요하다면, 이는 안정성의 문제로 이어질 것이다.

구글은 이 지점에서 브라우저 프로세스가 항상 끊임없이 반응하길 원했고, 따라서 비동기적으로 동작하는 IPC 를 선택한 것이다.

앞서 얘기했던, “안정성” 은 이러한 속성에 해당한다. 프로세스 간의 통신을 비동기적으로 동작하게 하여, 지연없이 끊임없이 이루어지게 하는 것이다.


다음 항목에서, 브라우저 프로세스와 렌더러 프로세스의 IPC 통신을 도식화한 다이어그램을 살펴보며, 브라우저 프로세스와 렌더러 프로세스를 구성하는 객체 와 IPC 통신의 흐름에 대해 간략하게 살펴보자.

그런데, 각 프로세스는 앞서 살펴본대로 브라우저가 실행되는 동안 처리해야할 작업들이 따로 있다. 이러한 작업들을 처리하며, 동시에 어떻게 다른 프로세스와 통신을 할 수 있는 것일까? 이는 프로세스가 멀티 스레드 구조를 가짐으로써 가능하다.

멀티 스레드 구조의 프로세스

모든 크로미움의 프로세스는 다음과 같은 멀티 스레드 구조이다. 다음 스레드 중, I/O 스레드에서 다른 프로세스와 통신 하게 된다.

  • 메인 스레드
    • 브라우저 프로세스에서 UI를 업데이트한다.
    • 렌더러 프로세스에서(Blink 메인 스레드) Blink의 대부분을 실행한다.
  • I/O 스레드
    • 모든 IPC 메시지는 이 스레드에서 수신된다. 메시지를 처리하는 애플리케이션 로직은 다른 스레드에 있을 수 있다.
  • 몇 개의 특수 목적 스레드
  • 일반 목적 스레드 풀

출처 : https://chromium.googlesource.com/chromium/src/+/HEAD/docs/threading_and_tasks.md#Threads


다음 다이어그램을 살펴보며, IPC 통신이 어떤 스레드를 통해 어떤식으로 이루어지는 지 전체적인 흐름을 살펴보자.

IPC 통신의 전체적인 흐름

다음은 브라우저 프로세스와 렌더러 프로세스의 관계를 나타내는 다이어그램이다. 앞서 살펴본 대로, 각 프로세스가 I/O 스레드로 나누어져있는 것을 확인 할 수 있다.

브라우저 프로세스에서의 IPC

앞서 살펴보았듯, 브라우저 프로세스 내에서 렌더러 프로세스와의 통신은 별도의 I/O 스레드에서 이루어진다. 뷰 로부터 오가는 메시지는 ChannelProxy 라는 것을 사용하여 브라우저 프로세스의 메인 스레드로 프록시 된다. 이 방식의 장점은 성능이 중요한 메시지인 리소스 요청(웹 페이지 등) 을 I/O 스레드에서 전적으로 처리할 수 있어 사용자 인터페이스를 차단하지 않는다는 것이다. 이는 RenderProcessHost가 채널에 삽입하는 ChannelProxy::MessageFilter 의 사용을 통해 이루어진다.

위의 다이어그램과 함께 브라우저 프로세스 내부에서 렌더러 프로세스와의 통신을 위한 객체들을 살펴보자.

  • RenderViewHost : 각 렌더러 프로세스의 RenderView 들에 대응하여 생성 된다. 예를 들어, 총 RenderView 의 개수가 5개라면, 브라우저 프로세스의 RenderViewHost 또한, 5개 생성된다.
  • RenderProcessHost : 렌더러 프로세스의 RenderProcess 와 통신한다. 하나 이상의 RenderViewHost 들과 통신하며, 브라우저 프로세스는 정확히 하나의 RenderProcessHost 객체를 가진다.

렌더러 프로세스에서의 IPC

기본적으로 각 렌더러 프로세스마다 브라우저 프로세스와의 통신을 위해 명명된 파이프 라는 것이 할당된다. 파이프는 비동기 모드로 사용되어, 비동기적인 IPC 통신의 구현을 가능하게 한다.

다음은 렌더러 프로세스 내부에서 브라우저 프로세스와의 통신을 위한 객체들이다.

  • RenderView : 웹 페이지를 구성하고 표현하는 역할을 한다. 따라서, 렌더러 프로세스에서 핵심적인 역할을 한다고 볼 수 있다. 하나의 렌더러 프로세스에는 하나 이상의 RenderView 가 존재할 수 있다.
  • RenderProcess : 브라우저 프로세스와 통신하는 객체 이다. 정확하게 말하면 브라우저 프로세스의 RenderProcessHost 와 서로 통신한다. RenderProcess 는 하나 이상의 RenderView 들과 통신하며, 렌더러 프로세스에는 하나의 RenderProcess 만이 존재한다.

즉 간단하게 말하면, 렌더러 프로세스의 하나 이상의 RenderView 와 통신하는 RenderProcess 와, 브라우저 프로세스의 하나 이상의 RenderViewHost 와 통신하는 RenderProcessHost 가 서로 IPC 통신을 하는 것이다.

IPC 통신을 통한 충돌하거나 오작동하는 렌더러 프로세스 감지

브라우저 프로세스는 IPC 통신을 통해 렌더러 프로세스의 핸들을 감시한다. 핸들이 신호를 받으면 렌더러 프로세스가 충돌했다고 판단하고, 해당 탭에 충돌을 알린다.

크로미움 은 이러한 렌더러 프로세스의 충돌 이 발생하면, “슬픈 탭” 또는 “슬픈 프레임” 이미지를 보여주며, 렌더러 프로세스가 없음을 감지하여 새 프로세스를 생성한다.


출처 : https://unitstep.net/blog/2008/09/02/google-chrome-what-it-offers/

References

Chromium's multi-process architecture

The Chromium Projects

[Chromium] How Chromium Displays Web Pages

Chromium Docs

profile
안녕하세요. 환영합니다. 프론트엔드 개발자 오현재입니다.

0개의 댓글