블로그로 돌아가기
·8분 분량·productdevbook

도우미 프로세스가 macOS 대역폭 모니터를 헷갈리게 하는 이유

도우미 프로세스는 사용자에게 보이는 앱 하나를 네트워크 행 수십 개로 쪼개 놓습니다. macOS에서 왜 이런 일이 생기는지, 어떻게 이를 꿰뚫어 볼지 다룹니다.

  • macOS
  • Bandwidth
  • Network monitoring
  • Deep dive

조용한 오후에 활성 상태 보기를 열고, 네트워크 바이트로 정렬하면, 목록 상단이 다음과 같이 보입니다. Google Chrome Helper (Renderer), Google Chrome Helper (GPU), Google Chrome Helper, Slack Helper, Slack Helper (Renderer), Slack Helper (GPU). "Chrome"이나 "Slack"의 행은 없습니다 — 그저 각각 초당 몇백 킬로바이트를 끌어오는 도우미들의 색종이 조각입니다. 어떤 앱이 실제로 통신하고 있나요? 그 혼란이 macOS에서 네트워크 모니터를 만들 때 Mac 도우미 프로세스 대역폭 귀속이 가장 어려운 부분인 전체 이유입니다.

이 글은 앱이 처음에 도우미를 생성하는 이유, 커널이 그것들의 네트워크 사용을 별도로 보고하는 이유, 그리고 도구가 깨끗한 앱별 숫자를 제공하려 할 때 "접기"가 무엇을 의미하는지 설명합니다.

앱이 도우미 프로세스를 생성하는 이유

현대 macOS 앱은 거의 절대 단일 프로세스로 실행되지 않습니다. 세 가지 큰 이유가 있습니다.

샌드박싱과 권한 분리

Apple의 App Sandbox는 프로세스별 경계입니다. 신뢰할 수 없는 HTML을 파싱하거나 JavaScript를 실행하는 렌더러는 마이크 권한을 소유한 상위와 다른 자격 세트로 실행됩니다. 렌더러가 악성 페이지에 의해 침해되더라도, 폭발 반경은 그 프로세스에서 멈춥니다 — 갑자기 오디오를 녹음하거나 문서 폴더를 읽기 시작할 수 없습니다. 이것이 Chrome과 Safari가 모두 각 탭이나 사이트에 별도의 프로세스와 함께 출시되는 이유이며, Slack, Discord, Notion(모두 Electron 앱)이 같은 패턴을 따르는 이유입니다.

충돌 격리

Chrome의 한 탭이 메모리가 부족하거나 JavaScript 엔진 버그를 만나면, 브라우저의 모든 탭이 그것과 함께 죽기를 원하지 않습니다. 도우미 프로세스는 단일 나쁜 페이지가 그 탭에 대해 "아 이런" 화면을 보여주고 다른 어떤 것도 보여주지 않는다는 것을 의미합니다. 같은 논리가 한 채널을 렌더링하는 Slack이나 음성 통화를 렌더링하는 Discord에 적용됩니다.

Electron과 Chromium 아키텍처

앱이 Electron으로 만들어졌다면 — Slack, Discord, VS Code, Notion, Linear, Figma desktop, 1Password 8, Microsoft Teams, Postman — Chromium의 다중 프로세스 모델을 통째로 상속합니다. 하나의 "메인" 프로세스, 하나의 "GPU" 프로세스, 하나의 "네트워크 서비스" 프로세스(종종 유틸리티라고 불림), 그리고 각 브라우저 창이나 의미 있는 뷰당 하나의 렌더러 프로세스가 있습니다. 렌더러는 절대 자체적으로 소켓을 열지 않습니다. IPC를 통해 네트워크 서비스와 통신하고, 네트워크 서비스가 실제 TCP 핸드셰이크를 합니다.

그 마지막 세부사항은 들리는 것보다 더 중요합니다.

Mac 도우미 프로세스 대역폭 귀속이 어려운 이유

macOS 커널은 socket(2)를 호출한 다음 connect(2)/sendto(2)/recvfrom(2)를 호출한 어떤 PID에든 바이트를 귀속시킵니다. 그것이 커널의 적절한 답입니다 — "Slack"이 제품으로서 무엇을 의미하는지 모릅니다. PID 4711이 34.117.x.x:443에 소켓을 열었다는 것만 압니다.

그래서 nettop이나 lsof -i나 원시 proc_pidinfo API에 "누가 네트워크 바이트를 사용했는가"를 물으면, PID의 평면 목록을 받습니다. 각 PID는 프로세스 이름(디스크의 실행 파일 이름)을 가지고, 그 이름은 거의 항상 다음과 같은 무언가입니다.

  • Google Chrome Helper
  • Google Chrome Helper (GPU)
  • Google Chrome Helper (Renderer)
  • Google Chrome Helper (Plugin)
  • Slack Helper
  • Slack Helper (Renderer)
  • Slack Helper (GPU)
  • com.docker.backend

순진한 모니터 — 그저 커널의 뷰를 화면에 덤프하는 — 는 정확히 그것을 보여줍니다. "이번 시간에 Chrome이 얼마나 사용했는가?"에 답하기 위해 정신적으로 더해야 하는 일곱 개의 Chrome 행이 보입니다. 더 나쁘게도, 렌더러는 단명합니다. 탭을 닫으면 렌더러가 죽습니다. 새 탭을 열면 새 PID로 새 렌더러가 나타납니다. 한 시간 동안 합산하면 답은 수십 개의 죽은 PID에 걸쳐 분산됩니다. 그것이 Mac 도우미 프로세스 대역폭 귀속의 핵심 도전입니다. 커널이 노출하는 단위가 사람이 생각하고 싶어 하는 단위와 일치하지 않습니다.

"접기"가 무엇을 의미하는가

접기는 도우미 프로세스를 — 번들 경로, 부모 PID, 또는 둘 다로 — 보고 그것의 바이트를 그것이 속한 사용자가 보는 앱에 귀속시키는 행위입니다. 제대로 하면, 11개의 Chrome 행을 보는 것을 멈추고 합계가 있는 "Google Chrome"이라고 불리는 한 행을 보기 시작합니다.

몇 가지 방법이 있고, 각각 트레이드오프가 있습니다.

번들 경로별

모든 도우미는 상위 앱의 번들 안에 살고 있습니다. Chrome의 경우 그 경로는 다음과 같은 무언가입니다.

/Applications/Google Chrome.app/Contents/Frameworks/
  Google Chrome Framework.framework/Versions/.../Helpers/
  Google Chrome Helper.app/Contents/MacOS/Google Chrome Helper

프로세스의 실행 파일 경로가 트리에서 더 위에 있는 다른 .app 번들을 포함한다면, 외부 번들이 거의 확실히 부모입니다. 이는 PID 변경에 걸쳐 살아남기 때문에 가장 신뢰할 수 있는 접기 신호입니다 — 새 Chrome 탭을 열면 새 렌더러의 경로는 여전히 Google Chrome.app 아래입니다.

부모 PID별

/Applications에 살고 있거나 번들 식별자가 "진짜" 애플리케이션과 일치하는 부모를 칠 때까지 프로세스 트리를 걸어 올라갈 수 있습니다. 이것은 작동하지만 더 약합니다 — launchd가 고아 프로세스를 다시 부모화하고, 일부 도우미는 부모에 의해 직접이 아니라 XPC 서비스에 의해 시작됩니다.

번들 식별자 접두사별

Slack의 번들 ID는 com.tinyspeck.slackmacgap입니다. 도우미의 번들 ID는 com.tinyspeck.slackmacgap.helper 또는 com.tinyspeck.slackmacgap.helper.renderer입니다. 설치된 앱 표에 대한 접두사 일치는 대부분의 경우를 잡습니다. 이는 Apple이 일부 사용자 대상 보고서에 내부적으로 사용하는 기술입니다.

실제로 좋은 모니터는 세 신호 모두를 사용하고 하나가 빠지면 우아하게 폴백합니다.

ova 작동 모습 보기

한눈에 볼 수 있는 메뉴 바 대역폭 모니터 — 로컬, 서명, 약 3MB.

macOS용 다운로드

Electron 특수 사례

Electron 앱은 너무 흔하기 때문에 자체 단락을 받을 만합니다. Slack Helper (Renderer)라는 Electron 도우미는 실제로 소켓을 열지 않습니다 — 그것은 네트워크 서비스이며, Electron 버전에 따라 보통 Slack Helper(접미사 없음) 또는 Slack Helper (Plugin)이라고 불립니다. 렌더러는 Chromium의 Mojo IPC 버스를 통해 네트워크 서비스와 통신합니다.

그래서 맨 Slack Helper에서만 트래픽을 본다면, 그것은 버그가 아닙니다 — 그것이 아키텍처입니다. 렌더러가 요청을 만들고 있지만, 커널은 네트워크 서비스가 I/O를 하는 것을 봅니다. 사용자 관점에서 둘 다 "Slack" 아래로 접혀야 합니다. 디버깅 관점에서 이를 아는 것은 렌더러가 왜 켜지지 않는지 궁금할 때 오후를 절약합니다.

같은 것이 Chrome 자체에 적용됩니다. 최근 Chrome 빌드에서, 거의 모든 네트워크 트래픽은 탭별 렌더러가 아니라 Google Chrome Helper (Plugin) 또는 네트워크 서비스 도우미에서 옵니다.

단순 합계를 신뢰할 수 없는 이유

도우미를 이해하면, 몇 가지 흔한 질문이 답하기 더 쉬워집니다.

"왜 Chrome 사용량이 낮아 보이나요?" 바이트가 12개 도우미에 걸쳐 분산되어 있고 모니터가 그것들을 합산하지 않기 때문입니다. 각 개별 도우미는 적당해 보입니다.

"왜 200MB가 갑자기 인식하지 못하는 프로세스 아래에 나타났나요?" 아마 비디오 스트림용 렌더러, 동기화를 처리하는 XPC 서비스(CloudKit, iCloud, Dropbox), 또는 백그라운드 작업을 하는 시스템 데몬일 것입니다. 이름뿐만 아니라 실행 파일 경로를 보세요.

"같은 앱이 재부팅에 걸쳐 두 다른 이름 아래에 나타나는 이유는?" 일부 도우미는 프로세스 이름에 버전 번호나 UUID를 포함합니다. 대부분의 모니터는 그것을 제거하지만, 모두는 아닙니다.

실제로 접기 — 그리고 언제 되돌릴지

도우미를 접는 모니터는 다음과 같은 것을 보여줍니다.

Google Chrome    412 MB ↓   18 MB ↑
Slack             89 MB ↓    4 MB ↑
Spotify           62 MB ↓  120 KB ↑
Dropbox           34 MB ↓   12 MB ↑

…다음 대신:

Google Chrome Helper (Renderer)   38 MB ↓
Google Chrome Helper (GPU)         2 KB ↓
Google Chrome Helper (Plugin)    220 MB ↓
Google Chrome Helper             140 MB ↓
Google Chrome Helper (Renderer)   12 MB ↓
Slack Helper (Renderer)           41 MB ↓
Slack Helper                      48 MB ↓
... (30개 행 더 계속)

첫 번째가 실제로 원하는 것입니다. ova는 이 접기를 자동으로 합니다 — 모든 도우미 PID를 상위 번들 아래로 그룹화하므로 일곱 개의 도우미 행 대신 "Slack"을 읽게 됩니다.

도우미 프로세스 접기
ova는 모든 도우미 PID를 상위 앱 아래로 그룹화하므로 일곱 개의 도우미 행 대신 "Slack"을 읽게 됩니다. Chrome, Slack, Discord, Telegram, VS Code, Figma — 모두 자동으로 통합됩니다.

실제로 도우미를 보고 싶을 때

접기가 방해가 되는 한 경우가 있습니다. 잘못 동작하는 앱을 디버깅하는 경우입니다. 어느 Chromium 프로세스가 소켓을 누설하고 있는지, 또는 렌더러가 네트워크 서비스를 우회하고 있는지 알아내려는 개발자라면 원시 뷰를 원합니다.

유용한 모니터는 접힌 행을 클릭해 들어가서 기본 도우미를 볼 수 있게 합니다 — 도우미당 바이트, 현재 PID, 전체 실행 파일 경로. 그렇게 하면 기본 뷰는 깨끗하지만, 세부사항은 한 번의 탭 거리에 있습니다.

이는 또한 충돌을 진단하는 데 유용합니다. 30초마다 다시 시작되어 TLS 세션을 다시 설정하는 도우미는 의외의 백그라운드 트래픽을 쌓을 수 있고, 도우미 수준 기록을 볼 수 있을 때만 보입니다.

둘러보기: 한 Slack 미스터리 조사

Slack이 오늘 600MB 다운로드를 보여주는데 비디오를 본 적이 없다고 가정합시다. 확인할 순서:

  1. 시간대 패턴 확인. 오전 9시의 급증은 아마 아침 따라잡기 동기화입니다. 평평한 24시간 곡선이 더 흥미롭습니다.
  2. 도우미 분석 확인. 90%가 Slack Helper (Plugin)(네트워크 서비스)에 있다면, 그것은 "진짜" 앱 트래픽입니다. 이상한 방식으로 렌더러에 걸쳐 분할되어 있다면, 누수된 탭이 열려 있을 수 있습니다.
  3. 워크스페이스 수 확인. 각 Slack 워크스페이스는 지속적인 WebSocket과 자체 아바타/파일 캐시를 추가합니다. 세 개의 워크스페이스는 대략 한 개의 3배의 유휴 트래픽입니다.
  4. 화면 공유 또는 허들 기록 확인. 허들은 WebRTC를 사용하고 활성일 때 1~3MB/s를 갉아먹습니다.

이 중 어느 것에도 패킷 캡처가 필요하지 않습니다. 도우미를 접고 한 시간의 기록을 유지하는 앱별 모니터가 필요합니다.

마무리

도우미 프로세스는 우회할 특이점이 아닙니다 — 현대 macOS 앱이 안전하고 안정적으로 머무는 방법입니다. 비용은 "누가 네트워크를 사용했는가"에 대한 커널의 뷰가 암호 같은 평면 목록처럼 읽힌다는 것이며, 그것이 Mac 도우미 프로세스 대역폭 회계가 모든 범용 도구를 걸려 넘어지게 하는 이유입니다. macOS의 적절한 대역폭 모니터는 도우미 접기를 해야 하며, 그렇지 않은 어떤 것이든 평생 행을 정신적으로 합산하게 만들 것입니다.

현재 도구가 원시 도우미 행을 보여주고 천 번째로 "Google Chrome Helper (Renderer)"를 읽는 데 지쳤다면, ova를 설치하세요 — 약 3MB로 메뉴 바에 자리 잡고, 약 1Hz로 샘플링하고, 도우미를 자동으로 접습니다. 모든 데이터는 Mac에 머무릅니다. macOS 14 이상, Apple Silicon과 Intel.