1장 프로그래밍 언어부터 프로그램 실행까지, 이렇게 진행된다
1.1 여러분이 프로그래밍 언어를 발명한다면? __1.1.1 창세기: CPU는 똑똑한 바보 __1.1.2 어셈블리어 등장 __1.1.3 저수준 계층의 세부 사항 대 고수준 계층의 추상화 __1.1.4 가득한 규칙: 고급 프로그래밍 언어의 시작 __1.1.5 〈인셉션〉과 재귀: 코드 본질 __1.1.6 컴퓨터가 재귀를 이해하도록 만들기 __1.1.7 우수한 번역가: 컴파일러 __1.1.8 해석형 언어의 탄생 1.2 컴파일러는 어떻게 작동하는 것일까? __1.2.1 컴파일러는 그저 일반적인 프로그램일 뿐, 대단하지 않다 __1.2.2 각각의 토큰 추출하기 __1.2.3 토큰이 표현하고자 하는 의미 __1.2.4 생성된 구문 트리에 이상은 없을까? __1.2.5 구문 트리를 기반으로 중간 코드 생성하기 __1.2.6 코드 생성 1.3 링커의 말할 수 없는 비밀 __1.3.1 링커는 이렇게 일한다 __1.3.2 심벌 해석: 수요와 공급 __1.3.3 정적 라이브러리, 동적 라이브러리, 실행 파일 __1.3.4 동적 라이브러리의 장단점 __1.3.5 재배치: 심벌의 실행 시 주소 결정하기 __1.3.6 가상 메모리와 프로그램 메모리 구조 1.4 컴퓨터 과학에서 추상화가 중요한 이유 __1.4.1 프로그래밍과 추상화 __1.4.2 시스템 설계와 추상화 1.5 요약 2장 프로그램이 실행되었지만, 뭐가 뭔지 하나도 모르겠다 2.1 운영 체제, 프로세스, 스레드의 근본 이해하기 __2.1.1 모든 것은 CPU에서 시작된다 __2.1.2 CPU에서 운영 체제까지 __2.1.3 프로세스는 매우 훌륭하지만, 아직 불편하다 __2.1.4 프로세스에서 스레드로 진화 __2.1.5 다중 스레드와 메모리 구조 __2.1.6 스레드 활용 예 __2.1.7 스레드 풀의 동작 방식 __2.1.8 스레드 풀의 스레드 수 2.2 스레드 간 공유되는 프로세스 리소스 __2.2.1 스레드 전용 리소스 __2.2.2 코드 영역: 모든 함수를 스레드에 배치하여 실행할 수 있다 __2.2.3 데이터 영역: 모든 스레드가 데이터 영역의 변수에 접근할 수 있다 __2.2.4 힙 영역: 포인터가 핵심이다 __2.2.5 스택 영역: 공유 공간 내 전용 데이터 __2.2.6 동적 링크 라이브러리와 파일 __2.2.7 스레드 전용 저장소 2.3 스레드 안전 코드는 도대체 어떻게 작성해야 할까? __2.3.1 자유와 제약 __2.3.2 스레드 안전이란 무엇일까? __2.3.3 스레드 전용 리소스와 공유 리소스 __2.3.4 스레드 전용 리소스만 사용하기 __2.3.5 스레드 전용 리소스와 함수 매개변수 __2.3.6 전역 변수 사용 __2.3.7 스레드 전용 저장소 __2.3.8 함수 반환값 __2.3.9 스레드 안전이 아닌 코드 호출하기 __2.3.10 스레드 안전 코드는 어떻게 구현할까? 2.4 프로그래머는 코루틴을 어떻게 이해해야 할까? __2.4.1 일반 함수 __2.4.2 일반 함수에서 코루틴으로 __2.4.3 직관적인 코루틴 설명 __2.4.4 함수는 그저 코루틴의 특별한 예에 불과하다 __2.4.5 코루틴의 역사 __2.4.6 코루틴은 어떻게 구현될까? 2.5 콜백 함수를 철저하게 이해한다 __2.5.1 모든 것은 다음 요구에서 시작된다 __2.5.2 콜백이 필요한 이유 __2.5.3 비동기 콜백 __2.5.4 비동기 콜백은 새로운 프로그래밍 사고방식으로 이어진다 __2.5.5 콜백 함수의 정의 __2.5.6 두 가지 콜백 유형 __2.5.7 비동기 콜백의 문제: 콜백 지옥 2.6 동기와 비동기를 철저하게 이해한다 __2.6.1 고된 프로그래머 __2.6.2 전화 통화와 이메일 보내기 __2.6.3 동기 호출 __2.6.4 비동기 호출 __2.6.5 웹 서버에서 동기와 비동기 작업 2.7 아 맞다! 블로킹과 논블로킹도 있다 __2.7.1 블로킹과 논블로킹 __2.7.2 블로킹의 핵심 문제: 입출력 __2.7.3 논블로킹과 비동기 입출력 __2.7.4 피자 주문에 비유하기 __2.7.5 동기와 블로킹 __2.7.6 비동기와 논블로킹 2.8 높은 동시성과 고성능을 갖춘 서버 구현 __2.8.1 다중 프로세스 __2.8.2 다중 스레드 __2.8.3 이벤트 순환과 이벤트 구동 __2.8.4 첫 번째 문제: 이벤트 소스와 입출력 다중화 __2.8.5 두 번째 문제: 이벤트 순환과 다중 스레드 __2.8.6 카페는 어떻게 운영되는가: 반응자 패턴 __2.8.7 이벤트 순환과 입출력 __2.8.8 비동기와 콜백 함수 __2.8.9 코루틴: 동기 방식의 비동기 프로그래밍 __2.8.10 CPU, 스레드, 코루틴 2.9 컴퓨터 시스템 여행: 데이터, 코드, 콜백, 클로저에서 컨테이너, 가상 머신까지 __2.9.1 코드, 데이터, 변수, 포인터 __2.9.2 콜백 함수와 클로저 __2.9.3 컨테이너와 가상 머신 기술 2.10 요약 3장 저수준 계층? 메모리라는 사물함에서부터 시작해 보자 3.1 메모리의 본질, 포인터와 참조 __3.1.1 메모리의 본질은 무엇일까? 사물함, 비트, 바이트, 객체 __3.1.2 메모리에서 변수로: 변수의 의미 __3.1.3 변수에서 포인터로: 포인터 이해하기 __3.1.4 포인터의 힘과 파괴력: 능력과 책임 __3.1.5 포인터에서 참조로: 메모리 주소 감추기 3.2 프로세스는 메모리 안에서 어떤 모습을 하고 있을까? __3.2.1 가상 메모리: 눈에 보이는 것이 항상 실제와 같지는 않다 __3.2.2 페이지와 페이지 테이블: 가상에서 현실로 3.3 스택 영역: 함수 호출은 어떻게 구현될까? __3.3.1 프로그래머를 위한 도우미: 함수 __3.3.2 함수 호출 활동 추적하기: 스택 __3.3.3 스택 프레임 및 스택 영역: 거시적 관점 __3.3.4 함수 점프와 반환은 어떻게 구현될까? __3.3.5 매개변수 전달과 반환값은 어떻게 구현될까? __3.3.6 지역 변수는 어디에 있을까? __3.3.7 레지스터의 저장과 복원 __3.3.8 큰 그림을 그려 보자, 우리는 지금 어디에 있을까? 3.4 힙 영역: 메모리의 동적 할당은 어떻게 구현될까? __3.4.1 힙 영역이 필요한 이유 __3.4.2 malloc 메모리 할당자 직접 구현하기 __3.4.3 주차장에서 메모리 관리까지 __3.4.4 여유 메모리 조각 관리하기 __3.4.5 메모리 할당 상태 추적하기 __3.4.6 어떻게 여유 메모리 조각을 선택할 것인가: 할당 전략 __3.4.7 메모리 할당하기 __3.4.8 메모리 해제하기 __3.4.9 여유 메모리 조각을 효율적으로 병합하기 3.5 메모리를 할당할 때 저수준 계층에서 일어나는 일 __3.5.1 천지인과 CPU 실행 상태 __3.5.2 커널 상태와 사용자 상태 __3.5.3 포털: 시스템 호출 __3.5.4 표준 라이브러리: 시스템의 차이를 감춘다 __3.5.5 힙 영역의 메모리가 부족할 때 __3.5.6 운영 체제에 메모리 요청하기: brk __3.5.7 빙산의 아래: 가상 메모리가 최종 보스다 __3.5.8 메모리 할당의 전체 이야기 3.6 고성능 서버의 메모리 풀은 어떻게 구현될까? __3.6.1 메모리 풀 대 범용 메모리 할당자 __3.6.2 메모리 풀 기술의 원리 __3.6.3 초간단 메모리 풀 구현하기 __3.6.4 약간 더 복잡한 메모리 풀 구현하기 __3.6.5 메모리 풀의 스레드 안전 문제 3.7 대표적인 메모리 관련 버그 __3.7.1 지역 변수의 포인터 반환하기 __3.7.2 포인터 연산의 잘못된 이해 __3.7.3 문제 있는 포인터 역참조하기 __3.7.4 초기화되지 않은 메모리 읽기 __3.7.5 이미 해제된 메모리 참조하기 __3.7.6 배열 첨자는 0부터 시작한다 __3.7.7 스택 넘침 __3.7.8 메모리 누수 3.8 왜 SSD는 메모리로 사용할 수 없을까? __3.8.1 메모리 읽기/쓰기와 디스크 읽기/쓰기의 차이 __3.8.2 가상 메모리의 제한 __3.8.3 SSD 사용 수명 문제 3.9 요약 4장 트랜지스터에서 CPU로, 이보다 더 중요한 것은 없다 4.1 이 작은 장난감을 CPU라고 부른다 __4.1.1 위대한 발명 __4.1.2 논리곱, 논리합, 논리부정 __4.1.3 도는 하나를 낳고, 하나는 둘을 낳고, 둘은 셋을 낳으며, 셋은 만물을 낳는다 __4.1.4 연산 능력은 어디에서 나올까? __4.1.5 신기한 기억 능력 __4.1.6 레지스터와 메모리의 탄생 __4.1.7 하드웨어 아니면 소프트웨어? 범용 장치 __4.1.8 하드웨어의 기본 기술: 기계 명령 __4.1.9 소프트웨어와 하드웨어 간 인터페이스: 명령어 집합 __4.1.10 회로에는 지휘자가 필요하다 __4.1.11 큰일을 해냈다, CPU가 탄생했다! 4.2 CPU는 유휴 상태일 때 무엇을 할까? __4.2.1 컴퓨터의 CPU 사용률은 얼마인가? __4.2.2 프로세스 관리와 스케줄링 __4.2.3 대기열 상태 확인: 더 나은 설계 __4.2.4 모든 것은 CPU로 돌아온다 __4.2.5 유휴 프로세스와 CPU의 저전력 상태 __4.2.6 무한 순환 탈출: 인터럽트 4.3 CPU는 숫자를 어떻게 인식할까? __4.3.1 숫자 0과 양의 정수 __4.3.2 부호 있는 정수 __4.3.3 양수에 음수 기호를 붙이면 바로 대응하는 음수: 부호-크기 표현 __4.3.4 부호-크기 표현의 반전: 1의 보수 __4.3.5 간단하지 않은 두 수 더하기 __4.3.6 컴퓨터 친화적 표현 방식: 2의 보수 __4.3.7 CPU는 정말 숫자를 알고 있을까? 4.4 CPU가 if 문을 만났을 때 __4.4.1 파이프라인 기술의 탄생 __4.4.2 CPU: 메가팩토리와 파이프라인 __4.4.3 if가 파이프라인을 만나면 __4.4.4 분기 예측: 가능한 한 CPU가 올바르게 추측하도록 4.5 CPU 코어 수와 스레드 수 사이의 관계는 무엇일까? __4.5.1 레시피와 코드, 볶음 요리와 스레드 __4.5.2 작업 분할과 블로킹 입출력 __4.5.3 다중 코어와 다중 스레드 4.6 CPU 진화론(상): 복잡 명령어 집합의 탄생 __4.6.1 프로그래머의 눈에 보이는 CPU __4.6.2 CPU의 능력 범위: 명령어 집합 __4.6.3 추상화: 적을수록 좋다 __4.6.4 코드도 저장 공간을 차지한다 __4.6.5 필연적인 복잡 명령어 집합의 탄생 __4.6.6 마이크로코드 설계의 문제점 4.7 CPU 진화론(중): 축소 명령어 집합의 탄생 __4.7.1 복잡함을 단순함으로 __4.7.2 축소 명령어 집합의 철학 __4.7.3 복잡 명령어 집합과 축소 명령어 집합의 차이 __4.7.4 명령어 파이프라인 __4.7.5 천하에 명성을 떨치다 4.8 CPU 진화론(하): 절체절명의 위기에서 반격 __4.8.1 이길 수 없다면 함께하라: RISC와 동일한 CISC __4.8.2 하이퍼스레딩이라는 필살기 __4.8.3 장점은 취하고 약점은 보완하다: CISC와 RISC의 통합 __4.8.4 기술이 전부는 아니다: CISC와 RISC 간 상업적 전쟁 4.9 CPU, 스택과 함수 호출, 시스템 호출, 스레드 전환, 인터럽트 처리 통달하기 __4.9.1 레지스터 __4.9.2 스택 포인터 __4.9.3 명령어 주소 레지스터 __4.9.4 상태 레지스터 __4.9.5 상황 정보 __4.9.6 중첩과 스택 __4.9.7 함수 호출과 실행 시간 스택 __4.9.8 시스템 호출과 커널 상태 스택 __4.9.9 인터럽트와 인터럽트 함수 스택 __4.9.10 스레드 전환과 커널 상태 스택 4.10 요약 5장 작은 것으로 큰 성과 이루기, 캐시 5.1 캐시, 어디에나 존재하는 것 __5.1.1 CPU와 메모리의 속도 차이 __5.1.2 도서관, 책상, 캐시 __5.1.3 공짜 점심은 없다: 캐시 갱신 __5.1.4 세상에 공짜 저녁은 없다: 다중 코어 캐시의 일관성 __5.1.5 메모리를 디스크의 캐시로 활용하기 __5.1.6 가상 메모리와 디스크 __5.1.7 CPU는 어떻게 메모리를 읽을까? __5.1.8 분산 저장 지원 5.2 어떻게 캐시 친화적인 프로그램을 작성할까? __5.2.1 프로그램 지역성의 원칙 __5.2.2 메모리 풀 사용 __5.2.3 struct 구조체 재배치 __5.2.4 핫 데이터와 콜드 데이터의 분리 __5.2.5 캐시 친화적인 데이터 구조 __5.2.6 다차원 배열 순회 5.3 다중 스레드 성능 방해자 __5.3.1 캐시와 메모리 상호 작용의 기본 단위: 캐시 라인 __5.3.2 첫 번째 성능 방해자: 캐시 튕김 문제 __5.3.3 두 번째 성능 방해자: 거짓 공유 문제 5.4 봉화희제후와 메모리 장벽 __5.4.1 명령어의 비순차적 실행: 컴파일러와 OoOE __5.4.2 캐시도 고려해야 한다 __5.4.3 네 가지 메모리 장벽 유형 __5.4.4 획득-해제 의미론 __5.4.5 C++에서 제공하는 인터페이스 __5.4.6 다른 CPU, 다른 천성 __5.4.7 누가 명령어 재정렬에 관심을 가져야 하는가: 잠금 없는 프로그래밍 __5.4.8 잠금 프로그래밍과 잠금 없는 프로그래밍 __5.4.9 명령어 재정렬에 대한 논쟁 5.5 요약 6장 입출력이 없는 컴퓨터가 있을까? 6.1 CPU는 어떻게 입출력 작업을 처리할까? __6.1.1 전문적으로 처리하기: 입출력 기계 명령어 __6.1.2 메모리 사상 입출력 __6.1.3 CPU가 키보드를 읽고 쓰는 것의 본질 __6.1.4 폴링: 계속 검사하기 __6.1.5 배달 음식 주문과 중단 처리 __6.1.6 인터럽트 구동식 입출력 __6.1.7 CPU는 어떻게 인터럽트 신호를 감지할까? __6.1.8 인터럽트 처리와 함수 호출의 차이 __6.1.9 중단된 프로그램의 실행 상태 저장과 복원 6.2 디스크가 입출력을 처리할 때 CPU가 하는 일은 무엇일까? __6.2.1 장치 제어기 __6.2.2 CPU가 직접 데이터를 복사해야 할까? __6.2.3 직접 메모리 접근 __6.2.4 전 과정 정리 __6.2.5 프로그래머에게 시사하는 것 6.3 파일을 읽을 때 프로그램에는 어떤 일이 발생할까? __6.3.1 메모리 관점에서 입출력 __6.3.2 read 함수는 어떻게 파일을 읽는 것일까? 6.4 높은 동시성의 비결: 입출력 다중화 __6.4.1 파일 서술자 __6.4.2 다중 입출력을 어떻게 효율적으로 처리하는 것일까? __6.4.3 상대방이 아닌 내가 전화하게 만들기 __6.4.4 입출력 다중화 __6.4.5 삼총사: select, poll, epoll 6.5 mmap: 메모리 읽기와 쓰기 방식으로 파일 처리하기 __6.5.1 파일과 가상 메모리 __6.5.2 마술사 운영 체제 __6.5.3 mmap 대 전통적인 read/write 함수 __6.5.4 큰 파일 처리 __6.5.5 동적 링크 라이브러리와 공유 메모리 __6.5.6 mmap 직접 조작하기 6.6 컴퓨터 시스템의 각 부분에서 얼마큼 지연이 일어날까? __6.6.1 시간 지표로 환산 __6.6.2 거리 지표로 환산 6.7 요약 |
저루 샤오펑
관심작가 알림신청Lu Xiaofeng
역김진호
관심작가 알림신청김진호의 다른 상품