컴퓨터가 이해하는 정보
컴퓨터가 이해하는 정보는
- 데이터
- 숫자, 문자 등 정적인 정보
- 명령어에 종속적인 정보
- 명령어
- 수행할 동작
- 수행할 대상
컴퓨터의 핵심 부품
CPU(Central Processing Unit)
CPU는 정보를 읽어들이고, 해석하고, 실행하는 부품
구성
- ALU (산술논리연산장치)
- Control Unit (제어장치)
- 레지스터
이 중, CPU가 처리하는 명령어는 반드시 레지스터에 저장된다.
메모리 & 캐시 메모리
메인 메모리
메모리는 현재 ‘실행 중’인 프로그램을 구성하는 데이터와 명령어를 저장하는 부품
즉, 프로그램이 실행되려면 프로그램을 이루는 데이터와 명령어가 메모리에 저장되어 있어야 한다.
- 메모리는 ‘주소’를 통해 저장되어 CPU가 원하는 정보로 접근을 빠르게 할 수 있다.
- 메모리는 전원이 공급되지 않을 때 저장하고 있는 정보가 지워지는 ‘휘발성’을 갖는다.
캐시 메모리
캐시는 CPU와 메모리 사이에 있는 저장장치
빠른 메모리 접근을 보조하는 저장장치
- CPU안에 존재하기도, 밖에 존재하기도 한다.
보조기억장치
보조기억장치는 전원이 꺼져도 저장된 정보가 사라지지 않는 비휘발성 저장장치
메모리는 실행중인 프로그램을 저장, 보조기억장치는 보관할 프로그램을 저장
입출력장치
입출력장치는 컴퓨터 외부에 연결되어 컴퓨터 내부와 정보를 교환하는 장치
메인 보드와 버스
메인 보드는 핵심 부품을 연결하는 기판
메인 보드에 연결된 부품들은 각자의 역할을 수행하기 위해 서로 정보 교환
→ 이 때, 정보를 주고받는 통로를 ‘버스’라고함
시스템 버스는 핵심 부품(CPU, 메모리, 보조기억장치 등)을 연결하는 버스
컴퓨터가 이해하는 정보
CPU는 기본적으로 0과 1만을 이해한다. → 비트
- N비트는 2^N개의 정보 표현 가능
구분 | 비트 |
---|---|
1 byte | 8bit |
1 KB | 1,000 byte |
1 MB | 1,000 KB |
1 GB | 1,000 MB |
1 TB | 1,000 GB |
워드: CPU가 한번에 처리할 수 있는 데이터의 크기
→ 현재 운영체제의 32비트 64비트가 바로 이것
부동 소수점
CPU는 0과 1로만 모든 데이터를 처리하기 때문에 소수점을 나타내기에는 한계가 있다.
a = 0.1
b = 0.2
c = 0.3
if a + b == c:
print("Equal")
else:
print("Not Equal")
이 코드를 돌리면 거의 대부분의 언어에서 `Not Equal` 을 출력한다. 이는 현대 컴퓨터는 소수점을 부동 소수점 방식으로 저장하는데, 정밀도에 한계가 있기 때문 2진수의 지수와 가수를 다음과 같은 형식으로 저장한다.
IEEE 754
가수의 정수부에는 1로 통일된 ‘정규화된 수’가 저장되기 때문에
- 지수에 해당하는 값
- 지수는 바이어스 값이 더해져 저장됨(2^(k-1) -1)
- 소수 부분
이 두가지만 저장하면 된다.
소수 부분이 무한소수점이게 되면 컴퓨터가 일부 소수점을 생략하여 저장하게 되므로 위와같은 오차가 발생
0과 1로 문자 표현
문자 집합(컴퓨터가 이해할 수 있는 문자 집합) → 0 ,1 (실제 코드)
- 이 과정을 문자 ‘인코딩’이라고 한다.
- 이 반대의 과정은 ‘디코딩’
가장 기본적인 문자 집합은 아스키 코드가 있다.
아스키 코드는 8비트로 이루어져 있는데, 8비트 중 1비트는 패리티비트로 오류 검출용 비트이다.
따라서, 7비트로 문자를 표현해야 하므로 아스키 코드는 총 128개의 문자를 표현
하지만 아스키 코드로는 표현하지 못하는 문자가 많다
→ 이를 위해 유니코드 등장
- UTF-8과 같은 익숙한 명칭들이 유니코드 문자에 부여된 값을 인코딩 하는 방식
base64 역시 인코딩 방식으로 모든 데이터(이미지, 이진 데이터 등등)을 인코딩 할 수 있음
패딩은 빈 자리를 특정 문자로 채우는 것
명령어
‘명령어가 수행할 동작’ 연산 코드 + ‘동작에 사용될 데이터(혹은 저장된 위치)’ 오퍼랜드
연산자 + 피연산자
오퍼랜드에는 주로 데이터가 저장된 위치, 즉 ‘주소’가 명시된다. (물론 직접 데이터가 올 수 있음)
명령어 사이클
CPU가 명령어를 처리하는 과정에서 프로그램 속 각각의 명령어들은 일정한 주기를 반복하며 실행됨
- 명령어를 CPU로 가져오는 ‘인출 사이클’
- CPU로 가져온 명령어를 실행하는 ‘실행 사이클’
- 오퍼랜드에 있는 주소값을 실제로 접근하는 ‘간접 사이클’
- 인터럽트를 처리하는 ‘인터럽트 사이클’
CPU
레지스터
CPU 안에 있는 작은 임시 저장장치
- 프로그램 카운터 (PC)
- 메모리에서 다음으로 읽어 들일 명령어의 주소 저장
- 프로그램이 순차적으로 실행되는 이유 → PC가 1씩 증가
- 하지만 return 문 or 조건문과 같이 비순차적일 경우 PC가 임의로 위치 변경
- 명령어 레지스터
- 해석할 명령어를 저장하는 레지스터(메모리에서 가져온 명령어)
- 제어장치는 해당 레지스터의 내용을 해석한 뒤 ALU로 보내거나 다른 부품으로 제어신호를 보내 해당 부품 작동
- 범용 레지스터
- 데이터, 명령어, 주소 모두 저장 가능
- 일반적으로 여러개의 범용 레지스터들이 CPU안에 포진
- 플래그 레지스터
- 연산의 결과 or CPU의 상태에 대한 부가정보 ‘플래그’ 저장
| 종류 | 설명 |
| --- | --- |
| 부호 플래그 | 연산 결과의 부호 |
| 제로 플래그 | 연산 결과가 0인지의 여부 |
| 캐리 플래그 | 연산 결과에 올림수나 빌림수가 발생했는지의 여부 |
| 오버플로우 플래그 | 오버플로우가 발생했는지의 여부 |
| 인터럽트 플래그 | 인터럽트가 가능하지의 여부 |
| 슈퍼바이저 플래그 | 커널모드로 실행 중인지, 사용자 모드로 실행 중인지의 여부 |
- 스택 포인터
- 메모리 내 스택 영역의 최상단 스택 데이터의 위치를 가리키는 레지스터
인터럽트
CPU의 작업을 방해하는 신호
동기 인터럽트
- CPU에 의해 발생하는 인터럽트
- 프로그래밍 오류와 같은 예외적인 상황에 발생하는 인터럽트
- ‘예외’ 라고도 부름
비동기 인터럽트
- 입출력 장치에 의해 발생하는 인터럽트
- 입출력이 완료되었는지 등 상태에 따른 알림 역할
- 하드웨어 인터럽트
하드웨어 인터럽트
CPU는 효율적으로 명령어를 처리하기 위해 하드웨어 인터럽트 사용
인터럽트를 사용하지 않을경우
- CPU는 계속해서 입출력장치에게 작업이 완료되었는지 물어봐야함
- 이와 같은 기법 ‘폴링’
인터럽트를 사용할 경우
- CPU는 다른 작업 진행중
- 입출력이 끝난 입출력 장치가 하드웨어 인터럽트 발생
- CPU가 실행 사이클이 끝나고 명령어를 인출하기 전 인터럽트 확인
- 인터럽트 확인 후 인터럽트 플래그를 보고 받아도 되는지 확인
- 가능하다면 CPU 작업 백업 후 인터럽트 벡터를 참조하여 ‘인터럽트 서비스 루틴’ 실행
- 수많은 인터럽스 서비스 루틴을 구분하기 위해 인터럽트 벡터를 인터럽트를 요청한 대상으로 부터 전달받음
- 메모리 내 스택에 진행중이던 작업 백업
- 인터럽트 처리가 끝나면 백업 복구
예외
예외(동기 인터럽트)가 발생하면 하던 일을 중단하고 예외를 처리, 이 후 다시 복귀
복귀 후 예외가 발생한 명령어부터 or 예외가 발생한 명령어 다음 명령어 부터
→ 폴트와 트랩을 나누는 기준
- 폴트
- 예외가 발생한 명령어 부터
- 페이지 폴트가 가장 대표적인 예시
- 메모리에 저장되어 있어야 할 데이터가 없으면 페이지 폴트
- 디스크에서 메모리로 데이터를 가져오는 예외처리 후
- 다시 메모리로 데이터 접근 실행
- 트랩
- 예외가 발생한 명령어의 다음 명령어 부터
- 디버깅의 브레이크가 대표적인 예시
- 디버깅(트랩)을 걸면 해당 코드가 실행되는 순간 프로그램 정지
- 디버깅(트랩)이 끝나면 다음 명령어부터 실행
- 중단
- 프로그램 강제 중단 → 심각한 오류
- 소프트웨어 인터럽트
- 시스템 콜 발생시 발생하는 예외
CPU 성능 향상을 위한 설계
CPU의 클럭 속도
클럭속도가 높아지면 CPU의 명령어 사이클을 더 빠르게 반복하게 할 수 있다.
→ 발열과 같은 문제 발생 및 성능의 한계
멀티코어와 멀티스레드
여러개의 명령어를 해석하고 처리할 수 있는 모듈을 가진 CPU를
멀티코어 CPU, 멀티코어 프로세서 라고 부른다
하드웨어 스레드란 하나의 코어가 동시에 처리하는 명령어의 단위
→ 두개의 부품이 한번에 4개의 명령어를 처리 = 2코어 4스레드 CPU
소프트웨어 스레드란 하나의 프로그램에서 독립적으로 실행되는 단위
→ 메모리에 적재된 해당 프로그램을 구성하는 여러 부분이 동시 실행
병렬성
- 작업을 물리적으로 동시에 처리하는 성질
- 하드웨어 스레드 4개 → 4개의 명령어 동시처리
동시성
- CPU가 빠르게 작업을 번갈아 가며 처리
- 마치 동시에 처리되는 것 같은 느낌
파이프라이닝을 통한 명령어 병렬 처리
명령어가 처리되는 과정
- 명령어 인출
- 명령어 해석
- 명령어 실행
- 결과 저장
이 단계들이 겹치지만 않으면 CPU는 각각의 단계를 동시 실행할 수 있다.
따라서 이를 적절하게 분배하면 동시에 처리할 수 있는데 이런 기법을
명령어 파이프라이닝이라 한다.
CISC 와 RISC
CISC
- 복잡한 명령어로 구성된 명령어 집합
- 적은 수로 명령 실행 가능
RISC
- 단순한 명령어로 이루어진 명령어 집합
- 가능한 1클록 내외로 실행되는 명령어 지향
이렇게 보면 CISC가 우월해 보이지만 CISC는 복잡한 대신 실행하는데 시간이 걸림
→ 클록에 따라 규격화된 RISC가 명령어 파이프라이닝에 적합
Appendix
CPU의 레지스터와 CPU의 캐시가 다른가?
- 캐시와 레지스터는 어떤 명령어나 데이터를 임시적으로 저장해두는 저장공간인 것은 같다.
- 레지스터는 CPU 내부에서 연산을 처리하기 위한 데이터 저장 공간
- 캐시는 CPU와 별도로 있는 메인 메모리와의 속도 차이를 극복하기 위한 저장 공간
폰 노이만 구조 vs 하버드 구조
폰 노이만 구조
- 실행되는 프로그램이 컴퓨터 내부에 저장되는 구조
- Stored Program Concept
- 명령어와 데이터를 한 곳에 저장하므로, 복잡한 명령어를 수행하기 용이
- CPU와 메인 메모리 간의 통신은 폰 노이만 구조를 따른다
하버드 구조
- 명령용 버스와 데이터용 버스를 분할한 컴퓨터 구조
- CPU가 데이터와 명령어에 한번에 접근 가능
- 명령어와 데이터를 분리된 메모리에 저장하므로, 병렬적으로 데이터와 명령어에 접근이 용이
- 프로세스 내부 혹은 캐시 계층에서는 하버드 구조를 따른다
폰 노이만 구조 | 하버드 구조 | |
병목현상 | 발생 | 발생하지 않음 |
하드웨어 구조 | 단순 (구현비용 낮음) | 복잡 (구현비용 높음) |
명령어 코드 변경 | 가능 | 불가능 |
주 사용 ISA | CISC | RISC |
폰 노이만 구조가 병렬로 행동하지 않는 것은 아니지만, 명령과 데이터가 같은 신호 버스와 메모리를 사용하기 때문에 액세스가 동시에 발생할 수 없다.
32 bit vs 64 bit
#include <stdio.h>
int main(void){
int arr[10] = {0, };
int arrVal = (int)arr;
printf("pointer : %d \n", arrVal);
return 0;
}
이 코드의 문제점이 무엇일까?
해당 코드는 32비트 시스템에서는 문제가 되지 않는다. 32비트 시스템에서는 int는 4바이트이고, 포인터 역시 4바이트이기 때문.
하지만 64비트 시스템에서는 어떨까?
64비트 시스템에서는 int는 4바이트이고, 포인터는 8바이트이기 때문에 형 변환 과정에서 데이터 손실이 발생한다.
Window를 기준으로 WIN64는 16테라바이트의 메모리 공간을 활용할 수 있도록 설계되었다. 따라서 배열 arr이 4GB 이하의 메모리 영역에 할당되었다면 운이 좋게도 데이터 손실이 발생하지 않을 수 있지만, 그 이상의 영역에 할당되면 배열의 정확한 주소값을 확인할 수 없게 된다.
멀티 프로세서 혹은 멀티 스레드와 레지스터의 관계
멀티 프로세서 혹은 멀티 스레드는 여러개의 명령어를 처리해야 하는데, 레지스터는 스택의 형식이다.
하나씩 나오는 명령어를 자기 것인지 어떻게 알고 가져가는 걸까?
이 질문을 보고 의문을 느낀다면 앞의 내용을 다시 보고 와야함!
레지스터는
- 메모리에서 명령어를 가져올 주소를 저장하는 프로그램 카운터(PC)
- 메모리에서 가져온 명령어를 저장해두는 명령어 레지스터
가 존재한다. (더 많은 레지스터가 있음)
결국 어떤 스레드가 처리 되는건지는 레지스터에 어떤 스레드의 명령어가 들어오는지에 달린 것.
컨텍스트 스위칭이 되면 레지스터에 들어있는 값, 프로그램 카운터, 스택 카운터와 같은 값들이 메모리에 저장됨
즉, 레지스터의 값이 전부 다른 스레드로 덮혀 씌워진다.
따라서 레지스터의 명령어는 전부 실행중인 스레드의 것!
참고로 멀티 프로세서라면 코어가 여러개 있으므로 레지스터가 여러개 존재한다.
'Study > CS' 카테고리의 다른 글
3. 운영체제 (2) (0) | 2025.03.06 |
---|---|
3. 운영체제 (1) (0) | 2025.02.27 |
2. 컴퓨터 구조 (2) (1) | 2025.02.18 |