1984년 128K Mac의 단일 태스크 환경부터 오늘날 멀티코어와 Grand Central Dispatch, QoS까지, macOS에서 프로세스·스레드·작업과 멀티태스킹(협력형/선점형)이 어떻게 발전해 왔는지 간략히 정리합니다.
1984년의 오리지널 128K Mac은 8 MHz로 동작하는 단일 Motorola 68000 프로세서를 탑재해 한 번에 하나의 앱만 실행할 수 있었다. 그러나 오늘날의 Mac은 여러 개의 CPU 코어를 갖추고 있어, 여러 개의 무게감 있는 앱을 동시에 무리 없이 실행하는 한편, 백그라운드에서는 타임 머신 백업과 다른 작업들도 병행한다. 이 간략한 역사는 그 사이의 여정을 개괄한다.
하나의 코어를 가진 프로세서가 멀티태스킹을 지원하지 않으면, 한 번에 하나의 명령 시퀀스만 실행한다. 실행 중인 앱이 운영체제 기능 수행을 요청하면, 앱의 실행이 중단되고 제어가 시스템으로 넘어가며, 그 작업이 끝나면 제어가 다시 앱으로 돌아온다. 초기 Mac이 바로 그렇게 동작했다. 그러다 앤디 허츠펠드가 작성한 스위처(Switcher)가 1985년 4월 애플에서 공개되었고, 이것이 여러 앱 사이를 전환할 수 있게 해 주었다. 다만 동시에 실제로 실행되는 앱은 여전히 하나에 불과했다.
이후 몇 년 동안 서드파티 유틸리티가 스위처보다 더 나아가려 했지만, 1987년에야 멀티파인더(MultiFinder)가 스위처를 대체했고, 1991년에는 시스템 7에 통합되었다. 에리히 링에발트와 필 골드먼이 개발한 멀티파인더는 클래식 Mac OS의 주력 모델이 되는 협력형 멀티태스킹을 도입했다.
단일 코어 프로세서가 있는 컴퓨터에서 멀티태스킹은 마치 프로세서가 동시에 여러 일을 하는 듯한 인상을 주기 위한 일종의 ‘속임수’다. 실제로는 둘 이상의 서로 다른 프로그램 사이를 아주 빠르게 전환할 뿐이다. 이를 구현하는 기본 모델은 두 가지다.
프로세서가 한 작업에서 다음 작업으로 전환할 때는, 현재 작업의 상태를 저장해 나중에 재개할 수 있어야 한다. 그 과정이 끝나면 다음 작업을 불러와 컨텍스트 스위치를 완성한다. 이는 처리 시간과 메모리 모두에 오버헤드를 야기하며, 더 ‘가벼운’ 작업들 사이를 전환할수록 그 오버헤드는 작다. 최적의 작업 크기와 컨텍스트 스위치가 부과하는 오버헤드를 두고 다양한 전략들이 고안되었고, 여기에 쓰이는 용어도 ‘프로세스(process)’, ‘스레드(thread)’, 심지어 ‘파이버(fiber)’ 등으로 달라져 꽤 혼란스러울 수 있다.
클래식 Mac OS에는 앱을 협력형 멀티태스킹으로 실행하는 프로세스 관리자(Process Manager)가 있다. 이는 대체로 잘 작동하지만, 버릇없는 작업이 CPU를 독점해 다른 작업이 제 몫을 받지 못하게 만들 수 있다. Mac 앱의 심장부에 있는 메인 이벤트 루프(main event loop)는 사용자 입력을 기다렸다가 앱이 사용자를 위해 일을 하도록 지시한다는 점에서 큰 도움이 된다. 하지만 어떤 앱이 메인 이벤트 루프를 폴링하지 않은 채 수 초 동안 무거운 일을 처리하느라 ‘돌격’해 버리면, 사용자는 한참 동안 앱이 먹통이 된 것처럼 느끼게 된다.
1988년 2월 애플은 A/UX라는 Macintosh용 최초의 유닉스를 공개했는데, 여기에 선점형 멀티태스킹이 포함되어 있었다. 그 기능은 1996년 시스템 7.5.3에서 ‘멀티프로세싱 서비스(Multiprocessing Services)’라는 이름으로 Mac OS에 추가되었고, 3년 뒤 Mac OS 8.6에서 더 강화되었다. 협력형 멀티태스킹은 ‘스레드 관리자(Thread Manager)’에서도 지원됐다.
2000년, 애플의 하드웨어와 소프트웨어는 급격히 바뀌었다. 파워 맥 G4 데스크톱 시스템의 PowerPC 7400(G4) 칩으로 듀얼 프로세서를 탑재한 첫 Mac이 등장했고, Mac OS X는 여러 종류의 스레드와 선점형 멀티태스킹을 도입해, 여러 프로세서 또는 CPU 코어에서의 처리를 관리할 수 있게 했다. 스레드 유형에는 저수준의 Mach 스레드, 멀티프로세싱 서비스를 대체한 고수준의 POSIX 스레드(Pthreads), 자바 스레드(Java Threads), 코코아의 NSThread, 그리고 ‘카본 스레드 관리자(Carbon Thread Manager)’를 사용하는 협력형 스케줄 스레드 등이 있다. 다음 다이어그램은 애플의 현재 용어를 요약한다.

대부분의 경우, 우리가 염두에 두는 대상은 GUI를 갖춘 ‘애플리케이션’으로, 보통 번들 구조에서 실행된다. 이들은 필요 시 권한 상승이 필요한 작업을 수행하는 특권 헬퍼 앱 같은 자체 코드를 추가로 실행할 수 있다. 최근 몇 년 동안 많은 앱에 추가 실행 파일 코드가 덧붙는 현상이 늘어났다.
앱이 실행되면, 단일 실행 파일에서 만들어진 단일 런타임 인스턴스가 생성되고, 그에 필요한 가상 메모리와 시스템 자원 접근 권한이 부여된다. 이것이 ‘프로세스’이며, 예를 들어 활동 모니터(Activity Monitor)에도 그렇게 표시된다.
각 프로세스에는 코드 실행의 단일 흐름인 메인 ‘스레드’가 있고, 백그라운드 실행 등을 위해 추가 스레드를 만들 수 있다. 스레드는 별도의 가상 메모리를 갖지 않고 프로세스의 메모리를 공유하지만, 각자 고유한 스택은 가진다. 애플 실리콘 Mac에서는 스레드가 한 번에 하나의 코어에서만 실행될 수 있기 때문에 구분이 비교적 쉽다. 다만 스레드는 코어 사이에서, 때로는 빠르게 옮겨질 수 있다.
각 스레드 내부에는 개별 ‘작업(tasks)’이 있으며, 이는 수행해야 할 일정량의 일이다. 작업은 짧은 코드 조각일 수 있고, 스레드보다 상호 의존성이 크다. 흔히 작업은 엄격한 순서의 일부로 실행되어야 하는 동기 작업과, 그렇지 않은 비동기 작업으로 나뉜다.
2005년에는 파워 맥 G5가 듀얼 코어 PowerPC G5 프로세서를 사용한 첫 Mac이 되었고, 이듬해 17인치 iMac은 애플의 첫 인텔 코어 듀오 프로세서(두 코어)를 탑재했다.
2009년 Mac OS X 10.6 스노 레퍼드는 새로운 디스패처인 ‘그랜드 센트럴 디스패치(Grand Central Dispatch, GCD)’를 도입했는데, 이름은 뉴욕의 그랜드 센트럴 터미널에서 따왔다. 이후 10년이 지나 macOS 시에라에서 강화되었고, 최근에는 간단히 ‘디스패치(Dispatch)’라고 부르기도 한다.
GCD의 핵심은 작업 큐를 관리하는 디스패처다. 실행 우선순위가 높은 작업을 활성화하고, 긴급하지 않은 작업은 조금 더 기다리게 한다. GCD에는 자체 큐가 있고, 앱이 구성한 큐도 있다. 어떤 큐는 단순히 선입선출(FIFO) 규칙으로 실행되며, 어떤 큐는 상대적 우선순위를 판단하기 위한 정교한 휴리스틱을 사용한다. GCD 내부 구조에 대해서는 조나단 레빈(Jonathan Levin)의 저서 ‘OS Internals’ 1권에 자세한 설명이 있으며, 애플의 최신 개발자 문서는 여기에서 볼 수 있다.
GCD는 동일한 코어가 여러 개인 Mac에서 대칭 멀티프로세싱(SMP)을 지원하기 위해 도입되었고, 2020년 11월 첫 애플 실리콘 Mac 출시 이후에는 성능 코어(P)와 효율 코어(E)라는 두 종류의 CPU 코어에서 실행될 스레드 큐를 관리해 왔다. 이제 코어 할당은 각 스레드에 부여된 QoS(서비스 품질)에 따라 관리된다. 코어 자원 경쟁이 없는 SMP 프로세서에서 QoS는 스레드 성능에 미치는 영향이 제한적일 수 있지만, P 코어와 E 코어 간 성능 차이는 최대 10배에 달할 수 있다.
지난 41년 동안 macOS는 서로 다른 두 종류의 CPU 코어를 포함해 최대 32개의 코어를 가진 칩에서 다수의 작업, 스레드, 프로세스를 최대한 효율적으로 실행하기 위한 폭넓은 지원을 갖추게 되었다. 단 하나의 68000 프로세서에서 출발한 것과는 천양지차다.
관련
다음 분류에 게시됨: Macs, Technology | 태그: GCD, Grand Central Dispatch, Mac history, multitasking, thread. 고정 링크를 북마크하세요.