일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- beanfactory
- Kotlin for Java Developers
- java
- 토비의스프링
- 메이븐
- AutoConfiguration
- String
- 빌드툴
- lambda
- 프록시
- springboot
- 링커
- gradle
- ORM
- FunctionalInterfaces
- 토비의스프링3.1
- springwebmvc
- exception
- ApplicationContext
- hibernate
- 클린코드
- Immutable
- DesignPattern
- JPA
- Spring
- 링킹
- IOC
- DispatcherServlet
- 컴퓨터시스템
- 자바
- Today
- Total
엔지니어로 가는 길
(C언어)프로그램이 실행되는 과정을 살펴보자 본문
컴퓨터가 C언어를 이해할 수 있을까?
요즘은 프로그램을 소스 코드로 작성한다. 소스 코드는 인간이 이해할 수 있는 언어로 작성한 코드이다. 시스템은 소스 코드를 이해할 수 없다. 시스템은 기계어만 이해할 수 있다. 따라서 누군가 소스 코드를 기계어로 번역해주어야 한다.
(C언어의 경우) 컴파일러와 어셈블러가 번역 과정을 맡는다. 먼저 컴파일러가 소스 코드를 어셈블리어로 번역해놓고 나면, 어셈블러가 어셈블리어를 기계어로 번역한다. 소스 코드를 한 번에 기계어로 번역해주지 않고 왜 굳이 두 단계로 나누었을까라는 의문이 들었다.
하지만 만들어진 순서를 생각해보면 당연한 결과인 것 같다. 프로그래밍 언어의 시작부터 사람이 이해할 수 있는 언어가 있었던 것이 아니라, 처음엔 기계어에서부터 시작했다. 기계어는 0과 1로만 이루어져 있기 때문에 너무 불편해서 어셈블리어라는 것을 만들었다.
예를 들어 두 숫자를 더하기 위해 0000000F를 이진수로 입력했어야 했는데 어셈블리어를 만들어 'add'라는 명시적인 명령어가 덧셈을 맡도록 하고, 어셈블러가 어셈블리어의 'add' 명령어를 기계어 0000000F로 번역하도록 만든 것이다. 기계어로 프로그래밍을 했던 사람들은 어셈블러와 어셈블리어의 탄생에 매우 기뻤을 것이다.
하지만 어셈블리어만으로 프로그램을 개발하기에 충분하지 않았다. 더 추상화된, 더 고급 수준의 명령과 문법이 필요했던 것이다. 그래서 C와 같이 사람이 이해하기 쉽고 작성하기 쉬운 프로그래밍 언어가 탄생한 것이다. 마치 어셈블리어를 어셈블러가 기계어로 번역해주었듯, 고급 언어도 누군가 어셈블리어로 번역해주어야 했는데 그 역할을 컴파일러가 맡았다고 생각하면 된다.
이를 정리해보면, 사람이 작성한 코드를 기계가 실행하기까지 다음과 같은 변화를 겪는다.
소스 코드 작성 → 어셈블리어로 번역 (by compiler) → 기계어로 번역 (by assembler)
컴파일 시스템
지금까지는 언어만 생각했다. 실제로 프로그램을 실행할 때는 언어만 생각해서는 안된다. 필요한 과정이 몇 개 더 있다. (예를 들어, 프로그램에서 printf라는 라이브러리 함수를 사용한다면 이 라이브러리 함수를 가져오는 과정이 필요할 것이다. 프로그램이 실행되는 과정은 사람이 작성한 언어를 기계가 이해할 수 있는 언어로 번역하는 과정 이외에 이런 추가적인 과정도 고려해야 한다.) 프로그램이 실행되는 과정에 참여하는 친구들을 컴파일 시스템이라 부르며 컴파일 시스템에는 다음의 4가지가 있다.
전처리기(전처리 단계): ‘#’으로 시작하는 디렉티브에 따라 프로그램 수정. 예를 들어 C언어에서 #include<stdio.h>는 전처리기에게 시스템 헤더파일 stdio.h를 프로그램에 삽입하라는 지시.
컴파일러(컴파일 단계): 소스 코드를 어셈블리어로 번역.
어셈블러(어셈블리 단계): 어셈블리어를 기계어로 번역한 뒤 재배치가능 목적프로그램 형태로 묶어 목적파일 생성.
링커(링크 단계): 프로그램에서 사용한 다른 목적파일을 통합. 예를 들어 표준 C 라이브러리의 printf 함수를 사용했다면 이미 컴파일된 별도의 목적파일 printf.o를 링커가 통합(아 그래서 재배치가능 목적프로그램이라고 부르는군). 그 결과를 실행가능 목적파일로 메모리에 적재.
컴파일 시스템까지 고려한다면 다음과 같은 순서를 따른다.
참고: 컴퓨터 시스템(Randal E. Bryant 저) 3판
'프로그래밍 > 전공서적' 카테고리의 다른 글
메모리의 계층구조와 캐시 그리고 지역성 (2) | 2020.11.21 |
---|---|
[Linking] 정적 링킹과 동적 링킹의 차이 (0) | 2020.01.05 |
[Linking] 링커 / 링킹이란 무엇인가 (0) | 2020.01.04 |