컴퓨터 아래의 언어, CPU, 운영체제(프로세스), 메모리, I/O, CPU 캐시에 대해 설명하는 책
- 프로그래밍 언어**(CPU 명령어의 추상화)**
- 인간의 추상적 표현을 CPU가 이해할 수 있는 구체적 표현으로 변환하기 위한 언어
- 구문 트리: 간결한 문장으로 코드 전체를 표현하는 트리
- 인터프리터: 표준 명령어 집합을 정의하여 CPU 명령어 실행 과정을 모방하는 프로그램
- 컴파일러: 구문 트리를 CPU의 명령어로 변환하는 도구
- 어휘 분석: 소스 코드에서 토큰을 추출하는 과정
- 파싱: 구문 트리에 따라 해석한 소스 코드의 구조
- 중간 코드: 구문 트리의 탐색 결과를 바탕으로 좀 더 다듬어진(최적화 포함) 코드
- 심볼 테이블: 외부 심볼 정보(전역 변수, 매크로 등등)를 기록하는 표
- 링커: 여러 파일을 병합하는 작업
- 참조된 외부 라이브러리 기능의 메모리 실제 주소를 연결하는 작업 담당
- 정적 링크: 컴파일 타임에 라이브러리를 실행 파일에 복제하는 방식
- 동적 링크(로더)
- 참조 라이브러리, 심벌 테이블, 메모리 재배치 정보만 포함하여 컴파일
- 메모리에 적재되어 실행될 때 실제 라이브러리를 링크하여 실행
- 운영체제, 프로세스, 스레드
- 운영체제: 프로세스, CPU, 메모리, I/O, 파일 등을 관리하는 프로그램
- 프로세스: CPU가 어떤 기계 명령어를 실행 했는지와 관련된 기타 상태 값을 저장하는 컨텍스트
- 코드 영역: 컴파일하여 생성된 기계 명령어를 저장
- 데이터 영역: 전역 변수 등이 저장
- 힙 영역: 동적 메모리 요청 시 할당되는 영역
- 상호 배제와 동기화를 이용한 공유 리소스 문제를 프로그래머가 직접 해결해야한다
- 스택 영역: 함수의 실행 시간 스택
- 여유 공간: 동적 라이브러리의 코드와 데이터가 저장되는 영역
- 스레드
- 프로세스 공유 주소 공간에 속한 여러 명령어를 동시에 실행하기 위한 흐름. 작은 프로세스
- 프로세스 스택 영역 아래에서 자신만의 스택 영역(콜 스택, 스택 프레임)을 별도로 가지고 있음
- 함수 반환 값, 매개 변수, 지역변수, 레지스터 정보를 저장
- 스레드 전용 저장소: 모든 스레드가 동일한 변수에 접근하는 것처럼 보이나 실제로는 스레드 별로 개별 변수를 가지는 저장소
- 코루틴(사용자 상태 스레드)
- 일시 중지와 재시작 기능이 있는 함수
- 자체적으로 힙 영역을 이용하여 실행 시간 스택 프레임 정보를 저장
- 동기 방식으로 논 블록킹 프로그래밍을 가능하게 한다
- 동기/비동기, 블록킹/논 블록킹
- 콜백: 함수를 인자로 하여 콜러(Caller)가 콜리(Callee)에게 특정한 동작을 수행하도록 하는 방법
- 클로저: 콜백 함수를 데이터와 묶어 하나의 변수로 취급하는 함수
- 동기/비동기 = 결과 전달 방식
- 동기 방식: 호출 스레드가 끝나기 전에 콜백 함수가 실행되어야 하는 콜백 방식
- 비동기 방식: 호출 스레드가 콜백 함수 실행에 의존하지 않는 콜백 방식
- 블로킹/논블로킹 = 스레드 동작 방식
- 블록킹: 함수 호출로 스레드 또는 프로세스가 일시 중지 되는 호출 방식
- 논 블록킹: 함수 호출이 스레드 또는 프로세스를 일시 중지하지 않는 호출 방식
- event loop(reactor pattern)
- 적은 스레드로 대량의 이벤트를 처리하기 위한 패턴
- 이벤트 대기 큐 → 이벤트 루프 → 이벤트 핸들러 의 구조로 이루어짐
- 이벤트 핸들러에서는 절대 블로킹 방식의 호출이 있어서는 안된다
- CPU
- PC(프로그램 카운터/프로그램 레지스터): CPU가 다음에 실행할 명령어의 주소를 저장하는 메모리
- 특권 단계
- 0~3 단계로 이루어져 있어 낮은 단계일수록 많은 CPU 명령어 실행이 가능
- 0으로 커널 상태, 3은 사용자 상태라고 칭한다
- 프로그램이 시스템 콜을 사용할 때에는 3 → 0 으로 특권 단계를 변경하여 실행한다
- 지역성: 프로그램이 시간, 공간에 따라 매우 규칙적으로 메모리 접근한다는 성질
- 자주 접근하는 핫 데이터와 가끔 접근하는 콜드 데이터를 분리하면 좋은 접근성을 얻을 수 있다
- 캐시
- Cpu 와 메모리 사이에는 L1, L2, L3 의 세 단계 캐시가 존재한다.
- 멀티 코어 구조에서 L3는 공용으로 사용되고, 나머지 L1,L2는 MESI 규칙으로 동기화한다
- 캐시 라인
- 지역성 원칙에 따라 데이터가 존재하는 주변 묶음 데이터를 캐시에 함께 적재하는 방식
- 멀티 스레드 환경에서의 데이터 공유가 오히려 캐시 무효화로 인해 성능을 낮출 수 있다
- 메모리 배리어(장벽)
- 특정 코어를 다른 코어가 보았을 때 순서가 일치하도록 하는 장벽
- CPU,컴파일러는 효율성을 위해 명령어 순서를 재 정렬 할 수 있고 이로 인해 코드 상의 순서와 다르게 동작 할 수 있다
- L1, L2 캐시는 코어마다 존재하며 같은 변수에 대해 서로 다른 값을 보는 상황이 존재한다
- 유형
- LoadLoad: 다음 Load 명령어가 먼저 실행되는 것을 방지
- StoreStore: 다음 Store 명령어가 먼저 실행되는 것을 방지
- LoadStore: 다음 Store 명령어가 Load 보다 먼저 실행되는 것을 방지
- StoreLoad: 다음 Load 명령어가 먼저 실행되는 것을 방지. 가장 느리고 무거운 동기 작업
- 획득과 해제 장벽
- 획득: 장벽 이전에 어떤 메모리 작업도 먼저 실행될 수 없는 구간
- 해제: 장벽 이후에 어떤 메모리 작업도 실행될 수 없는 구간
- 잠금 없는 프로그래밍: 성능보다는 스레드가 대기 없이 일을 계속 할 수 있는 것에 가치를 두고 있음
- 메모리와 가상 메모리**(메모리의 추상화)**
- 페이지 테이블(가상 메모리)
- 프로세스는 인지하지만 실제로 메모리 주소와는 별개로 동작하는 주소 구조
- 가상 메모리의 주소와 실제 메모리 주소를 맵핑하는 테이블
- 주소 공간을 동일한 크기의 조각인 페이지로 쪼개서 관리
- 메모리
- 메모리 셀: 메모리를 공간으로 쪼개어 1bit 를 저장하는 공간
- 메모리 주소: 1byte(8bit)로 구성된 메모리의 주소
- 12byte를 이용하여 구조체 또는 객체 정보를 가르킨다
- 포인터: 메모리 주소의 추상화한 변수
- 참조: 포인터를 한 번 더 추상화한 변수
- 힙 영역의 관리
- 연결 리스트와 메모리 정보를 메모리 자체에 저장하여 관리한다
- 메모리 해제 시 효율적인 병합을 위해 양방향 연결 리스트를 이용하여 관리한다
- 실제 물리 메모리는 사용되는 순간에 할당되고, 연결되어 있지 않다면 페이지 누락(page fault) 오류 가 발생한다 → 프로그램이 실패하는 것은 아니며 디스크에서 데이터를 다시 불러오려고 시도한다
- 수동으로 메모리를 초기화해야 하며 기본값이 있다고 생각해서는 안된다
- 메모리 풀: 한 번에 큰 조각을 요청하고 사용자가 할당과 해제하는 방식으로 표준 라이브러리와 운영체제를 우회하는 메모리 관리 방법
- 페이지 캐시: 디스크의 일부분을 읽어들여 디스크에서 데이터를 읽어오는 일을 최소화하기 위해 메모리를 사용
- File ( I/O 의 추상화 )
- 소켓 ( 네트워크 I/O의 추상화 ): 리눅스에서는 파일로 관리됨
- Busy Wait: 논 블록킹 호출을 사용하나 작업이 완료될 때까지 계속 반복 확인하면서 동기 방식으로 운영되는 형태