세상에는 "난수를 얻기 위해서는 난수를 생성하는 첫 씨앗도 난수여야 한다.",
"엔진이 정지 상태였다가 돌아가기 위해서는 맨 처음에 외부로부터 힘을 공급받아야 한다"(시동) 같은 재귀적인 원리 법칙이 있다.
컴퓨터가 먼저 등장했을까, 프로그래밍 언어가 먼저 등장했을까? 역사적으로 답은 후자이다.
지금은 완전히 망해 버렸지만 2000년대에 인텔 Itanium이라는 64비트 CPU가 처음 출시됐을 때 Itanium용 Windows 2000도 같이 개발되고 있었다. 아직 미완성인 CPU를 타겟으로 그걸 지원하는 운영체제가 어떻게 나란히 개발될 수 있었을까? (에뮬레이터, 그리고 비슷한 특성을 지닌 다른 기성 64비트 OS의 도움을 받아서)
이런 식으로 컴퓨터의 세계에서는 뭔가 재귀 딜레마를 논리적으로 해결하려 한 사례를 분야별로 찾아볼 수 있다.
1. 파일 압축 프로그램을 설치하기 위해서 압축 프로그램 패키지의 압축을 풀어야 한다??
그러니 PC 통신 시절부터 압축 프로그램은 무조건 당장 실행 가능한 EXE 형태로 배포되는 게 불문율이었다. 당연히 이 딜레마를 파훼하기 위해서다.
뭐, 옛날엔 용량을 줄이는 것도 중요했으니 그 EXE는 실행 파일 압축 유틸로 압축돼 있기도 했을 것이다.
2. 부팅을 위해서는 컴에다 운영체제를 설치해야 하는데, 운영체제 설치 프로그램을 구동하기 위해서는 부팅이 돼 있어야 한다.
그러니 운영체제들은 원본 CD나 USB 메모리로부터 자체 부팅하는 기능이 있다. 그리고 설치 프로그램부터가 자기 운영체제 GUI 셸의 자그마한 클론을 자체 보유하고 있다.
설치 대상 컴터에서 기본적인 하드웨어들을 세팅하고 최소한의 파일들을 하드에 복사했으면.. 다음엔 그 GUI 클론 환경으로 곧장 진입한다.
옛날에 Windows 9x 시절에는 설치 프로그램이 Windows 3.1 셸 기반의 GUI 클론을 갖고 있었다. 그러다가 운영체제의 설치가 거의 다 끝나면.. 설치 프로그램의 최종 마무리 파트(프린터 세팅, 응용 프로그램 등록..)는 "새 운영체제"에서 돌아가는 일회성 프로그램의 형태가 될 정도로 비중이 줄어들었다. 그러면서 끝.. 신기하지 않은가?
3. 컴파일러를 돌리기 위해서 컴파일러의 소스를 컴파일해야 한다.
그래서 C/C++처럼 컴파일러 자기 자신을 직접 빌드할 수 있을 정도로 스케일이 큰 언어는 버전 n의 소스를 개조해서 차기 버전 n+1을 만들었다.
그 뒤 버전 n 컴파일러로 버전 n+1을 빌드하고, 빌드된 버전 n+1 컴파일러로 n+1 소스를 '또 다시' 컴파일해서 최종적으로 n+1 컴파일러 바이너리를 만들었다. 최신 컴파일러도 새 기능만 돌아가는 게 아니라, 최신 버전에서 구현된 최적화 기능 같은 게 적용돼야 하니까 말이다.
물론 컴퓨터의 초창기 시절.. 진짜 최초의 원조 컴파일러는 얄짤없이 쑤제 기계어/어셈블리어로 만들어져야 했다. 이건 마치 지구상 완전 최초의 생명체만큼이나(진화론 관점에서..) 지금으로서는 흔적을 찾을 수 없는 전설적인 존재이다.
4. 컴터는 운영체제가 파일 시스템을 세팅하기 전에, 운영체제 프로그램 파일을 디스크로부터 읽어야 한다.
그렇기 때문에 운영체제 프로그램은 파일 시스템과 무관하게 디스크에서 물리적으로 고정된 첫 지점으로부터 읽어들이는 걸로 규약이 정해졌다. 컴퓨터의 펌웨어 차원에서 말이다. (BIOS건 UEFI건)
부팅 디스크는 io.sys 같은 파일을 아무렇게나 카피만 해 넣는다고 만들어지는 게 아닌 이유가 이 때문이다. 특수한 전용 유틸을 써야 만들 수 있다.
5. 가상 메모리는 컴퓨터의 메모리를 관리하는 파트인데.. 가상 메모리 관리자의 동작을 위한 메모리를 관리하는 파트도 필요하다.
이건 메모리를 관리하는 데 드는 메모리, 로켓이 연료 무게 때문에 더 필요해지는 연료, 세금을 걷는 데 드는 세금.. 이런 개념이다.
보통은 메모리 관리자를 두 번 세팅하는 걸로 해결했다.
오늘날 64비트 컴퓨터들이 포인터에서 44~48비트 정도밖에 사용하지 않는 것도 이 때문이다. 현실적으로 컴의 물리적인 메모리가 많아 봤자 수백 GB나 수 TB 정도밖에 없는데, 비현실적으로 공간을 너무 방대하게 잡으면.. 아무 도움이 안 되고 불필요한 메모리 사용만 늘기 때문이다.
6. 응용 프로그램들이 사용하는 C/C++ 런타임 라이브러리는 역시 응용 프로그램과 같은 계층에 존재한다. 그러나 사실은 그 전, 부팅을 위해 실행되는 운영체제의 코드들도 C/C++ 함수를 왕왕 사용한다.
그래서 Windows의 경우, msvcrt뿐만 아니라 ntdll에도 보면 C 함수들 구현체가 왕왕 들어있다. 커널용 CRT와 응용 프로그램용 CRT가 별도로 제공되곤 했다.
단, NT 말고 9x 계열은 CRT DLL에 대한 배려가 딱히 없었다. 16비트 시절에는 DOS나 Windows를 불문하고 CRT를 그냥 static link 해서 각자 탑재하는 게 관행이었기 때문이다.
프로그램들마다 메모리 모델이 다를 수 있어서, DLL이 프로세스별로 독립된 메모리 주소 공간을 갖지 못해서, C 라이브러리가 지금에 비해 그닥 거대하지도 않아서.. 같은 이유들 때문이다.
Posted by 사무엘

