[인사이드 머신] - 2. 프로그램 실행의 원리
2. 프로그램 실행의 원리
1. 컴퓨터 동작의 기본 개념 에서 프로그램은
데이터 스트림을 가져와서 코드 스트림에 따라 처리하고 이를 다시 저장하는 과정의 반복 이라 하였다.
사실 컴퓨터는 기계어 1, 0 을 (전기 신호 on/off) 제외하곤 나머진 이해하지 못한다.
그러므로 컴퓨터 프로그램을 실행하기 위해서는 모든 명령어를 기계어인 바이너리표기(2진수) 해야한다.
우리가 앞에서 썼던 add A, B, C 와 같은 명령어가 사실은 어떻게 컴퓨터가 처리하는지 알아보자.
연산코드와 기계어
어셈블러 언어(assembly language) 의 등장
- 컴퓨터의 연산 속도가 증가하고, 메모리가 비약적으로 커지면서
프로그래머의 작업 효율을 돕기 위한 사용자 친화적인 개발 환경의 등장.- 기존에는 기계어로 프로그램을 작성하였다.
- 기계어 예시 : 1000100000001000 => add C, 8, A
- 기존에는 기계어로 프로그램을 작성하였다.
- 사람이 읽을 수 있는 프로그램을 기계어로 변환하는 지루한 작업을 자동화해주는 어셈블러가 등장.
- 프로그래머는 니모닉, 레지스터 이름, 메모리 주소 등을 이용하여 프로그램을 작성하고,
어셈블러는 이를 기계어로 변환
- 프로그래머는 니모닉, 레지스터 이름, 메모리 주소 등을 이용하여 프로그램을 작성하고,
니모닉(mnemonic)
- 컴퓨터가 이해할 수 있는 연산코드(opcode)라고 하는 2진수와
사람들이 이해할 수 있는 단어 (add, load, store 등)를 대응시켜
사람들이 쉽게 기억할 수 있도록 만든 기호. - 아래는 가상 컴퓨터 DLW-1 의 니모닉과 레지스터의 바이너리 코드 표이다.
가상 컴퓨터 DLW-1 의 니모닉과 레지스터 바이너리 코드.
- 니모닉으로 명령을 실행하게 되면
이 니모닉을 컴퓨터가 이해할 수 있는 바이너리로 인코딩 하게 된다.
산술 명령어의 바이너리 인코딩
- 위의 니모닉과 레지스터 바이너리 코드를 이용한 산술 명령어의 포맷이다.
- 산술 명령어는 2바이트 (16비트) 로 이루어져 있고 아래와 같은 구성이다.
- 모드(0 비트)
- 0 : 레지스터들만 사용하는 레지스터형 명령어
- add C, D, A
- 소스 목적어 모두 레지스터
- 1 : 직접값을 사용하는 직접형 명령어
- add C, 8, A
- 레지스터와 직접값을 사용
- 0 : 레지스터들만 사용하는 레지스터형 명령어
- 모드(0 비트)
레지스터형 산술 명령어 포맷과 변환 예시
레지스터형 명령어 기계어 포맷 예시
레지스터형 산술 명령어의 기계어 변환 예시
직접형 산술 명령어 포맷과 변환 예시
직접값형 명령어 기계어 포맷 예시
직접형 산술 명령어의 기계어 변환 예시
- 이처럼 우리가 흔히 쓰는 단어 load, add 등의 명령어를 어셈블리어가 기계어로 인코딩 하여 컴퓨터는 이를 실행한다.
프로그래밍 모델과 ISA
- 어셈블러를 사용하기 위해서는, 프로그래머가 각 시스템에서 사용 가능한 레지스터의 수,
명령어의 종류 등에 관한 정확한 정보를 알아야 한다.- add C, D, A 와 같이 쓰기 위해선 무슨 레지스터가 있는지, 어떤 연산이 있는지 정확히 알아야 한다.
- 프로그램을 수행하기 위한 기기에 대한 잘 정의된 모델의 필요성이 커짐.
프로그래밍 모델
- 마이크로프로세서와 프로그래머 사이의 인터페이스.
- 사용자는 프로세서의 실제 복잡한 구현에 관해 모르더라도,
여러 단계의 추상화를 거친 프로그래밍 모델에 따라서 프로세서의 기능들을 활용할 수 있다. - 아래 그림은 8개의 레지스터를 가진 컴퓨터의 프로그래밍 모델이다.
- ALU
- I/O (Input/Output) 장치
- Load 와 Store 명령에서 메몰와 데이터를 주고 받는 장치.
- 제어 장치
- PC (Program Counter)
- 프로그램 카운터
- IR (Instruction Register)
- 명령어 레지스터
- PC (Program Counter)
레지스터가 8개인 컴퓨터 프로그래밍 모델
명령어 레지스터와 프로그램 카운터
- 명령어 레지스터 ( IR : Instruction Register )
- 다음에 실행될 명령어를 가지고 있는 레지스터.
- 프로그램 카운터 ( PC : Program Counter )
- 다음에 실행될 명령어의 주소를 가지고 있는 특수 레지스터.
명령어와 데이터 레지스터가 있는 단순한 컴퓨터
ISA(Instruction set architecture)
- 명령어 집합 아키텍쳐.
- 실제 컴퓨터 구조와 상관없이 프로그래머는 프로그래밍 모델에 맞춰 소프트웨어를 만들면
ISA 가 이 소프트웨어와 하드웨어 사이에 중재자 역할을 하여 서로다른 하드웨어에서도 호환이 가능하게 해준다.- 자세한 내용은 마이크로아키텍쳐와 ISA.
ISA 는 여러 세대 하드웨어에 대해 소프트웨어에 일관된 인터페이스를 제공한다.
- 자세한 내용은 마이크로아키텍쳐와 ISA.
- 따라서 우리는 프로그래밍 모델을 이용하여 레지스터가 8개가 있다고 가정하고 프로그램을 작성하면 된다.
프로그램의 실행 원리
- 니모닉 단락의 가상 컴퓨터 DLW-1 의 명령어 길이가 2 바이트이고,
메모리 셀이 1바이트를 저장할 경우 DLW-1 의 프로그램이 저장된 메모리는 아래와 같다.- 프로그램의 시작 주소는 #500 이다.
베이스 주소가 #500 인 메모리공간에 저장된 프로그램.
fetch - execute cycle
- fetch - execute cycle
- fetch 페치
- 제어 장치는 페치 명령어를 이용해서 프로그램의 명령어들을 메모리에서 명령어 레지스터로 옮겨 놓는다.
- 프로그램 카운터에서 저장된 주소값을 증가시켜 다음 명령어를 읽어올 수 있도록 한다.
- decode 디코드
- 명령어 레지스터상의 명령어를 디코드 한다.
- 즉 명령어를 해석하여 어떻게 수행할지 결정.
- 명령어 레지스터상의 명령어를 디코드 한다.
- execute 수행
- 명령어 레지스터 상의 명령어를 수행한다.
- 산술 명령어인 경우 ALU 와 레지스터 파일을 이용하여 수행.
- 메모리 접근 명령어의 경우, 메모리 접근 하드웨어를 이용하여 수행.
- 명령어 레지스터 상의 명령어를 수행한다.
- fetch 페치
- 약간만 수정하면 대부분 마이크로 프로세서가 프로그램을 실행하는 과정을 설명할 수 있고
이를 페치 - 수행 싸이클(fetch - execute cycle) 이라 한다.
fetch - execute cycle exemaple
- #500 에 저장된 load #12, A 명령어를 페치하여 명령어 레지스터로 로드.
프로그램 카운터는 다음 명령어인 #502 로 증가. - 명령어 레지스터의 load #12, A 를 디코드.
- 명령어 레지스터의 load #12, A 를 수행.
- ** 프로그램 카운터가 가르키는 #502 명령어를 페치하고 1-3 과정을 반복**.
Clock
- 페치 - 수행 루프의 3 단계 과정을 처리하는 단위
- 페치 - 수행 루프는 1 클럭(Clock)에 처리된다.
- 클럭은 컴퓨터 내부 마더모드 상의 클럭 생성기에서 만들어진 후, 프로세서 내부로 공급된다.
- 더 빠른 클럭을 사용할 수록 프로그램 수행 속도 역시 빨라질 것이다.
- CPU 의 속도를 나타내는 지표로 자주 사용된다.
- 단위 Hz
분기 명령어
- 프로세서가 프로그램의 원래 정해진 순서에서 벗어나 특정 명령어로 jump 하게 해주는 명령어.
- 무조건 분기
- 명령어
- jump #목적주소
- 프로세서는 단순히 제어 장치에 있는 프로그램 카운터의 값을
분기 명령어의 목적 주소로 바꿔준다. - 프로세서는 다음 클럭에서 프로그램 카운터가 가르키는 주소
(분기 명령어의 목적 주소) 에서 명령어를 페치하게 된다.
- 명령어
- 조건 분기
- 명령어
- jumpz #목적주소
- 특정 조건이 만족될 때에만 분기한다.
- PSW(Processor Status Word)
- 산술 명령어가 수행될 때면, 연산 결과에 따라 다양한 형태의 정보가 PSW 에 저장
- 조건 분기를 실행하기 위해 PSW 에 저장되어 있는 값을 통해 분기 명령어의 조건을 확인.
- 조건 분기 예제
- sub A, B, C
- 레지스터 A의 값에서 레지스터 B의 값을 뻐서 그 결과를 C 에 저장
- jumpz #106
- PSW 값이 0 이면(즉 바로위 명령 sub 의 결과) 메모리주소 #106 에 저장된 명령어 실행.
- PSW 값이 0 이 아니면 순서대로 아래 명령어 실행.
- add A, B, C
- 레지스터 A의 값과 레지스터 B의 값을 더해서 그 결과를 C 에 저장
- sub A, B, C
- 명령어
분기 명령어를 이용한 특수 로드
- 일반적인 로드 명령어와 마찬가지로, 분기 명령어는 레지스터를 사용해서 목적 주소를 지정 가능하다.
- jump #C
- jump #(C + 30)
- 상대 주소를 목적 주소로 지정 가능.
- 이 기법을 사용하면 프로그래머는 분기하는 코드 작성시 프로그램의 절대 주소를 알 필요가 없다.
- 프로그래머는 메모리를 관리하는 운영체제가 해당 코드의 시작 주소를 어디에 저장해두었는지만 알면 된다.
- 코드 세그먼트 기본 주소가 C 에 저장되어 있을때 15번째 명령어로 분기하는 방법은 아래와 같다.
- jump #(C + 30)
- 각 명령어가 2 바이트일때 30바이트는 15번째 명령어를 의미함.
- jump #(C + 30)
분기 명령어와 레이블
- 실제 프로그램에서 분기문의 목적 주소로 직접값이나 상대 주소를 쓰는 경우는 드물고,
레이블을 지정하여 레이블을 분기 명령어로 사용한다. - 분기 명령어와 레이블 예제
sub A, B, A
jumpz LBL1
add A, 15, A
store A, #(D + 16)
LBL1 :
add A, B, B
store B, #(D + 16)
- 여기서 어셈블리어를 자세하게 다루지 않는다.
- 어셈블리어를 예제로 보여주는 이유는 상대주소를 설명하기 위해서이다.
- 따라서 여기서 어셈블리어를 자세히 볼 필요는 없다.
다음 주제
[인사이드 머신] 병렬화를 통한 성능 향상 - 파이프라인
부록 - 부팅
- bootstrap 의 줄임말
- 컴퓨터를 처음 키면 메모리상에 아무 프로그램도 없기때문에,
프로세서는 첫 명령어를 미리 지정된 메모리 주소에서 가져온다.- 이 저장된 프로그램을 BIOS 라고 한다.
- 이 BIOS는 대개 마더보드상의 ROM(Read Only Memory) 에 저장된다.
- ROM 은 RAM(Random Access Memory)와 다르게
읽기전용/비휘발성 메모리 라 BIOS 를 저장하기 알맞다. - 운영체제를 실행시킬 프로그램위에 누군가 덮어쓰거나, 재부팅시 날라가면 되겠는가?
- ROM 은 RAM(Random Access Memory)와 다르게
- BIOS는 주변 장치에 대한 기본적인 작동을 테스트 한 후, 무조건 분기를 한다.
- 이 BIOS의 무조건 분기 목적 주소는 부트로더의 시작 주소이다.
- BIOS는 컴퓨터의 제어 권한을 부트로더에 넘겨준다.
- 부트로더는 하드 디스크에서 운영체제를 읽어온다.
- 운영체제가 동작하면 사용자는 컴퓨터를 제어할 수 있게 된다.
사용자가 입력한 문자나 기호들을 컴퓨터가 이용할 수 있는 신호로 만드는 것
서로 다른 두 개의 시스템, 장치 사이에서 정보나 신호를 주고받는 경우의 접점이나 경계면이다. 즉, 사용자가 기기를 쉽게 동작시키는데 도움을 주는 시스템
복잡한 자료, 모듈, 시스템 등으로부터 핵심적인 개념 또는 기능을 간추려 내는 것
전원이 공급되지 않아도 저장된 정보를 계속 유지하는 컴퓨터 메모리