대외활동 및 팀플/knock-on

[knock-on] 3주차 TIL

EYEN 2025. 4. 22. 02:23

 

1. 아키텍처

1) 폰 노이만 구조란?

  • CPU, 메모리, 입출력 장치가 하나의 버스로 연결된 구조.
  • 명령어와 데이터를 동일한 메모리에서 처리.
  • 단점: 명령어-데이터가 한 통로라 병목 현상 발생 (폰 노이만 병목).

2) 아키텍처란?

  • 정의: CPU나 GPU 같은 프로세서가 명령어를 해석하고 처리하는 방식 또는 설계 구조.
  • : x86, ARM, MIPS, RISC-V 등.

3) 인텔, AMD, M1, M2, M3의 CPU 차이

항목 Intel/AMD (x86/x86_64) Apple M1/M2/M3 (ARM 기반)
아키텍처 x86 / x86_64 ARM64 (AArch64)
호환성 서로 대부분 호환 (x86 기반) 인텔/AMD와는 아키텍처가 달라 직접 실행 불가
전력 효율 상대적으로 낮음 매우 높음 (모바일 기반 효율 최적화)
대표 OS Windows, Linux 등 macOS, 일부 ARM용 Linux
Rosetta 2 없음 x86 앱을 ARM mac에서 실행하는 에뮬레이터

💡 Intel 환경에서 만든 실행파일은 M1에서 바로 실행될 수 없음. M1은 ARM 기반이라 아키텍처가 다르므로 **에뮬레이션(Rosetta 2)**이 필요함. 반면 AMD는 x86 호환이라 실행 가능.

 

4) 아키텍쳐 종류 및 아키텍쳐별 차이점 (x86, x86_64, amd, arm, mips, risc-V 등..)

아키텍처 특징
x86 32비트 인텔/AMD CPU용
x86_64 64비트 확장형 (AMD64)
ARM 저전력 모바일용, 점점 데스크탑에도 사용 확대
MIPS 임베디드 시스템, 라우터 등에 사용
RISC-V 오픈소스 아키텍처, 학술·연구용으로 인기

 

2. 컴파일러

1) gcc 컴파일러가 컴파일을 진행하는 과정

gcc -E hello.c -o hello.i      # 전처리
gcc -S hello.i -o hello.s      # 컴파일
gcc -c hello.s -o hello.o      # 어셈블
gcc hello.o -o hello           # 링킹
  1. 전처리: #include, #define 처리 → .i 파일 생성
  2. 컴파일: C 코드를 어셈블리 코드로 변환 → .s
  3. 어셈블: 어셈블리 → 목적 코드로 변환 → .o
  4. 링킹: 여러 개의 .o를 묶어 실행 파일 생성 → hello

각 단계까지만 컴파일 해보며, 파일이 어떻게 변하는지 확인해 봅시다.

예제 파일

#include <stdio.h>

int main() {
    printf("Hello, world!\n");
    return 0;
}

 

전처리까지만

 

컴파일까지만

 

어셈블까지만

 

링킹까지만

 

2. 링커와 로더

프로그램이 실행되는 과정의 첫번째인 링커와 로더

이진 실행파일로 존재하는 프로그램의 실행

ram과 같은 메모리에 실행파일을 이동시키고, 해당 이진 코드를 CPU가 읽어 명령어를 실행하여 실행파일이 동작

이 과정이 시작하는 과정

 

1) 링커

1. 정적 링킹 (.a)

  • 필요한 함수들을 실행파일에 직접 포함
  • 실행 속도 빠름, 실행 시 외부 의존성 없음
  • 단점: 실행파일 크기 증가, 업데이트 반영 어려움

2. 동적 링킹 (.so, .dll)

  • 실행 시점에 외부 라이브러리 로딩
  • 실행파일 용량 작음, 라이브러리 공유 가능
  • 단점: 라이브러리 없으면 실행 실패 가능

3. 재배치(Relocation)

  • 링커는 함수/변수의 정확한 메모리 주소를 계산
  • 오브젝트 파일은 상대주소로 구성 → 링킹 시 절대주소로 매핑

4. dll / .so

  • .dll: Windows 동적 라이브러리
  • .so: Linux 공유 라이브러리 (Shared Object)

2)  로더란?

1. 로더의 역할

  • 실행파일을 RAM으로 로드
  • 메모리 맵 구성 (text, data, heap, stack)
  • 프로그램 시작 주소(Entry Point)로 CPU 점프

2. 동적 라이브러리 로딩

  • 로더는 ELF의 프로그램 헤더를 참고하여 필요한 .so를 ld-linux.so 같은 런타임 링커를 통해 로드
  • 환경변수 LD_LIBRARY_PATH에 따라 검색 경로 설정

 

3. 메모리 구조와 매핑

실행파일은 로더에 의해 ram에 로드된 후 cpu가 읽어 실행됨.

이 때 ram에 로드된 실행파일의 구조- 실행파일의 메모리 구조와 메모리의 매핑 과정

1) 실행파일의 메모리 매핑 과정

Linux에서 실행파일(ELF 형식)이 실행되면, OS 커널과 로더가 파일을 읽어 RAM에 적절한 구조로 매핑합니다.

2) 위 메모리 이미지의 각 섹션의 의미와 용도

메모리 주소 영역 설명
Text 영역 기계어 코드 (실행 명령어)
Data 영역 초기화된 전역/정적 변수
BSS 영역 초기화되지 않은 전역/정적 변수
Heap 영역 동적 메모리 (malloc, new 등)
Stack 영역 함수 호출, 지역 변수 저장
Shared Lib 영역 동적 라이브러리(.so) 매핑 영역
vdso, vsyscall 커널과 사용자 공간 사이 인터페이스

3) /proc/{PID}/maps

#ELF가 메모리에 로드된 후 구조 확인
cat /proc/$(pid)/maps

 

 

4. ELF 파일구조

리눅스 기반 OS의 기본 바이너리 파일은 ELF(Executable and Linkable Format)형식입니다.

1) ELF파일의 구성

1. ELF파일 헤더 구조체 및 용도 (readelf -h)

 

  • ELF의 파일 전반 정보 저장
  • 중요 필드:
    • e_ident: ELF magic number (0x7f 45 4c 46)
    • e_type: 실행파일, 공유 라이브러리, 목적파일 구분
    • e_machine: 아키텍처 (x86, ARM 등)
    • e_entry: 실행 시작 주소

 

 

2. ELF파일 섹션헤더 구조체 및 용도 (readelf -S)

섹션 이름 용도
.text 코드 (기계어 명령어)
.data 초기화된 전역변수
.bss 초기화되지 않은 전역변수
.rodata 읽기 전용 데이터 (문자열 리터럴 등)
.symtab 심볼 테이블
.strtab 심볼 이름 저장
.debug_* 디버깅 정보 (gdb용)

 

3. ELF파일 프로그램헤더 구조체 및 용도 

필드 의미
Type LOAD, DYNAMIC, INTERP 등
VirtAddr 메모리 매핑될 가상 주소
Offset 파일에서의 위치
Flags R, W, X 권한

 

2) ELF파일의 매핑

  1. 커널이 ELF 헤더 읽고 프로그램 헤더 테이블 확인
  2. PT_LOAD 섹션만 메모리에 적재
  3. 각각 .text, .data, .bss 등이 프로그램 헤더 기준으로 RAM에 매핑됨
  4. Entry point로 CPU가 점프하여 실행 시작

3) ELF파일의 실행법

  1. 사용자가 실행 (./hello)
  2. OS가 execve() 시스템 콜로 실행 요청
  3. 커널이 ELF 확인 및 로딩 -> 이때 매핑 프로세스 시작
  4. 필요한 .so 파일들 동적 로딩 (ld-linux.so가 수행)
  5. Entry point로 이동
  6. main()부터 코드 실행 시작