컴파일러와 인터프리터 그리고 컴파일 과정
컴파일러
컴파일러는 고급 언어로 작성된 프로그램을 해당 머신이 알아들을 수 있도록 번역해주는 도구입니다. 컴파일 과정을 통해 실행 파일이 생성되며, 이 파일은 독립적으로 실행할 수 있습니다.
컴파일 과정
c, java, python
(source program) → Compiler
→ 실행 머신의 언어(Target Program)
컴파일러의 동작 과정은 다음과 같은 단계로 이루어집니다:
- Preprocessor :
#include
와 같은 전처리 지시자에 따라 소스 코드의 주소를 적절히 변경하여 실행할 코드를 가져오는 단계입니다. - Compilation : 전처리된 코드를 어셈블리 언어로 변환합니다.
- Assembler : 어셈블리 언어 코드를 머신 코드로 바꿉니다.
- Linker : 변환된 머신 코드와 라이브러리를 하나로 묶어 최종 실행 파일을 만듭니다.
인터프리터
인터프리터는 컴파일러와 달리 소스 코드를 한 줄씩 번역과 동시에 실행합니다. 이 과정에서는 중간 실행 파일이 생성되지 않고, 프로그램의 실행 환경이 호환성이 높은 경우가 많습니다. 대표적인 예로는 JVM(Java Virtual Machine)이 있습니다. JVM은 특정 플랫폼에 종속되지 않고 독립적으로 동작하여 여러 환경에서 코드 호환성을 제공합니다.
어셈블러
어셈블러는 컴파일러의 일부분으로, 어셈블리 언어 코드를 0과 1로 이루어진 머신 코드로 번역합니다.
컴파일 과정의 상세 단계
컴파일 과정은 크게 프론트엔드와 백엔드로 나눌 수 있습니다.
- Source Program : 소스 코드는 일반 텍스트(ASCII) 파일 형식입니다.
- Lexical Analyzer : 소스 코드를 토큰으로 분해합니다. 예를 들어,
for
는 키워드,i
는 식별자(identifier),==
는 연산자(operator)로 분류됩니다. - Syntax Analyzer : 문법 분석을 통해 트리 구조를 생성하여 올바른 문법인지 확인합니다.
- Intermediate Code Generator : 특정 머신과 독립적인 중간 코드를 생성합니다. 이를 통해 코드의 이식성을 높이고 유동적인 코드 최적화를 가능하게 합니다.
- Code Optimizer : 해당 머신에서 가장 효율적으로 실행되도록 코드를 최적화합니다.
- Target Code Generator : 최종적으로 머신에 맞는 타겟 코드를 생성합니다.
프론트엔드는 코드 분석과 변환을, 백엔드는 최적화와 타겟 코드 생성을 담당합니다.
Cross Compilation과 Native Compilation
컴파일의 방식은 타겟 머신 코드에 따라 다를 수 있습니다.
- Native Compilation : 현재 시스템에서 실행할 코드를 컴파일할 때 사용합니다.
- 주요 단계: Preprocessing → Compilation → Assembler
- Cross Compilation : 다른 시스템에서 실행할 코드를 컴파일할 때 Linker 단계가 추가됩니다.
Makefile
컴파일 과정에서 makefile은 소스 코드의 빌드 방법을 미리 정의한 파일로, 협업이나 큰 프로젝트에서 자동화된 컴파일을 위해 사용됩니다. Makefile을 사용하면 코드의 개별 파일을 하나하나 컴파일할 필요 없이 사전에 정의된 방법에 따라 전체 프로젝트를 자동으로 빌드할 수 있습니다.
Machine State와 CPU 구성 요소
CPU 기본적으로 Register, ALU, 그리고 Control Unit으로 구성됩니다.
- Register : 데이터를 일시적으로 저장하는 32비트 플립플롭(flip-flop)으로 구성되며, 서버에서는 128개 정도가 일반적입니다.
- Memory : 프로그램이 상주하는 공간으로, 바이트 단위 주소로 데이터를 관리합니다.
- ALU (Arithmetic Logic Unit) : 산술 및 논리 연산을 수행하는 장치로, 마이크로 연산을 통해 레지스터에 저장된 데이터를 조작합니다.
- Control Unit : ALU의 동작을 지시하고, 레지스터 간 정보 전송을 제어하는 역할을 합니다.
- Register Set : 명령어 실행에 필요한 데이터를 보관하는 역할을 합니다.
메모리의 크기 예시:
- 2^30은 1GB이며, 이는 보통 4바이트 단위로 표현됩니다.
align 되어있다 -> 데이터 크기의 배수로 저장된다.
Machine Instruction
- Operation : ADD, MULT, LOAD, STORE, JUMP
- Operends : Source Operands(input data), Destination Operands(output data), The Location can be
Instruction Types
- Arithmethic and logic Operations : ADD, MULT, F.DIVIDE, XOR, F.ADD
- Data transfer Instructions : LOAD, STORE
- Controltransfer Instructions : JUMP, CALL, RETURN -> 프로그램 카운터 명령어의 주소를 바꿔준다.
- 프로그램 카운터 : 다음 실행할 명령어의 주소를 가지고 있는 것
- SRAM : Flip-flop으로 작동하는 방식 -> 내용이 변화하지 않는 안정적인 메모리
- DRAM : Capacitor로 작동하는 방식 -> Capacitor는 시간이 지나면 스스로 방전 -> 시간이 흐름에 따라 자연적으로 메모리가 변화함.
DRAM은 Capacitor의 충방전을 계속 신경써야 하므로 상대적으로 속도가 느리다. 반면에 메모리 용량이 크다. (대형 컴퓨터)
SRAM은 메모리만 기억하고 있으면 되므로 속도가 빠르다, 그래서 Cache Memory로 사용된다.(임베디드 컴퓨터)
CISC vs RISC
CPU가 이해할 수 있는 명령어들의 모음을 Intstruction Set 또는 Instruction Set Architecture라고 한다. ISA가 다르다는 건 CPU가 이해할 수 있는 명령어가 다르다는 뜻이고, 명령어가 달라지면 어셈블리어도 달리진다. 같은 소스 코드로 만들어진 프로그램이라 해도 ISA가 다르면 CPU가 이해할 수 있는 명령어도 어셈블리어도 달라진다.
- CISC : Complex Instruction Set Computer, 복잡한 명령어 집합을 갖는 CPU 아키텍처. 명령어가 복잡하기 때문에 명령어 해석하는 데 시간이 오래 걸리고, 명령어 해석에 필요한 회로도 복잡하다. 보통 풍부한 Addressing 기능을 갖추어 명령의 직교성이 좋다. 연산에 대해서는 레지스터와 레지스터 연산, 레지스터와 메모리 연산, 메모리와 메모리 연산 모두 갖추고 있는 것이 보통이다. 피연산자는 2개에서 3개까지 지정할 수 있는 경우가 많다. 명령어의 길이가 가변적이고, x-86, x-86-64는 대표적인 CISC기반의 ISA이다. CISC 활용하는 명령어는 명령어 수행 시간이 길고, 가지각색이라 파이프라인이 효율적으로 명령어를 처리할 수 없어 현대 CPU의 성능에 한계가 발생할 수 있다. 데스크탑 PC 등에 우위를 갖는다.
- RISC : Reduced Instruction Set Computer, CPU 명령어의 개수를 줄여 하드웨어 구조를 좀 더 간단하게 만드는 방식, SPARC나 MIPS 등 아키텍처에서 사용된다. 전통적인 CISC CPU에는 프로그래밍을 돕기 위해 많은 수의 명령어와 주소 모드가 존재했는데, 실제로 쓰이는 명령어는 몇 개 되지 않는다는 사실을 바탕으로 적은 수의 명령어만으로 명령어 집합을 구성한 것이 RISC이다. load, store 2개로 메모리에 직접 접근하는 명령어를 2개로 제한할 만큼 메모리 접근을 단순화하고, 최소화를 추구한다. 일정한 길이의 명령어와 스마트폰, 태블릿에 우위를 갖는다.
Little endian -> LSB부터 저장하는 방식
Big endian -> MSB부터 저장하는 방식
1Byte가 8bit인 이유
1.아스키코드 : 1바이트는 8비트로, 7비트(2**7=128)는 데이터 값을, 1비트는 오류검출(패리트비트)을 위해 사용.
-> 미국은 아스키코드 내의 문자면 충분했기 때문에, 아스키 문자 인코딩을 기준으로 1바이트가 8비트가 됐다
LSB 위치는 가장 값이 작은 비트인 2^0에 위치하고 있으므로 LSB의 값을 확인하면 숫자가 짝수인지 홀수인지 쉽게 알아낼 수 있다. LSB는 프로그래밍시 주로 난수발생 함수, 해시 함수, 검사합(Checksum) 함수 등에서 많이 쓰인다. 왜냐하면 값이 조금이라도 변경된다면, 데이터 형의 최하위 비트에서 변화가 발생할 확률이 높기 때문이다. MSB 는 최상위 비트로 signed 타입에서는 부호비트의 자리이다. 따라서 MSB만 비트연산을 통해 확인하면 양수인지 음수인지 알아낼 수 있다.
procedure Call & Return -> 메모리를 통해서 전달하면 오버헤드가 크기 때문에, argument로만 사용되는 레지스터가 있다.(a0 ~ a3)
http://www.kocw.net/home/cview.do?lid=6a156520027497a9
컴퓨터구조
컴퓨터의 구성 요소인 프로세서와 메모리, 입출력 장치의 동작원리와 내부 구조에 대하여 학습한다.
www.kocw.net
'독서 > 컴퓨터구조' 카테고리의 다른 글
Pipeline (2) | 2024.11.05 |
---|---|
Computer Arithmetic (0) | 2024.11.05 |
Linking (0) | 2024.10.25 |