우리가 프로그램을 실행하기까지의 전체 과정을 설명하면, 먼저 hello.c
파일을 작성한 후 빌드를 시키면 다음과 같은 과정들이 차례대로 진행된다.
1. 전처리 과정
- 헤더 파일 확장:
#include
로 포함된 헤더 파일들이 실제 코드로 확장된다. - 매크로 확장:
#define
으로 정의된 매크로가 코드에 삽입된다. - 주석 제거: 소스 코드 내 모든 주석이 제거된다.
이 과정을 통해 전처리가 완료되며, 그 결과물은 .i
파일로 저장된다.
2. 컴파일 과정
- 구문 분석: 소스 코드의 문법을 검사하여 파싱한다.
- 구문 트리 생성: 구문 트리를 생성하여 코드의 구조를 분석한다.
- 중간 표현(IR): 구문 트리를 기반으로 중간 표현(Intermediate Representation)으로 변환된다.
- 어셈블리 코드 생성: 중간 표현을 기반으로 어셈블리 코드가 생성되며, 결과물은
.s
파일로 저장된다.
3. 어셈블러 과정
- 어셈블리 코드의 인스트럭션 변환: 어셈블러는 어셈블리 언어를
opcode
와operand
로 구성된 기계어 명령어로 변환한다. - 심볼 테이블 생성: 변수, 함수 등의 위치 정보를 담은 심볼 테이블이 생성된다.
- 섹션 분할: 코드, 데이터, 초기화된 변수, 초기화되지 않은 변수 등의 섹션이 분할된다.
이 과정을 통해 재배치 가능한 목적 파일인 .o
파일이 생성되며, 이 파일은 심볼 정보, 코드 영역, 초기화된 변수와 비초기화 변수에 대한 정보를 포함하고 있다.
4. 링커 과정
- 심볼 결합: 각 객체 파일의 심볼을 결합하고, 정의되지 않은 외부 참조를 해결한다.
- 동적 라이브러리 연결: 동적 라이브러리를 프로그램에 연결한다.
- 주소 재배치: 가상 메모리 주소를 할당하기 위해 페이지 번호와 오프셋을 사용하여 재배치 과정을 진행한다.
- 섹션 결합: 여러 객체 파일의 각 섹션을 결합하여 실행 파일을 생성한다.
이렇게 생성된 실행 파일은 ELF 포맷으로, 재배치 가능 목적 파일들과 크게 다르지 않지만 여러 목적 파일이 하나로 결합된 형태이다.
보완 설명 - 주소 재배치
링크 과정에서 주소 재배치는 페이지 번호와 오프셋을 통해 가상 주소가 할당되는 과정이다. 이후 프로그램 실행 중 MMU가 가상 주소를 물리 주소로 변환해, 실제 메모리에서의 위치를 관리한다. 이는 CPU와 MMU가 협력하여 가상 주소와 물리 주소 사이를 효율적으로 변환하는 작업이다.
더 자세한 과정 -> https://developsvai5096.tistory.com/94
5. 프로그램 실행
./hello
를 실행하면 리눅스의/bin
이 실행 파일을 찾아 구문 분석을 하고,execve
시스템 콜을 통해 커널에 해당 파일을 실행하라는 명령을 전달한다.- 커널은 실행 파일을 열어 실행 내용을 확인하고, 프로세스에 가상 메모리 공간을 할당한다.
- 프로세스 테이블에 등록되고, PCB(Process Control Block)가 생성되어 프로세스에 고유한 PID가 부여된다.
- 초기화 루틴이 실행되면서 프로세스의 기본 환경이 설정된다.
보완 설명 - /bin
/bin은 리눅스 및 유닉스 계열 운영체제에서 "binary" 의 약자로, 기본 시스템 명령어들이 저장된 디렉터리다. 이 디렉터리에는 시스템 부팅과 운영에 필수적인 실행 파일들이 위치하며, 일반 사용자와 시스템 관리자 모두가 사용하는 기본적인 명령어들이 여기에 존재한다. 참고로 /bin 경로는 시스템 전역에서 사용되는 명령이 존재하고, 사용자별 명령어는 "/home/username/bin" 이거나 사용자가 설치한 명령어를 전역에서 사용하고 싶다면 "/usr/local/bin" 경로를 사용한다.
6. CPU 명령어 실행
- 프로그램 카운터(PC)는
main
함수를 가리키고, CPU는 명령어 사이클(페치-디코드-실행)을 반복하며 명령어를 처리한다. - 함수가 호출될 때마다 호출 스택이 쌓이며, 반환할 때 스택에서 제거된다.
- 동적 할당이 일어날 경우, 운영체제는 필요한 메모리를 힙에서 할당해준다.
7. 메모리에서 값을 가져오는 과정
- CPU는 명령어가 가리키는 가상 주소를 MMU(Memory Management Unit)에 전달하여 물리 주소로 변환을 요청한다.
- MMU는 가상 주소를 페이지 번호와 오프셋으로 나누어 처리한다.
- 해당 페이지가 메모리에 존재하면, 그 페이지에서 값을 가져온다.
- 만약 페이지가 메모리에 존재하지 않으면, 페이지 테이블에서 해당 페이지의 주소를 찾지 못해 페이지 폴트가 발생한다.
- MMU는 페이지 폴트를 처리하기 위해 트랩을 발생시키고, CPU는 페이지 폴트 처리 루틴으로 이동한다.
- 페이지 폴트 핸들러가 호출되며, 운영체제가 DMA(Direct Memory Access)를 통해 디스크에서 해당 페이지를 메모리로 가져온다.
- 운영체제의 메모리 관리 서브시스템에 의해 페이지가 메모리에 적재된다.
- 트랩이 종료되고, CPU는 다시 중단된 명령어로 돌아가 실행을 계속한다.
보완 설명 - 페이지 폴트 처리
페이지 폴트가 발생하면, 운영체제는 DMA를 사용해 디스크에서 해당 페이지를 효율적으로 메모리로 가져온다. 이때 커널의 메모리 관리 서브시스템이 페이지를 적재하고, CPU는 중단되었던 명령어로 돌아가 다시 실행을 이어간다.
더 자세한 과정
- cpu의 명령어 사이클 -> https://developsvai5096.tistory.com/83
- 가상 메모리와 페이징 -> https://developsvai5096.tistory.com/91
'cs' 카테고리의 다른 글
[크래프톤 정글] 메모리 관리 기법 (2) | 2024.10.24 |
---|---|
[크래프톤 정글] 동적 메모리 할당기 총정리 (1) | 2024.10.16 |
[크래프톤 정글] 동적 메모리 할당(CSAP 9.9장) (0) | 2024.10.14 |
[크래프톤 정글] c프로그래밍에서의 고통된 메모리 관련 버그(CSAP 9.11장) (0) | 2024.10.14 |
[크래프톤 정글] 시그널(CSAP 8.5장) (0) | 2024.10.12 |