Windows 3.x의 내부 구조를 개관하고 표준 모드와 향상 모드, 메모리 관리와 스케줄링, 파일 I/O, DPMI와 DOS 익스텐더, VxD, Win32s 등을 통해 왜 단순한 DOS 위의 UI 그 이상인지 설명한다.
Windows 3는 흔히 DOS 위에 얹은 단순한 UI라고 말된다. 이 글은 Windows 3.x의 내부를 들여다보며, 특히 386 컴퓨터에서 실행될 때 훨씬 더 야심찬 체계였음을 보여준다. Windows 3는 386 전용 “Enhanced Mode(향상 모드)”에 32비트 기능을 점진적으로 더해가며, Windows 95로 가는 길을 닦았다.
이 용어집은 본문을 이해하는 데 도움이 되는 기술 용어를 설명한다. 대부분은 x86 CPU, 그 메모리 모델, 그리고 그것을 우회하는 방법과 관련되어 있다.
세그먼트 메모리는 16비트 CPU로 64KiB를 넘는 RAM을 주소 지정하는 방법이다.
실모드에서는 16비트 세그먼트 주소를 왼쪽으로 4비트 시프트한 뒤 16비트 오프셋을 더해 물리 주소를 계산한다. 따라서 CPU는 최대 20비트의 주소를 생성할 수 있어 최대 1MiB에 접근 가능하다.
16비트 보호 모드(286이 도입한 보호 모드)에서는 16비트 세그먼트 레지스터가 테이블에 저장된 24비트 세그먼트 주소를 가리키는 인덱스를 담는다. 여기에 16비트 오프셋을 더해 최대 16MiB의 메모리에 접근할 수 있다.
32비트 보호 모드(386에서 도입)에서는 세그먼트와 오프셋이 모두 32비트 폭이어서, 68000처럼 플랫 메모리 모델을 구현할 수 있다.
실모드는 8086의 원래 동작 모드다. 1MiB의 세그먼트 메모리에 직접 접근할 수 있고, 주소 지정 가능한 모든 메모리와 I/O에 접근 제한이 없다. 모든 x86은 리셋 시 실모드로 시작한다. DOS와 BIOS는 실모드에서 동작한다.
보호 모드는 80286에서 도입되고 80386에서 확장되었다. 286에서는 16비트 세그먼트, 386에서는 32비트 세그먼트로 여전히 세그먼트 기반이며, 특히 386에서는 플랫 주소 공간처럼 보이게 할 수 있다. 보호 모드 프로그램은 실모드에서 동작하는 DOS와 BIOS와는 [직접] 상호작용할 수 없다.
가상 8086 모드(가끔 V86이라 부름)는 80386에서 도입된 하드웨어 가상화 기법으로, CPU가 다른 보호 모드 “작업(task)”를 실행하는 동안 실모드에서 프로그램을 구동하는 여러 8086 칩을 에뮬레이션할 수 있게 한다. 이는 “링 0”(특권 모드)에서 실행되는 “모니터”가 필요하며, 8086 프로그램은 “링 3”(사용자 모드)에서 실행된다.
DPMI는 DOS Protected Mode Interface의 약자다. 이는 Microsoft와 여러 벤더가 제정한 규격으로, 이 규격에 따라 DOS는 서비스 제공자 역할의 DPMI 호스트(또는 서버)를 지원하고, 이 서비스는 DPMI 클라이언트(“확장” 프로그램)에 제공된다. 확장 프로그램은 보호 모드에서 실행되며, DPMI를 통해 실모드 소프트웨어(DOS, BIOS)와 인터페이스할 수 있다. DOS 익스텐더는 애플리케이션을 보호 모드로 올려 실행시키고, 실모드 애플리케이션을 위해 DOS와 BIOS가 제공하던 API의 확장판을 서비스하는 확장 프로그램이다. 286용 16비트 DOS 익스텐더도 있고, 386의 기능을 활용하는 32비트 익스텐더도 있다.
DOS/4GW는 늦은 DOS 시대의 게이머들 사이에서 잘 알려진 DOS 익스텐더였다
eXtended Memory Specification은 실모드에서 첫 1MiB를 넘어서는 메모리에 접근하는 방법을 규정한다. 286 이상의 CPU에서만 가능하다. 이 메모리는 DOS 실모드 드라이버인 _HIMEM.SYS_가 제공한다.
Expanded Memory Specification은 실모드에서 뱅크 스위칭을 통해 첫 1MiB를 넘어서는 메모리에 접근하는 방식이다. 8086과 286에서는 전용 하드웨어와 드라이버가 필요하다. 386에서는 _EMM386.EXE_가 EMS를 에뮬레이션하므로 별도 하드웨어가 필요 없다.
이 중 일부 용어는 MS-DOS 메모리 관리에 관한 내 이전 글에서 더 자세히 설명했다.
Windows 3.x에는 세 가지 동작 모드가 있다.
서로 다른 링에서 실행되는 VMM과 V86 가상 머신의 개념도
이 글의 대부분은 표준 모드와 향상 모드를 다룬다.
Windows는 계층 구조로 설계되었다. 위에서 아래로 구성은 다음과 같다.
애플리케이션이 상호작용하는 API(이후 Win16이라 불림). 개발자는 내부 구현, 설치된 드라이버, 심지어 하드웨어 구성까지 몰라도 Windows를 대상으로 개발할 수 있다.
코어 구성 요소와 동적으로 로드되는 확장으로 이루어진 중간 계층.
코어는 세 하위 구성 요소로 이루어진다.
확장은 DLL들로 구성되며 멀티미디어 지원 등 3rd 파티가 추가하고자 하는 기능을 더할 수 있다. 이로써 Windows는 매우 유연해진다.
맨 아래에서는 드라이버가 하드웨어 지원을 제공한다. DOS는 DOS 익스텐더와 DPMI 서버를 통해 파일과 메모리 같은 저수준 작업에 사용된다.
16비트 Windows 운영 환경
Windows를 “DOS 익스팬더 + 운영 환경”이라 부르는 사람도 있다. 그리 나쁜 표현은 아니다.
Windows는 DOS에서 _WIN.COM_이라는 실모드 프로그램을 통해 호출된다. WIN.COM은 CPU 종류를 검사하고 Windows의 세 가지 형태, 실모드, 표준 모드, 향상 모드 중 무엇을 실행할지 결정한다.
실모드는 모든 x86 CPU와 호환되지만 Windows 자체가 실모드로 동작하므로 DOS와 같은 메모리 제약을 받는다. 표준 모드는 286이 필요하고 보호 모드에서 실행되어 286이 주소 지정할 수 있는 모든 메모리를 사용할 수 있다. 향상 모드 또한 보호 모드에서 실행되며, 필수인 386과 그 MMU가 가능한 많은 이점을 제공한다.
향상 모드의 경우 WIN.COM은 “386 메모리 매니저”가 이미 실행 중인지 확인하고, Windows가 메모리 관리를 인수하기 전에 그 페이징 테이블을 사용한다.
실모드가 선택되지 않았다면, WIN.COM은 보호 모드의 Windows가 DOS와 BIOS와 대화할 수 있게 하는 _DPMI 호스트_와 _DOS 익스텐더_를 기동한 뒤, 태스크 스위처와 “커널” 자체를 실행한다.
실행 파일은 모드와 CPU에 따라 다르다.
모드는 CPU(향상 모드는 386 필요)와 메모리 용량을 기준으로 선택된다. 386이라도 2MiB 미만이면 Windows는 표준 모드로 시작한다.
표준 모드에서는 DOSX.EXE가 실모드에서 실행을 시작해 초기화를 수행한 뒤 KRNLx86.EXE를 적재한다.
향상 모드에서는 설정이 더 복잡하지만 훨씬 발전된 시스템으로 이어진다. WIN386.EXE는 DPMI 호스트/도스 익스텐더일 뿐 아니라 선점형 태스크 스위처를 내장하고, 향상 모드 전용 “가상 장치 드라이버”(VxD)를 관리하며, “가상 머신 관리자”(VMM)를 호스팅한다. WIN386.EXE는 모든 VxD를 로드한 후 VMM을 실행한다. VxD와 VMM은 32비트 보호 모드로 전환해 자신을 초기화하고, 이어 VMM은 실모드에서 동작하는 첫 번째 가상 8086 세션을 만든다. 그 세션이 시스템 가상 머신이며, 커널은 바로 그 안에서 기동된다!
KRNLx86은 일반 DOS 실행 파일로 로드된다. 메모리로 스스로를 부트스트랩하고, 보호 모드로 전환한 다음, 일부 DOS 인터럽트 벡터를 자체 것으로 교체하고, DPMI 서버의 도움으로 “전역 힙”을 할당한다. 그런 다음 KRNLx86은 Windows DLL로 자신을 다시 로드하여, 내보내기 함수 테이블을 채워 사용자 코드에서 사용할 수 있게 한다. 이후 로드되는 모든 DLL도 마찬가지다.
이 시점에서 Windows KERNEL은 보호 모드에서 실행되며, 메모리 관리를 위한 모든 저수준 도구들이 준비된다. DPMI 서비스와 이를 사용하는 DOS 익스텐더가 실모드와 보호 모드 간 전환을 처리하고, 실모드에서만 실행되어야 하는 DOS와 BIOS API 호출을 대신 처리한다.
Windows는 _SYSTEM.INI_에서 설정을 읽고, 해당 DLL과 DRV를 로드하여 SYSTEM, USER, GDI 모듈과 모든 16비트 장치 드라이버(DRV 파일), 시스템 확장, 폰트를 메모리에 올리며 기동을 마무리한다.
USER 초기화가 창 관리자(Window Manager)를 가동시킨다.
마지막으로 커널 로딩 과정은 스케줄러 루프로 점프하고, Windows가 완전히 올라온다!
프로그램 관리자가 맞아준다
Windows가 완전히 로드된 후에도 DOS는 여전히 메모리에 남아 있다. 이는 주로 DPMI 서버를 통해 저수준 메모리 관리와 파일 작업에 사용된다. 그러나 상위 관점에서 프로그래머는 Windows API(Win16)와 상호작용하며, DOS 자체와 직접 맞닿을 일은 없다. 그런 관점에서 Windows는 완전한 운영체제처럼 동작한다.
KERNEL은 고수준의 메모리 작업 인터페이스를 제공하고, 페이지 폴트 처리 같은 저수준 작업은 DPMI 서버가 담당한다.
실모드는 매우 제한적이며, 메모리 접근 제약이 DOS와 동일하다. EMS 메모리를 활용해 최대 4MiB까지 접근할 수 있다.
표준/향상 모드에서는 모든 할당이 HIMEM.SYS 드라이버를 통한 XMS 메모리에서 이뤄진다. Windows에 하나가 포함되어 있지만 DOS 5.0+의 HIMEM.SYS도 사용할 수 있다. Windows 3.0은 최대 16MiB로 제한되지만, Windows 3.1은 386 이상에서 최대 256MiB까지 관리할 수 있다.
표준 모드와 향상 모드의 차이 대부분은 각자의 DPMI 서버(DOSX.EXE와 WIN386.EXE)가 제공하는 기능과 서비스에 관련된다. WIN386은 386의 가상 메모리와 페이징 기능 위에 구축되어 단순한 DOS 익스텐더 그 이상이다. 실제 장착된 용량보다 큰 메모리 공간을 제공할 수 있고, 각 VM에 사설 주소 공간을 제공한다. 즉, 향상 모드에서는 VMM과 여러 VxD가 실행되는 링 0을 제외하고, KERNEL/USER/GDI, Windows/DOS 애플리케이션, 16비트 드라이버가 자신의 사설 메모리 공간 밖이나 링 0에 쓸 수 없다.
KERNEL의 역할은 Win16 애플리케이션에 연속된 메모리 블록을 제공하는 것이다. 이 관점에서는 하위 계층의 차이가 모두 투명하다.
실모드에서는 256KiB “대형 프레임”을 지원하고, KERNEL은 EMS 접근의 특이점을 감춘다. 번거로운 EMS 풀을 Win16 API를 통해 쉽게 접근하게 해 준다. 하지만 일이 쉽진 않다. 첫 1MiB의 물리 주소는 세그먼트 주소와 오프셋의 직접 변환으로, 간접 참조가 없다. 따라서 KERNEL은 메모리 단편화를 피하거나, 비어 있는 구멍에 맞지 않는 새 블록을 위해 공간을 만들려고 메모리를 재배치할 때 코드 전반의 많은 세그먼트 주소를 갱신해야 한다. EMS 풀은 확장 메모리에 대한 간접 접근이므로 더 관리하기 쉽다.
메모리 단편화의 예시
표준/향상 모드에서는 물리 주소가 셀렉터 테이블을 통해 계산되므로 관리가 더 쉽다. 물리 주소 갱신은 이 테이블만 바꾸면 된다. 그러나 보호 모드와 386에서 실행하더라도, Windows는 16비트 x86 세그먼트 메모리 모델과 그 64KiB 세그먼트의 복잡성을 일부 감당해야 한다.
사용자 프로그램에서 세부 사항을 가급적 감추기 위해, KERNEL은 모든 16비트 DLL과 프로그램의 코드/데이터 할당을 추적하는 큰 전역 힙을 유지한다. 여기에서 각 프로그램 전용의 로컬 힙과 스택을 분할해 내준다. 이들은 접근이 빠르지만 총 64KiB로 제한된다.
16비트이고 구형 x86에서도 실행되어야 하므로, 프로그램은 최저 수준에서 64KiB 구조체 제한을 받는다. 그러나 KERNEL은 더 높은 수준에서 세그먼트를 “타일링(tiling)”하여 더 큰 구조를 다룰 수 있게 해 준다. “Huge”(즉 64KiB 초과) 데이터 구조는 전역 힙에서 요청할 수 있다. C/C++ 컴파일러는 당연히 KERNEL을 활용하며, 거대 구조체를 다루려면 비표준 키워드를 사용하는 정도로 충분하다.
컴파일러들이 공통 메모리 모델을 공유하고, 시스템 호출로 메모리를 관리하며, Windows의 ABI를 따르므로, 어떤 프로그램이 다른 컴파일러로 빌드된 DLL의 함수를 호출해도 된다.
마지막으로 KERNEL은 메모리 단편화를 자동으로 완화하려 노력하며, 프로그램이 사용 중인 블록이 잠겨 있지 않다면 이들을 이동시킬 수 있다. Win16 API에서 메모리를 할당하면 “핸들”이 반환된다. 해당 포인터를 조작하려면 프로그램이 이를 락해, 이리저리 옮겨지지 않도록 해야 한다.
그러나 향상 모드에서도, 시스템 VM 안에서 실행되는 Windows와 프로그램에는 메모리 보호가 없다. UNIX, Windows NT, OS/2와 달리, Windows 자체를 이루는 16비트 DLL을 포함한 모든 프로그램과 DLL이 동일한 주소 공간을 공유한다. 프로그램은 물리 메모리의 어느 부분이든 실수로 덮어쓸 수 있어, 불안정과 크래시로 이어질 수 있다. DOS 세션과 관련해서는, 향상 모드에서 각 세션(및 그 안의 프로그램)은 가상 8086 모드에서 실행되며 독립 주소 공간을 가진다. VxD와 VMM은 특권 링에서 실행되어 시스템 VM이나 DOS 세션의 잘못된 포인터로부터 수정될 수 없다. 하지만 VxD에 버그가 있으면 VMM과 같은 특권 모드에서 실행되므로 시스템 전체를 다운시킬 수도 있다.
Windows 운영 환경은 협조형(비선점형) 스케줄러를 갖고 있다. 프로그램은 자발적으로 “양보(yield)”하여 스케줄러가 제어권을 회수하고 다른 프로세스를 깨울 수 있게 해야 한다. 양보는 “Yield()” 함수를 호출해 일어나기도 하지만, 이벤트/메시지 처리 같은 일부 API 호출 중에도 일어난다. 잘 작성된 프로그램은 주기적으로 양보하는 메커니즘을 갖는다. 그렇지 않다면 시스템조차 CPU 사이클을 얻지 못해, 인터페이스가 굼떠지거나 긴 루프에 갇히면 완전히 멈출 수도 있다!
이런 스케줄러는 80년대 말의 UNIX나 OS/2에 이미 있던 선점형 스케줄러에 비하면 훨씬 원시적이다.
그럼에도 작업 우선순위 같은 편의는 지원한다. 실제로 작업에는 우선순위 수준이 있다. 제어권을 얻으면 스케줄러는 큐에 이벤트가 대기 중인 작업들 가운데 가장 높은 우선순위를 가진 작업을 깨운다.
작업은 우선순위로 정렬된 연결 리스트로 관리된다
대기 중인 이벤트가 하나도 없으면 스케줄러는 _유휴 루프_로 들어간다. 이 루프는 메모리 관리 작업을 수행한 뒤 DOS 유휴 인터럽트를 호출해 DOS TSR(상주 프로그램)을 실행한다. 필요하면 스크린세이버를 기동하고, 노트북의 전원 관리 기능도 활성화한다.
대부분의 프로그램이 같은 우선순위로 실행되므로, 스케줄러는 공평하게 타임슬라이스를 분배하려 우선순위를 조정할 수 있다.
향상 모드와 관련해서 “오직 협조형 멀티태스킹만 있다”고 말하는 것은 완전히 정확하진 않다. 각 DOS 세션이 가상 8086 모드의 가상 머신에서 실행되기 때문이다. 이들 각 가상 머신은 **가상 머신 관리자(VMM)**가 선점형으로 스케줄한다. 더구나 협조형 스케줄러 자체도 시스템 VM 안에서 실행되며, 이 또한 VMM이 스케줄한다. 즉 스케줄링에는 두 레벨이 있다. DOS 애플리케이션들은 시스템 VM과 함께 선점형으로 스케줄되고, 시스템 VM 내부에서는 Windows 애플리케이션들이 협조형으로 스케줄된다!
향상 모드에서의 스케줄링
실모드와 표준 모드에서는 모든 파일 작업이 실모드 API인 INT 0x21과 INT 0x13을 통해 DOS와 BIOS에 의해 수행된다. DPMI를 통해 보호 모드와 실모드 간 전환은 느리다. 게다가 이 함수들은 재진입 불가이고 대개 블로킹이다. 따라서 호출을 직렬화해야 하며, 성능을 더 저하시킨다.
Windows 3.1의 향상 모드에서는 32비트 디스크 접근을 제공한다. 이는 보호 모드 장치 드라이버를 기반으로 BIOS의 인터럽트 13h 호출을 처리한다. 디스크 섹터 읽기/쓰기를 담당하는 실모드 BIOS 함수가 32비트 동등 기능으로 대체된다. 이로써 BIOS 호출의 오버헤드를 피할 뿐 아니라, 후대의 486 머신에서는 하드 디스크 컨트롤러의 32비트 인터페이스를 활용할 수 있다. 기본으로 WDCTRL이라는 32비트 VxD 드라이버가 제공된다. 다만 이는 일부 기하 제약을 가진 IDE 하드 드라이브만 처리한다. 예컨대 504MiB의 하드 한계가 있다. 이 한계를 넘거나 SCSI 등 다른 컨트롤러를 관리하기 위해서는 서드파티 32비트 드라이버가 제공되었다.
Windows 3.11은 32비트 파일 접근을 추가한다. 이는 DOS를 완전히 우회하는 고수준 파일 접근 및 캐싱 방식으로, 인터럽트 21h를 가로챈다. 디스크 컨트롤러에 대한 32비트 디스크 접근 기능과 직접 상호작용할 수 있으며, BIOS가 제공하는 실모드 기능과도 상호작용할 수 있다.
Win32s는 Windows 3.1 및 3.11용으로 1992년에 도입된 선택적 32비트 런타임 환경으로, 별도로 설치해야 한다. 이는 Windows NT와 Windows 95 대상 프로그램이 사용하는 Win32 API의 부분 구현이다. Win32s 애플리케이션은 Win32 API의 하위집합과 상호작용하며, 스레딩이나 네트워크 서비스 같은 Win95/NT 전용 기능은 빠져 있다. Win32s 호출은 썽킹(thunking)을 통해 Win16 호출로 변환된다. 즉, Win32s 함수가 “퍼사드”처럼 동작하며, 호출을 변환해 하위의 16비트 API로 전달한다. 사실 Win32s 애플리케이션은 32비트와 16비트 메모리 모델이 매우 다르기 때문에 Win16 API를 직접 호출할 수 없다.
16비트와 32비트 메모리 모델이 다르기에 썽킹이 필요하다
장점은 Win32s 애플리케이션이 완전한 32비트라는 것이다. 32비트 데이터에 32비트 연산을 수행하고, 세그먼트 없는 플랫 주소 공간으로 할당된 메모리를 다룰 수 있다!
하지만 시스템과의 상호작용은 여전히 16비트다. Win32 프로그램과의 호환성은 Win32s가 불완전하기 때문에 당연히 부족하다. 또한 DOS 프롬프트에서 Win32s 앱을 실행할 수 없다는 제약도 있다. DOS는 자체 VM에서 실행되며 Win32s 애플리케이션이 무엇인지 알 수 없기 때문이다.
Win32s 프로그램을 로드할 때 표준 16비트 WinExec() 함수가 호출된다. 이것이 _LoadModule()_을 호출하지만, 프로그램이 표준 Win16 NE(New Executable) 파일이 아니므로 _LoadModule_은 오류 코드를 반환하며, 그 결과 KERNEL 내의 최신 Win32의 PE(Portable Executable) 형식을 로드하도록 맞춰진 함수 _ExecPE()_가 실행된다. 이는 Windows 3.1이 처음부터 PE 파일을 지원했음을 의미하며, Win32s가 사후에 덧붙인 기능이 아니었음을 보여준다!
Windows가 로드될 때 DOS 드라이버는 메모리에 남아 있다. 일부 Windows 16비트 드라이버는 이를 밑단에서 사용한다. 또한 일부 DOS 애플리케이션은 이들과 직접 상호작용하도록 작성되었다. 따라서 DOS 드라이버는 이용 가능해야 한다.
많은 16비트 드라이버가 DOS나 BIOS 호출을 통해 장치와 상호작용한다. 예를 들어 BIOS의 표준 방법을 호출해 장치 구성 옵션을 얻을 수 있다. 이런 호출은 DPMI 서버의 서비스가 필요하다. DOS에서 로드된 TSR과의 상호작용도 마찬가지다. 안타깝게도 보호 모드와 실모드 간 왕복 전환은 비용이 크다. 그리고 BIOS 호출은 본질적으로 블로킹이어서 완료될 때까지 할 수 있는 일이 거의 없다.
더 나은 성능을 위해, 16비트 드라이버는 I/O 및 메모리 맵드 장치에 직접 접근하고, DMA 전송을 수행하며, 인터럽트를 처리할 수도 있다. 물론 향상 모드에서는 사용자 모드에서 금지된 명령이 트랩되어 VxD가 이를 처리하거나 전달한다.
대부분의 메모리 맵드 장치는 주소 공간 0xA0000~0xF0000 사이에 위치하며, DOS와 마찬가지로 64KiB 미만을 점유한다. 이런 장치는 실모드에서처럼 직접 접근할 수 있다. 하지만 일부는 64KiB에 맞지 않거나 1MiB보다 높은 곳에 있다. 그 경우 DPMI 서비스 호출이 필요하다.
사실 16비트 드라이버는 시스템의 특수 진입점으로 인터페이스하는 표준 애플리케이션과 크게 다르지 않다. 이들은 KERNEL이 제공하는 메모리 관리를 사용하며, 코드/데이터 메모리를 고정(이동 불가), 페이지-락, 비폐기(non-discardable)로 요청할 수 있다.
VxD(가상 장치 드라이버)는 Windows/386 2.x에서 처음 도입되었지만, 향상 모드에서만 지원된다.
이는 여러 가상 머신(시스템 VM과 여러 DOS 상자)의 도입으로 인해 필요한 아키텍처 변경이었다. 후자의 환경에서 각 애플리케이션은 하드웨어에 대한 배타적 접근 권한을 가진다고 생각한다. 자신의 VM 안에서 DOS 애플리케이션이나 Windows 운영 환경은 x86의 제한된 “사용자 모드”인 “링 3”에서 실행된다. 이 링에서는 IN/OUT 명령이 금지된다. 응용이 접근하려는 장치를 가상화하는 VxD 드라이버에 제어권을 넘기는 예외가 발생한다. VxD 드라이버는 재진입 가능하며 애플리케이션마다 상태를 관리해, 각 애플리케이션이 가상 장치에 배타적 제어권을 가진다는 착각을 유지한다. 하드웨어 인터럽트 또한 VxD가 처리한 다음, 이를 필요로 하는 각 VM에 반영한다.
가상 장치 드라이버는 일반적으로 해당 16비트 드라이버와 함께 동작하며, 실제 작업과 장치에 특화된 무거운 일은 여전히 16비트 드라이버가 수행한다. Windows 환경은 장치를 독점하지 않는다는 사실을 알고 있으며, 드라이버가 반드시 가상화될 필요는 없기 때문에, Win16 코드는 종종 VxD와 상호작용하지 않고 16비트 드라이버만으로 동작한다.
Win16은 물론, DOS 애플리케이션도 VxD가 “API 프로시저”를 내보낸다면 이를 “의식적으로” 호출할 수 있다. 링 3에서 이를 요청하려면 INT 0x2F를 발생시키고, 레지스터 AX와 BX로 원하는 매개변수를 전달한다. VMM이 인터럽트를 트랩해 VxD의 “API 프로시저”를 호출할 함수 포인터를 얻어, Win16/Win32s/DOS 프로그램으로 회답에 담아 전달한다.
VxD가 반드시 가상 드라이버일 필요도, 심지어 드라이버일 필요도 없다. 어떤 VxD는 장치 드라이버처럼 동작하지만 장치를 가상화하지 않으며, 어떤 VxD는 그 어떤 장치와도 상호작용하지 않는다. 그저 다른 VxD나 애플리케이션에 서비스를 제공하고, Windows의 기능을 확장하는 수단으로 존재할 수 있다.
VxD는 특권 모드에서 실행되므로 모든 x86 명령에 제한 없이 접근하고, 메모리의 어느 곳이든 접근할 수 있다. IN/OUT 명령을 문제없이 실행할 수 있으며, 메모리 맵드 장치와 직접 대화할 수 있다. 또한 VMM과 긴밀히 협력해, 디스크립터/페이지 테이블 같은 운영체제 자료구조를 자유롭게 살펴볼 수 있다. 하지만 오동작하는 VxD는 시스템 전체를 크래시시킬 수도 있다!
모든 VxD는 시작 시 로드되어야 한다. _SYSTEM.INI_의 device= 항목으로 선언되어야 한다. 일부는 WIN386.EXE 내부에 포함되어 제공되고, 나머지는 .386 파일로 제공된다.
VxD 환경은 Windows 95의 드라이버 모델로 확장되었다.
향상 모드에서는 대부분의 장치를 지원하려면 VxD와 16비트 드라이버가 모두 필요하다. 하지만 예외도 있다.
16비트 DLL/DRV****VxD DisplayDISPLAY.DRV VDD.VXD PrinterPRINTER.DRV KeyboardKEYBOARD.DRV VKD.VXD MouseMOUSE.DRV VMD.VXD Serial / Parallel PortCOMM.DRV VCD.DRV MultimediaInstallable DLL NetworkDOS device driver or TSR **Block Device (HDD, CDROM)**DOS device driver WDCTRL (if possible) or 3rd party
이 글은 모두를 다루진 못했다. GDI(Windows 3에서 처음 도입된 것이 아님)나 DirectX의 전신 격인 WinG도 언급하지 않았다. 그럼에도 Windows 3.x가 단순히 MS-DOS 위에 얹은 “GUI”가 아님을 말하기엔 충분하다. 이는 90년대 초의 기준에 걸맞은 완전한 운영 환경을 제공하며, Macintosh의 System 6/7이 제공하던 것과 거의 대등한 기술 수준을 갖췄다. 특히 빛나는 건 향상 모드다. Win32s와 다양한 VxD로 32비트의 맛을 제공하고, VMM이라는 일종의 “하이퍼바이저”를 통해 다수의 VM을 선점형으로 스케줄링한다. 향상 모드에서 VMM은 Windows의 진정한 커널로 볼 수 있다.
물론 DOS는 여전히 필요하다. 하지만 그 존재감은 많은 이들이 생각하는 것보다 훨씬 가볍고, 사실상 단지 부트로더로 남기까지 한 발짝 남았을 뿐이다. 그 한 발을 Windows 95가 내딛었고, 이는 다른 날의 이야기다.
80386+ 프로세서 모드에 대응하는 물리적 실행 환경
32-bit Protected16-bit ProtectedV86 Total Address Space 4GiB 16MiB 1MiB Segment Size 4GiB 64KiB 64KiB Address Translation 논리→선형: 셀렉터 조회 선형→물리: 페이지 테이블 논리→선형: 셀렉터 조회 선형→물리: 페이지 테이블 논리→선형: 세그먼트<<4 + 오프셋 선형→물리: 페이지 테이블 Privilege Level 0–3 0–3 0
프로세스 유형별 Windows 실행 환경
Process type Processor mode Privilege Bitness Memory model VM VMM Protected Ring 0 32-bit “flat” Outside all Win32s Protected Ring 3 32-bit “flat” System VM Win16 Protected Ring 3 16-bit Segmented System VM DOS V86 Ring 3 16-bit Segmented DOS VM