[TIL/크래프톤 정글] DAY 26

배재준·2025년 4월 4일

크래프톤 정글 - TIL

목록 보기
19/93
post-thumbnail

2025.04.04

TIL(TODAY I LEARN)


  • WEEK04 :
    동적 프로그래밍, 그리디 알고리즘
    CSAPP 3장. 프로그램의 기계 수준 표현 (특히 3.4, 3.7, 3.8)

  • 3.1 ~ 3.3 까지 공부


3.1 역사적 관점

어셈블리어를 배워야 하는 이유

  • 컴파일러 최적화에 대해 이해할 수 있음.
  • 반복된 컴파일 과정 속에서 나오는 어셈블리의 성능을 측정해 성능을 극대화할 수 있다.
  • 고급언어가 추상화하는 프로그램의 런타임 행동을 이해할 수 있음.
  • 고급 언어의 쓰레드 패키지를 통해 동시 프로그래밍을 할 때 공유 자원이 오가는 것을 머신 레벨에서 파악 가능.
  • 프로그램의 취약점을 방어할 수 있다.

무어(Moore)의 법칙

  • 마이크로칩에 저장할 수 있는 데이터 분량이 18-24개월 마다 두 배씩 증가한다는 법칙
  • 반도체 기술의 빠른 발전 속도를 나타냄.

3.2 프로그램의 인코딩

  • gcc, x86-64 기준으로 설명함.

gcc 명령
1. C 전처리기를 통해 #include로 명시된 파일을 코드에 삽입, #define으로 선언된 매크로 확장
2. 컴파일러가 어셈블리어로 변경 (.s 파일 생성)
3. 어셈블러가 어셈블리 코드를 바이너리 목적코드(기계어 코드의 한 유형)로 변경(.o 파일 생성)
4. 링커가 오브젝트 코드들을 실제 코드가 구현된 라이브러리의 주소와 일치시켜서 마침내 실행 가능한 프로그램을 만듦

자세한 과정은 7장에서 배운다.

3.2.1 기계수준 코드

기계수준 프로그래밍에서 특히 중요한 추상화

1. 프로그램의 포맷과 행동은 ISA(Instruction set architectue) 에 의해 결정됨.
2. 사용되는 메모리 주소는 모두 Virtual addresses 이다.

보여지는 프로세서 상태의 대부분은 C 프로그래머에게 감쳐줘 있음.

레지스터의 종류

Program Counter (PC, %rip(x86_64): 다음 명령이 실행될 메모리 주소를 가리킴

정수 register file : 16개의 64비트 레지스터, C포인터와 대응하는 주소나 정수 값을 저장.
			
Condition code register 최근에 실행된 산술 혹은 논리 명령의 상태 정보를 담고 있음.(if. while문)

벡터 레지스터: 하나 이상의 정수나 부동소수점(float) 값을 저장

머신 레벨 메모리

기계어 입장에서 메모리는 단순한 "바이트 배열".

C언어의 구조체나 포인터, 배열도 결국 이 바이트 배열 위에서 구현됨.
  • 모든 메모리 접근은 가상 주소(Virtual Address) 를 통해 이뤄짐

  • x86-64에서 주소는 64비트로 표현되지만, 실제로는 상위 16비트는 0.

    why?
    기술적 이유 + 역사적 호환성 + 하드웨어 제약 때문:

    • 모든 주소를 64비트로 사용하려면 페이지 테이블이나 메모리 관리 구조도 훨씬 더 커짐.
    • 아직 대부분의 시스템은 그 정도 메모리를 쓰지 않기 때문에 낭비를 줄이기 위해 48비트만 사용.
    • 페이지 테이블 구조도 4단계 (4-level page table) 로 설계 → 이것이 48비트와 잘 맞음.
      즉, 유효 주소 범위는 0 ~ 2⁴⁸ - 1
  • (optional) Canonical Address(정규 주소)

    x86-64는 주소의 상위 비트(16비트) 가 하위 48비트의 최상위 비트(47번째 비트) 값을 따라야 한다는 규칙.
    0x0000 7FFF FFFF FFFF 유효한 주소 (상위 16비트 = 0)
    비트 는 오른쪽에서 왼쪽으로 번호를 매김

    0x0000 = 0000 0000 0000 0000  
    0x7FFF = 0111 1111 1111 1111  
    0xFFFF = 1111 1111 1111 1111  
    0xFFFF = 1111 1111 1111 1111

3.2.2 코드 예제

multstore:
    pushq   %rbx             # ① %rbx 값을 스택에 저장
    movq    %rdx, %rbx       # ② %rbx = %rdx (rdx는 결과 저장할 주소)
    call    mult2            # ③ mult2 함수 호출 → 결과는 %rax에 저장됨
    movq    %rax, (%rbx)     # ④ *rbx = rax (rax 결과를 주소 rbx에 저장)
    popq    %rbx             # ⑤ %rbx 원래 값 복원
    ret                      # ⑥ 함수 종료

pushq,popq 레지스터 %rbx는 원래 어떤 값(주소든 숫자든)을 갖고 있었는데,
함수 안에서 잠깐 다른 용도로 써야 하니까
→ 스택에 저장(pushq)했다가
→ 함수 끝나기 전에 다시 복구(popq)하는 것

3.2.3 형식에 대한 설명

  • %r~~ 레스터 들은 범용 레지스터
  • 범용 레지스터 :x86-64 아키텍처의 "범용 레지스터(General-Purpose Registers)",(GPR)

정수, 주소, 임시값 등 여러 용도로 자유롭게 사용할 수 있는 레지스터들

  • x86-64에는 총 16개의 범용 레지스터가 존재
  • 각각은 64비트 크기(8바이트) 를 가짐
  • 사용 목적은 고정되어 있지 않지만, 관습적/규약적으로 특정 용도가 있음.

전체 범용 레지스터 목록 (x86-64 기준)

이름크기역할 (관습적으로)
%rax64비트연산 결과 저장 (Accumulator)
%rbx64비트callee-saved, 임시값 저장
%rcx64비트루프 카운터, 시프트 연산
%rdx64비트입출력, 함수 인자 전달
%rsi64비트함수 인자 (source index)
%rdi64비트함수 인자 (destination index)
%rbp64비트프레임 포인터 (stack frame 기준점)
%rsp64비트스택 포인터 (현재 스택 꼭대기 주소)
%r8 ~ %r1564비트함수 인자/임시 저장 (x86-64에서 추가된 레지스터들)

설명 참고

3.3 데이터의 형식

  • 1byte == 8 bits

  • 데이터 이동 인스트럭션에는 크게 4가지 존재
    movb, movw, movl, movq
    (바이트, 워드, 더블워드, 쿼드워드)

  • 더블워드의 l은 32bit가 long word로 간주되기 때문에 사용됨.

0개의 댓글