도스 프로그래밍 (메모리 모델)

MS-DOS를 사용하던 시절에는 지금은 생각할 필요가 없는 메모리 모델을 생각하면서 프로그래밍을 해야 했습니다. 메모리 모델이 무엇인지 간략히 설명하겠습니다. MS-DOS는 16비트 CPU를 위한 운영체제입니다. 16비트 CPU란 레지스터의 크기가 16 비트라는 뜻입니다. 어드레스를 제어할 수 있는 비트도 16비트였고요. 16비트란 2^16승이므로 65535입니다. 이 뜻은 주소가 0 ~ 65534까지 가능하다는 뜻이 됩니다. 아무리 MS-DOS를 사용하던 시절이 텍스트 베이스의 UI이고, 프로그램 성능이 떨어진다고 해도 65535 바이트에 MS-DOS 운영체제를 운영하고, 응용 프로그램을 실행하기에는 턱없이 부족한 용량입니다. 이를 해결하기 위해서 세그먼테이션 방식을 사용합니다. 문제는 이 세그먼테이션을 어떻게 운영하는지에 따라 프로그램의 성능에 많은 영향을 주게 됩니다. 이유는 아래에 설명하는 메모리 모델을 보면 충분히 짐작할 수 있습니다. 

그 이후 32비트 OS가 대중화되면서 메모리의 크기가 비약적으로 커지고, 가상 메모리 상에서 동작하므로 실제 장착되어 있는 메모리보다 훨씬 큰 4 GByte의 메모리 (정확히는 OS가 사용 중인 메모리 외의 공간)를 전부 사용한다는 가정으로 프로그래밍을 하게 되었습니다. 몇 년 전부터 64비트 OS가 대중화되면서 메모리에 대한 것은 특별한 프로그램을 제외하고는 신경 쓰지 않아도 될 상황이 되었지요. 

아래의 내용은 MS-DOS에서 사용했던 메모리 모델입니다.


위에서 말했듯이 2^16승이면 10진수로 65535입니다. 즉, 메모리 주소가 0~65534라는 것입니다. 그러면 8비트 CPU는 2^8 이면 10진수로 255이므로, 메모리 주소가 0~254이냐고 물으면 “맞다.”입니다. 그런데 이렇게 되면 아무리 20년 전이라고 해도 너무 적은 메모리입니다. 마이크로컨트롤러로 생각해보면 FND 몇 개 다이내믹 디스플레이로 동작시키고 키 매트릭스 입력받으면 메모리 부족으로 아무것도 못합니다. 그래서 8비트 CPU도 어드레스 레지스터는 H, L 두 개의 8비트 레지스터를 합쳐서 16비트로 제어할 수 있도록 했고 그래서 대부분의 8비트 CPU의 어드레싱 능력은 0~65534 (64 Kbyte)가 됩니다.  

그러면 8086/8088 CPU도 H, L 두 개의 16비트 레지스터를 합쳐서 32비트로 어드레싱하도록 만들었을까요? 그런데 Intel에서는 조금 더 복잡한 방식을 사용하고 있습니다. 세그먼트 레지스터라는 것을 사용하는데 이것을 이용하여 총 20비트 2^20승, 즉 1 MByte의 메모리를 사용하도록 했습니다. 아래의 <그림 1>을 보면 이해하기 쉽습니다.

<그림1> 세그먼트 어드레싱 방식 (출처 : 위키피디아)

 

위 <그림 1>과 같이 세그먼트 레지스터를 MSB 쪽(왼쪽)으로 4비트 시프트 하고, 오프셋 레지스터와 합쳐서 전체 20비트 어드레스를 만듭니다. 이렇게 해서 만들어진 20bit address를 이용하여 외부 메모리를 제어하게 됩니다. 프로그램에서 매번 이렇게 계산을 하면 CPU 자원 소모가 심해지므로 일반적으로 두 개의 레지스터를 계속 바꿔가면서 사용하는 것이 아니라 세그먼트 레지스터는 처음에 한 번 설정하고 오프셋만 바꿔가면서 사용하게 됩니다. 이 정도 설명하면 눈치 빠른 사람은 이런 생각을 할 것입니다. “그러면 데이터나 코드가 한 번에 64 Kbyte를 넘어갈 수 없는 것이냐”라는 것일 텐데 맞습니다. Turbo C에서 Huge Model의 경우 64 Kbyte를 넘는 정적 데이터를 가질 수 있기는 하나 배열이나 구조체 하나의 크기가 64 Kbyte를 넘을 수는 없습니다.  

아래 <그림2>는 8086/8088의 내부 레지스터 구조입니다. Segment register와 Index registers의 조합으로 메모리 어드레싱을 하게 됩니다.

<그림2> 8086 레지스터 구조 (출처 : 위키피디아)

 

좀 더 상세한 내용이 궁금한 사람은 https://en.wikipedia.org/wiki/Intel_8086 링크로 가면 8086에 대한 내용을 볼 수 있습니다. 8088은 8086과 같은 16비트 CPU이나 외부 데이터 입출력은 8비트로 되어 있는 것으로 16비트 데이터를 취득하려면 8비트씩 두 번 외부 버스를 제어해야 합니다. 그 외 내부 구조는 8086과 8088이 동일합니다. 이렇게 만든 이유는 당시 16비트 입출력 구성을 하게 되면 하드웨어 비용 상승이 크기도 하고, 당시 대부분의 입출력 장치는 8비트를 유지하고 있었기 때문입니다.


 

지금부터는 Turbo C에서 사용하는 메모리 모델에 대해서 설명하겠습니다. C언어의 경우 효율이 좋은 코드를 만들기 위해서 메모리 모델이라는 것을 사용합니다. 파스칼, 베이식과 같은 고급 언어의 경우는 따로 메모리 모델을 사용하지 않습니다. 참고로 터보 파스칼의 경우 이후에 설명하는 Large Model을 사용하고 있습니다.  

Turboc C에서 사용하는 메모리 모델은 총 6가지로 , Tiny, Small, Medium, Compact, Large, Huge 모델로 각 메모리 모델은 아래와 같은 성질을 가지고 있습니다. 

1. Tiny model (초소형)  
가장 작은 메모리 모델로 4개의 세그먼트 (CS, DS, SS, ES)가 같은 어드레스를 가집니다. 그러므로 코드, 데이터, 스택 모두 64 Kbyte 메모리 안에 있습니다. 당시 바이러스 백신, 한글 바이오스 등 램 상주 프로그램은 전부 이 메모리 모델을 사용했습니다.  

램 상주 프로그램이란 지금의 윈도우즈 서비스와 같은 역할을 하는 것으로 지금과는 다르게 기본 메모리를 공유하는 구조로 동작합니다. DOS는 싱글 테스크 운영체제로 가상 메모리나 태스크 관리와 같은 기능이 없어서 인터럽트 테이블을 조작해서 실행하도록 하던가 타이머를 이용하는 방법으로 지금의 윈도우즈 서비스와 같은 기능을 하도록 프로그램을 만들었습니다.  

2. Small model (소형)  
Turbo C에서 기본값으로 사용하는 메모리 모델로 DS (데이터 세그먼트)만 별도의 어드레스를 가지고 나머지 3개는 동일한 값을 가지고 있습니다. 코드와 데이터가 분리되므로 128 Kbyte를 사용할 수 있습니다. 당연히 코드나, 데이터 어느 한쪽이 64 Kbyte를 넘어갈 수는 없습니다. Turbo C가 베이식이나 파스칼과 같은 다른 컴파일러와 다르게 이 메모리 모델을 주로 사용함으로써 기본적으로 속도가 빠르다는 얘기를 할 수 있습니다. 

3. Medium model (중형)  
CS (코드 세그먼트)는 수시로 바뀔 수 있는 메모리 모델로 함수 포인터가 원거리 포인터로 되어 있어 64 Kbyte 이상의 코드를 생성할 수 있습니다. 단, 데이터는 64 Kbyte로 한정됩니다. 이 메모리 모델을 사용하려면 분할 컴파일 방식을 사용해야 64 Kbyte를 사용할 수 있고, 하나의 C 소스 코드로 64 Kbyte가 넘는 코드를 만들 수는 없습니다.  

4. Compact model (중형)  
이 메모리 모델은 Medium model과 반대의 것으로 코드는 64 Kbyte를 넘을 수 없으며, 데이터 크기는 64 Kbyte를 넘을 수 있습니다. 단, 정적 데이터를 64 Kbyte 보다 크게 만들 수는 없습니다. Medium, Compact 두 모델은 실제로 거의 사용되지 않습니다.  

5. Large model (대형)  
파스칼, 베이식 등 대부분의 고급 언어가 사용하는 메모리 모델로 함수와 데이터 모두 원거리 포인터를 사용하고 분할 컴파일을 사용하여 1 Mbyte 전부 사용할 수 있는 메모리 모델입니다. 당시 판매되던 대부분의 상용 프로그램은 이 메모리 모델이라고 보면 됩니다.  

6. Huge model (거대형)  
Large model과 같이 함수, 데이터 모두 원거리 포인터를 사용하고 정적 데이터도 64 Kbyte 이상을 사용하는 사실상 어드레싱에 제한이 없는 메모리 모델입니다. 제한이 없다는 것은 곳 오버헤드를 동반하므로 무조건 이 메모리 모델을 사용한다고 될 일은 아닙니다. 그리고 정적 데이터를 64 Kbyte 이상 사용할 수 있다고 해서 배열이나 구조체 하나의 크기가 64 Kbyte를 넘을 수 있는 것은 아니고, 이것은 여전히 64 Kbyte를 넘을 수 없습니다.  

이 문제로 인하여 윈도우즈 9x 계열의 운영체제가 고질적인 리소스 부족 현상을 가지게 됩니다. 윈도우즈 9x 계열 운영체제의 경우 기존 16비트 프로그램과의 호환성 문제로 상당 부분에 16비트 코드가 남아 있었습니다. 특히 화면 처리는 16비트로 되어 있어서 GDI 처리를 위한 핸들 크기가 64 Kbyte 보다 작았습니다. 그래서 조금만 화려한 윈도우즈 프로그램을 실행하면 리소스가 바닥나서 다른 프로그램을 실행하기가 어려운 상태가 되어 버리곤 했습니다.

 

'Technical Note' 카테고리의 다른 글

마이크로컨트롤러의 발전  (0) 2019.06.06
ASCII Table (아스키코드 표)  (0) 2019.06.06
도스 프로그래밍 (메모리 모델)  (0) 2019.06.06
운영체제가 하는 일  (0) 2019.06.06
단일 칩 시스템 (SoC)  (0) 2019.06.06
안드로이드 코드 네임과 API Level  (0) 2019.06.06

댓글(0)

Designed by JB FACTORY