<컴퓨터의 계층 구조>
레지스터 > L1/L2/L3 캐시(SRAM) > 메인 메모리(DRAM) -> 보조 저장장치(SSD/HDD) -> 원격/네트워크 스토리지
먼저 CPU 안에 있는 레지스터에서 계산할 때 사용할 값을 저장해둔다.
최근에 쓰인 데이터를 SRAM 에 보관해두고, 추가로 필요하면 L1, L2, L3 캐시 순으로 가져온다. 각 캐시들은 숫자가 커질수록 속도는 느리지만 용량은 커진다.
다음으로는 주기억장치, 즉 메인 메모리에서 가져오고 이후로는 보조 저장장치, 원격/네트워크 스토리지 순으로 가져온다.
CPU 는 처리량이 적고 복잡한 연산을 처리하는 소량의 코어이다.
GPU 는 동일한 여러개의 연산을 처리하는 수천 개의 경량 코어이다.
#include 나 #define 같은 전처리 지시문을 처리하여 실제 컴파일러 프론트엔드가 읽을 수 있는 텍스트를 만든다.
전처리가 진행된 후, 컴파일 타임에서는 소스 텍스트를 읽어 의미 단위 (토큰) 로 쪼갠다.
ex) int, main, (, ), {, return, 0, ;, }
이곳에서 문법적 오류 (미완성 문자열, 잘못된 문자 등) 을 잡아준다.
다음으로 이 토큰들은 문법에 맞게 트리 형태로 변환한다. 이때 트리를 추상문법트리(AST) 라고 한다.
ex) int main > 함수노드, () > 매개변수노드, { return 0; } > 반환식노드
이후에는 타입 검사, 스코프 확인, 이름 해석, 오버로딩/템플릿 인스턴스화, 상수표현 평가 등을 한다.
이를 통해 타입 불일치, 선언되지 않은 변수 사용, 함수 인자 개수 불일치 등 여러 오류를 잡아낸다.
다음으로는 제일 중요한 중간표현 (IR) 생성과 최적화이다.
IR 은 소스 언어와 기계어의 중간 형태로, 어떤 언어에서든 이 IR 을 거쳐 다른 문법에서도 같은 구조로 표현할 수 있게 해주며, CPU 아키텍처에도 종속되지 않기 때문에 다양한 CPU 에 적용이 가능하다.
즉, IR 은 언어 독립적이며 CPU 독립적인 컴파일러가 이해하기 쉬운 중간 언어이다.
상수 접힘(Constant Folding), 불필요 연산을 제거하고 루프 최적화, 함수 인라이닝, 공통 부분식 제거 등을 통해 최적화를 진행한다.
IR > 어셈블리 > 기계어로 변환을 시켜준다.
어셈블리를 기계어로 변환할 때 코드 섹션, 데이터, 심볼 테이블, 재배치 정보 등이 담긴 목적 파일을 생성한다.
여러 오브젝트 파일과 라이브러리를 결합하여 하나의 실행 파일 / 공유 라이브러리를 생성한다.
| Linking | 로드 타임 | 특징 |
|---|---|---|
| 정적 링크(Static Linking) | 컴파일 타임 | 파일 크기가 크고, 배포가 간편 |
| 동적 링크(Dynamic Linking) | 런 타임 | 메모리 공유가 가능하고, 업데이트 용이 |
소스 코드 > 전처리 > 컴파일 > AST 변환 > IR 생성 > IR 최적화 > 어셈플 > 목적 파일 > 링크 > 실행파일
프로세스는 실행 중인 프로그램으로, 자기만의 가상 메모리 공간을 가진다.
즉, 다른 프로세스와 섞이지 않는다.
| 메모리 영역 | 역할 | 특징 | 성장 방향 |
|---|---|---|---|
| 코드 영역(Code Segment) | 실행 코드 | 읽기 전용, 공유 가능 | 고정 |
| 데이터 영역(Data Segment) | 전역/정적 변수 | 초기화 여부에 따라 프로그램 종료 시까지 유지 | 고정 |
| 힙(Heap) | 동적 할당 영역 (malloc/new) | 크기 가변, 런타임 관리 필요, 속도 느림 | 낮은 주소 > 높은 주소 |
| 스택(Stack) | 함수 호출 정보 저장 | LIFO 구조, 자동 관리, 속도 빠름 | 높은 주소 > 낮은 주소 |
실행 중인 독립 프로그램으로 자기만의 메모리 공간을 가진다.
프로세스 안에서 실제로 일을 하는 작은 실행 단위이다. 여러 스레드가 같은 메모리를 공유한다.
프로그램은 비슷한 데이터를 반복해서 쓰는 경향이 있다.
| 지역성 | 특징 |
|---|---|
| 시간 지역성 | 방금 쓴 데이터는 곧 다시 쓴다. |
| 공간 지역성 | 옆에 있는 데이터도 곧 쓴다. |
이와 같은 지역성 원리 덕분에 캐시가 잘 동작할 수 있는 것이다.
메모리가 조각나서 효율이 떨어지는 현상이다.
| 단편화 | 특징 |
|---|---|
| 내부 단편화 | 필요보다 큰 공간을 할당받아 일부가 낭비됨. |
| 외부 단편화 | 빈 공간은 많은데 흩어져 있어 큰 덩어리를 쓰지 못함. |
이를 해결하기 위해서는 크기를 일정 단위로 관리하는 메모리 풀 / 버디 시스템을 사용해야한다.
또는 가비지 컬렉션으로 사용되지 않는 메모리를 찾아 해제하거나 메모리 압축을 통해 사용 중인 메모리를 모아 연속 공간을 확보한다.
다른 방법으로는 동적 할당을 최소화 하거나 미리 할당하여 단편화를 방지하고 성능을 안정화 시킨다.
CPU가 한 프로세스에서 다른 프로세스로 바꿔 실행할 때, 이전 프로세스의 상태(레지스터, 스택 포인터 등)를 저장하고 새 프로세스 의 상태를 불러오는 것이다.
CPU는 한 번에 하나의 프로세스만 실행이 가능한데, 시스템은 여러 프로세스가 동시에 실행되어야 한다. 이를 위해 존재하는 과정이다.
데드락은 교착 상태라는 뜻을 가진다.
각 참여자가 서로가 가진 자원을 기다려 영원히 진행을 못하는 상태이다.
| 데드락 발생 조건 (Coffman 4조건) | 내용 |
|---|---|
| 상호 배제(Mutual Exclusion) | 자원은 한 번에 하나의 프로세스만 사용 가능 |
| 점유와 대기(Hold and Wait) | 최소한 하나의 자원을 점유한 상태로 다른 자원을 기다리는 상태 |
| 비선점(No Preemtion) | 이미 점유한 자원을 강제로 빼앗을 수 없음 |
| 순환 대기(Circular Wait) | 프로세스 A는 B, B는 C, ... 마지막 프로세스는 A가 가진 자원을 기다리는 순환 구조 |
표에 적힌 4조건이 모두 만족되면 데드락이 발생한다.
데드락 발생 조건 중 하나를 미리 깨뜨린다.
| 예방법 | 내용 |
|---|---|
| 순환대기 방지 | 자원 사용 순서를 정함 |
| 비선점 완화 | 필요 시 자원을 강제로 해제 가능 |
| 점유대기 방지 | 모든 자원을 한번에 요청 / 기다리지 않고 포기 |
여러 스레드가 동시에 접근하면 안 되는 코드 영역이다.
공유 변수 수정 같은 부분이 여기에 해당한다.
이를 보호하기 위해서 락(뮤텍스, 세마포어)을 사용한다.
아래에 이에 대한 추가 설명이 있다.
조건을 만족할 때까지 계속 CPU를 차지하며 반복 확인하는 방식으로, CPU의 낭비가 심하다.
대기 상태에서 슬립(block)시키고 CPU는 다른 일을 처리하게 해야한다.
함수 호출이 너무 깊거나 지역 변수를 너무 크게 잡을 때 스택 메모리가 한도를 넘어서 발생하는 오류이다.
무한 재귀 함수 같은 곳에서 이러한 오류가 발생한다.
ex) void F() { F(); }
엔디안의 정의는 멀티바이트 정수를 메모리에 어떤 순서로 저장하는지를 나타내는 방식이다.
즉, 바이트 순서(Byte Order) 문제이다.
하위 바이트(Least Significant Byte, LSB)가 낮은 주소에 저장한다.
단순하게 보면 뒤집어서 저장한다고 보면 된다.
CPU가 연산 시 하위 바이트부터 처리하기 때문에 효율적이다.
사용 아키텍처: x86, 대부분 ARM
상위 바이트(Most Significant Byte, MSB)가 낮은 주소에 저장
사람이 읽을 때 숫자와 메모리 순서가 같다.
하지만 비트 순서(Bit Order)와는 다르다.
엔디안은 바이트 순서, 비트 순서는 각 바이트 안에서 0~7번 비트 순서이기 때문이다.
사용 아키텍처: 일부 네트워크 프로토콜, IBM, SPARC 등
뮤텍스와 세마포어는 임계 영역을 보호하는 존재다.
아까 데드락 발생 조건 중 하나인 '상호 배제'이다.
한 번에 하나의 스레드만 접근할 수 있는 자원을 보호하기 위한 동기화 객체로, 만약 한 스레드가 잠그면 다른 스레드는 대기하고, 잠금을 해제해야 다른 스레드가 열 수 있다.
즉, 1:1 대응이다.
뮤텍스와 비슷하지만, 동시에 접근할 수 있는 스레드 수를 제한하는 카운터이다.
여러 스레드가 접근할 수 있지만, 세마포어에 의해 자원 수에 제한을 두면 일정 수의 스레드만 접근이 가능해진다.
세마포어는 임계 영역을 보호하는 동시에, 자원 수 제한에도 사용된다.
TCP 와 UDP 는 둘 다 전송 계층(Transport Layer) 프로토콜이다.
IP(Internet Protocol) 위에서 동작하면서 데이터를 목적지까지 전달하는 역할을 한다.
하지만 데이터 전송 방식은 아래와 같이 다르다.
| 특징 | 설명 |
|---|---|
| 연결형(Connenction-oriented) | 데이터를 보내기 전에 상대방과 먼저 연결(3-way handshake) |
| 신뢰성 O | 데이터 손실 시 순서 정렬 및 재전송 |
| 흐름 제어 / 혼잡 제어 | 네트워크 상태에 맞게 속도 조절 |
| 전송 순서 보장 O | 보낸 순서대로 받게 정렬 |
| 속도 느림 | 검사, 보정 과정이 많아 전송 속도 느림 |
| 특징 | 설명 |
|---|---|
| 비연결형(Connenctionless) | 데이터를 보내고 확인 X |
| 신뢰성 X | 데이터 손실돼도 재전송 X |
| 전송 순서 보장 X | 패킷 순서 뒤바뀔 수 있음 |
| 속도 빠름 | 단순하고 오버헤드가 작아 전송 속도 빠름 |