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 의 니모닉과 레지스터의 바이너리 코드 표이다.

mnemonic 가상 컴퓨터 DLW-1 의 니모닉과 레지스터 바이너리 코드.


  • 니모닉으로 명령을 실행하게 되면
    이 니모닉을 컴퓨터가 이해할 수 있는 바이너리로 인코딩 하게 된다.

산술 명령어의 바이너리 인코딩

  • 위의 니모닉과 레지스터 바이너리 코드를 이용한 산술 명령어의 포맷이다.
  • 산술 명령어는 2바이트 (16비트) 로 이루어져 있고 아래와 같은 구성이다.
    • 모드(0 비트)
      • 0 : 레지스터들만 사용하는 레지스터형 명령어
        • add C, D, A
        • 소스 목적어 모두 레지스터
      • 1 : 직접값을 사용하는 직접형 명령어
        • add C, 8, A
        • 레지스터와 직접값을 사용

레지스터형 산술 명령어 포맷과 변환 예시

register-format 레지스터형 명령어 기계어 포맷 예시

imm-format 레지스터형 산술 명령어의 기계어 변환 예시


직접형 산술 명령어 포맷과 변환 예시

imm-format 직접값형 명령어 기계어 포맷 예시

imm-format 직접형 산술 명령어의 기계어 변환 예시


  • 이처럼 우리가 흔히 쓰는 단어 load, add 등의 명령어를 어셈블리어가 기계어로 인코딩 하여 컴퓨터는 이를 실행한다.


프로그래밍 모델과 ISA

  • 어셈블러를 사용하기 위해서는, 프로그래머가 각 시스템에서 사용 가능한 레지스터의 수,
    명령어의 종류 등에 관한 정확한 정보를 알아야 한다.
    • add C, D, A 와 같이 쓰기 위해선 무슨 레지스터가 있는지, 어떤 연산이 있는지 정확히 알아야 한다.
  • 프로그램을 수행하기 위한 기기에 대한 잘 정의된 모델의 필요성이 커짐.

프로그래밍 모델

  • 마이크로프로세서와 프로그래머 사이의 인터페이스.
  • 사용자는 프로세서의 실제 복잡한 구현에 관해 모르더라도,
    여러 단계의 추상화를 거친 프로그래밍 모델에 따라서 프로세서의 기능들을 활용할 수 있다.
  • 아래 그림은 8개의 레지스터를 가진 컴퓨터의 프로그래밍 모델이다.
    • ALU
    • I/O (Input/Output) 장치
      • Load 와 Store 명령에서 메몰와 데이터를 주고 받는 장치.
    • 제어 장치
      • PC (Program Counter)
        • 프로그램 카운터
      • IR (Instruction Register)
        • 명령어 레지스터

regi-program-model 레지스터가 8개인 컴퓨터 프로그래밍 모델

명령어 레지스터와 프로그램 카운터

  • 명령어 레지스터 ( IR : Instruction Register )
    • 다음에 실행될 명령어를 가지고 있는 레지스터.
  • 프로그램 카운터 ( PC : Program Counter )
    • 다음에 실행될 명령어의 주소를 가지고 있는 특수 레지스터.

regi-program-model 명령어와 데이터 레지스터가 있는 단순한 컴퓨터


ISA(Instruction set architecture)

  • 명령어 집합 아키텍쳐.
  • 실제 컴퓨터 구조와 상관없이 프로그래머는 프로그래밍 모델에 맞춰 소프트웨어를 만들면
    ISA 가 이 소프트웨어와 하드웨어 사이에 중재자 역할을 하여 서로다른 하드웨어에서도 호환이 가능하게 해준다.
    • 자세한 내용은 마이크로아키텍쳐와 ISA. ISA ISA 는 여러 세대 하드웨어에 대해 소프트웨어에 일관된 인터페이스를 제공한다.


  • 따라서 우리는 프로그래밍 모델을 이용하여 레지스터가 8개가 있다고 가정하고 프로그램을 작성하면 된다.


프로그램의 실행 원리

  • 니모닉 단락의 가상 컴퓨터 DLW-1 의 명령어 길이가 2 바이트이고,
    메모리 셀이 1바이트를 저장할 경우 DLW-1 의 프로그램이 저장된 메모리는 아래와 같다.
    • 프로그램의 시작 주소는 #500 이다.

ISA 베이스 주소가 #500 인 메모리공간에 저장된 프로그램.

fetch - execute cycle

  • fetch - execute cycle
    • fetch 페치
      • 제어 장치는 페치 명령어를 이용해서 프로그램의 명령어들을 메모리에서 명령어 레지스터로 옮겨 놓는다.
      • 프로그램 카운터에서 저장된 주소값을 증가시켜 다음 명령어를 읽어올 수 있도록 한다.
    • decode 디코드
      • 명령어 레지스터상의 명령어를 디코드 한다.
        • 즉 명령어를 해석하여 어떻게 수행할지 결정.
    • execute 수행
      • 명령어 레지스터 상의 명령어를 수행한다.
        • 산술 명령어인 경우 ALU 와 레지스터 파일을 이용하여 수행.
        • 메모리 접근 명령어의 경우, 메모리 접근 하드웨어를 이용하여 수행.
  • 약간만 수정하면 대부분 마이크로 프로세서가 프로그램을 실행하는 과정을 설명할 수 있고
    이를 페치 - 수행 싸이클(fetch - execute cycle) 이라 한다.

fetch - execute cycle exemaple

  1. #500 에 저장된 load #12, A 명령어를 페치하여 명령어 레지스터로 로드.
    프로그램 카운터는 다음 명령어인 #502 로 증가.
  2. 명령어 레지스터의 load #12, A 를 디코드.
  3. 명령어 레지스터의 load #12, A 를 수행.
  4. ** 프로그램 카운터가 가르키는 #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 에 저장


분기 명령어를 이용한 특수 로드

  • 일반적인 로드 명령어와 마찬가지로, 분기 명령어는 레지스터를 사용해서 목적 주소를 지정 가능하다.
    • jump #C
    • jump #(C + 30)
      • 상대 주소를 목적 주소로 지정 가능.
  • 이 기법을 사용하면 프로그래머는 분기하는 코드 작성시 프로그램의 절대 주소를 알 필요가 없다.
    • 프로그래머는 메모리를 관리하는 운영체제가 해당 코드의 시작 주소를 어디에 저장해두었는지만 알면 된다.
    • 코드 세그먼트 기본 주소가 C 에 저장되어 있을때 15번째 명령어로 분기하는 방법은 아래와 같다.
      • jump #(C + 30)
        • 각 명령어가 2 바이트일때 30바이트는 15번째 명령어를 의미함.

분기 명령어와 레이블

  • 실제 프로그램에서 분기문의 목적 주소로 직접값이나 상대 주소를 쓰는 경우는 드물고,
    레이블을 지정하여 레이블을 분기 명령어로 사용한다.
  • 분기 명령어와 레이블 예제
  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 를 저장하기 알맞다.
      • 운영체제를 실행시킬 프로그램위에 누군가 덮어쓰거나, 재부팅시 날라가면 되겠는가?
  • BIOS는 주변 장치에 대한 기본적인 작동을 테스트 한 후, 무조건 분기를 한다.
    • 이 BIOS의 무조건 분기 목적 주소는 부트로더의 시작 주소이다.
  • BIOS는 컴퓨터의 제어 권한을 부트로더에 넘겨준다.
  • 부트로더는 하드 디스크에서 운영체제를 읽어온다.
    • 운영체제가 동작하면 사용자는 컴퓨터를 제어할 수 있게 된다.

사용자가 입력한 문자나 기호들을 컴퓨터가 이용할 수 있는 신호로 만드는 것
서로 다른 두 개의 시스템, 장치 사이에서 정보나 신호를 주고받는 경우의 접점이나 경계면이다. 즉, 사용자가 기기를 쉽게 동작시키는데 도움을 주는 시스템
복잡한 자료, 모듈, 시스템 등으로부터 핵심적인 개념 또는 기능을 간추려 내는 것
전원이 공급되지 않아도 저장된 정보를 계속 유지하는 컴퓨터 메모리