brunch

You can make anything
by writing

C.S.Lewis

by Younggi Seo Dec 16. 2019

C를 배우기 전에 반드시 알아야 할 것

H/W 구조 별 거 아니에요

'저장 프로그램 개념(stored-program concept)'으로 알려진 이 아이디어는 ENIAC 개발 프로젝트의 고문이었던 수학자 폰 노이만(John von Newumann)에 의해 제안되었다. 앨런 튜링(Alan Turing)도 거의 같은 시기에 이 개념을 개발하였다. 그 개념은 새로운 컴퓨터인 ENIVAC(Electronic Discrete Variable Computer)의 개발을 위하여 1945년에 폰 노이만이 작성한 제안서를 통하여 처음으로 발표하였다(William Stallings, 2006).



1946년 폰 노이만과 그의 동료들은 Princeton Institute for Advanced Studies에서 'IAS 컴퓨터'라고 하는 새로운 저장-프로그램 방식의 컴퓨터를 개발하기 시작하였다. IAS 컴퓨터는, 비록 1952년에서야 완성되었지만, 그 이후에 출현한 모든 목적용 컴퓨터들의 원형 모델이 되었다(William Stallings, 2006).



앞으로 컴퓨터가 이 저장 방식의 개념에서 벗어나는 방법으로 개발되지 않는 이상(저장식이 아닌 컴퓨터가 이미 있기도 하다), 프로그램 작동 시에 알아야 하는 H/W 구조의 기본 골격은 항상 동일하고 같은 개념에서 파생되기에 초기의 간단한 프로세서, 이를테면 아래의 IAS 컴퓨터의 일반적인 구조만 잘 알고 있으면 해당 지식을 반영구적으로 우려먹을 수 있다.



데이터와 명령어(instruction)들을 저장하는 주기억 장치(main memory)

2진수 데이터를 처리할 수 있는 산술 논리 연산장치(arithmetic-logical unit, ALU)

기억장치에 있는 명령어들을 해석하고 수행시키는 제어 장치(control unit)

제어 장치에 의하여 작동되는 입출력(I/O) 장치

클록(clock)* : 컴퓨터에서 일정 간격의 박자(tick)를 만들면 거기에 맞춰 CPU 안의 모든 구성 부품이 작업함. 이 박자는 펄즈(pulse) 혹은 클록 틱(clock tick)이라고 부르며 버스에서 여러 개의 부품이 연결되어 있는데, 메인보드의 클록이 틱을 보낼 때마다 데이터를 보낸다.



앞선 섹션에서 말한 폰 노이만의 저장 프로그램 개념이 중요한 까닭은 모든 프로그램은 메모리에 올려야만 실행이 가능하기 때문이다. 그리고 프로그램이 실행할 때 CPU에서 사용하는 주요 레지스터는 크게 시스템 사용자가 사용할 수 있는 가시적인 레지스터(user-visible register)와 시스템 자체에서만 사용되는 비가시적인 레지스터(control and status register)로 나뉘고, 가시적 레지스터는 말 그대로 일반적으로 사용하는 일반 목적용 레지스터와 데이터, 주소, 그리고 조건 코드들이 있다.

     


레지스터에는 하나의 데이터가 영구적으로 기록되지 않고 CPU가 연산장치를 통해 계산하기 위해서 임시적으로 저장하는 데 쓰이는데, 왜 이런 간접적인 방식이 사용될까? 프로그램이 실행하는데 모든 동작들은 전자 회로에 의하여 제어되며, 결과적으로 데이터 통로(버스)를 사용하게 된다. 전자회로를 간략화시키기(최대한 효율적으로 사용하기) 위하여 읽기 혹은 쓰기 동작을 위한 기억 장치 주소를 지정하는 데 사용되는 레지스터(IR, Instruction Register)가 ‘한 개’만 있으며, 출발지 혹은 목적지 주소를 지정하는 데 사용되는 레지스터(MAR, Memory Address Register)도 ‘한 개’만 존재한다.



그래서 일반 목적용 레지스터 같은 경우도 범용(다목적 용도)으로 쓰이기 때문에 한 번 쓰인 장소에 다시 덮어쓰기를 반복하는데, 이렇게 레지스터가 간접적으로 사용됨으로써 시스템 취약점(stack overflow)을 유발한다. 왜냐하면 한 번 저장된 장소에 시스템이 본래 수행하면서 쓰였던 값과 관계없는, 즉 의도하지 않았던 값으로 덮어쓰기(Displacement, 바꾸어놓기)가 되면 본래 실행하려던 루틴에서 벗어나는 수가 있고 이것은 일전의 섹션들에서 다룬 R.C.E.(Remote Control Execution)으로 운영체제의 수행 동작과는 관계없는 시스템 인터럽트**이다.



사용자에게 보이는 일반 범용 레지스터 중 주소 레지스터(AR)에는 세그먼트 포인터(Segment Pointer), 인덱스 포인터(Index Pointer), 스택 포인터(Stack Pointer) 레지스터들이 있다. 스택 포인터는 스택 주소 지정 방식을 사용하는 시스템에서 스택이 기억장치 내에 있으면 스택의 최상위(top of stacks)를 지정하는 레지스터가 필요한데 여기에 쓰인다. 이 레지스터를 사용하게 되면, PUSH(입력)나 POP(출력)과 같은 스택 연산에 오퍼레이터(연산자, + - * / %)가 포함되지 않아도 되는 묵시적 주소 지정(implied addressing) 방식이 사용될 수 있다(William Stallings, 2006).



참고로 시스템 설계 시 요점은 전체 레지스터들(일반목적용 또는 데이터 및 주소 레지스터들)의 수를 결정하는 것이다. 이것도 명령어 세트의 설계에 영향을 미치는데, 그 이유는 레지스터의 수가 증가할수록 더 많은 수의 오퍼랜드(피연산자로 연산하기 위한 숫자에 해당) 지정 비트들이 필요해지기 때문이다. 대개 8개에서 32개 사이의 레지스터들이 최적이다. 그러나 수백 개의 레지스터들을 사용하는 장점을 이용한 새로운 접근 방법이 몇몇 RISC(Reduced Instruction Set arChitecture) 시스템들에서 나타나고 있는데, 나중에 CISC(Complex Instruction Set arChitecture) 시스템과 시스템 효율성을 비교한 부분에서 좀 더 다뤄보겠다.



특수 목적용 레지스터에는 크게 다음과 같이 나뉜다.


프로그램 카운터(Program Counter, 여기서 카운터는 계산이라는 뜻보다는 ‘순서 판단’이라는 의미가 용도의 뜻에 맞는 것 같다.) : 기억장치로부터 읽힐 다음 명령어의 '주소'를 저장한다. IP(Instruction Pointer)라고도 하는데, 32bit 아키텍처에서 EIP 레지스터로 버퍼 오버플로 취약점 테스트 시에 특정 주소로 바꾸어놓기(displacement)가 가능한 간접 레지스터의 하나이다.

명령어 레지스터(Instruction Register, IR) : 실행될 명령어의 8비트 연산 코드를 저장한다. 즉 현재 실행 중인 명령어를 보관하고 명령어 레지스터에 있는 명령이 제어 장치(Control Unit) 내부에서 해석(decode)되어 외부장치에 적절한 신호로 보내진다.


기억장치 버퍼 레지스터(Memory Buffer Register, MBR) : 기억장치에 저장될 단어를 가지고 있거나, 기억장치로부터 읽힌 단어(CPU의 기본 단위인 word를 지칭) 즉 데이터를 저장하는 데 사용된다.

기억장치 주소 레지스터(Memory Address Register, MAR) : MBR로부터 쓰이거나 읽힐 데이터의 기억장치의 '주소'를 저장함.

누산기(Accumulator, AC) Multiplier-Quotient(MQ) : 계산에 사용될 데이터와 ALU 계산 결과를 일시적으로 저장한다. 예를 들면,  개의 40 비트 수를 곱하면 80 비트 (2진수다) 되는데, 그들  상위 40 비트는 누산기에 저장되고 하위 40 비트 MQ 저장된다(William Stallings, 2006).



위의 레지스터들이 프로세스가 실행되면 아래의 그림과 같은 명령어 사이클(instructioin cycle)을 반복적으로 수행하면서 동작한다.

IAS 연산의 전체 흐름도 (@copyright William Stallings, 2006) 아래 프로그램 컨트롤 유니트는(PCU)임. 오타로 보임.




첫 번째, 현재 실행 중인 코드의 행 번호 '1'이 프로그램 카운터(PC)에 저장된다. 메인 메모리의 주소지에 첫 번째 행을 의미하는 '1'이다. 그리고 이 번호를 제어장치(PCU)에 전달한다.


두 번째, 프로그램 제어 장치(PCU)가 명령어 레지스터(IR)에 있는 명령을 해석하여(Decode) 메모리에 있는 데이터를 가져오라는 '제어신호(Control Signal)를 보낸다.


세 번째, MAR에는 '100'이 저장되고, 메모리 관리자는 메모리의 100번지에 저장된 값 '2'를 가져옴. 즉 메인 메모리의 주소 100번에 '2'라는 값이 저장되어 있다.


네 번째, 프로그램 제어장치(PCU)는 메모리 버퍼 레지스터(MBR)에 저장된 값을 레지스터 2(레지스터의 주소)로 옮긴다.

 


위와 같은 프로그램 동작은 RISC(Reduced Instruction Set arChitecture)라는 축소된 아키텍처에서 일반적인 CPU 내의 연산과정이다. 시스템 아키텍트(설계자)가 메모리나 레지스터 이용률을 극대화시키는 까닭은 최적화를 한 CISC(Complex Instruction Set arChitecture)라는 혼합 명령어 세트 구조를 설계하기 위함이다. 그리고 컴파일러에 의한 고급 언어(High-Level Language, HLL)에 해당하는 자바나 C++ 등을 선호하는 까닭은 컴파일러를 단순화시키기 위한 것과 성능을 향상하려는 것이다. 따라서 아키텍처 설계자들은 고급 언어(C도 고급언어다.)를 더 잘 지원하는 컴퓨터를 설계하려고 한다.   



그런데 컴퓨터 시스템 사양이 발달할수록, 즉 메모리 사양이 상향되면서 굳이 CISC의 프로그램이 더 작아지고 실행이 더 빨라질 것이라는 것의 이점이 프로그램 동작 간의 장점으로 극대화되지 않고 있다.


You Know What I am Saying?




 




* 클록은 1초당 클록(clock/sec.)의 헤르츠(Hz)를 단위로 쓰며 CPU 속도를 나타낸다. Intel Core i7일 경우 3.4 GHz의 처리속도를 가지는데, 이는 1초에 clock tick, 즉 CPU가 1초에 3십억 번의 연산이 가능하다는 것을 나타냄.


** 시스템 인터럽트 : 작업의 효율성을 높이기 위해 CPU가 입출력 관리자에게 입출력 작업 요청을 하여 작업을 마친 입출력 관리자가 CPU에게 보내는 신호





Reference:

Stallings, W. (1996). Computer organization and architecture. Englewood Cliffs, NJ: Prentice Hall.


   




브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari