개발/Linux

우분투 환경에서 C언어로 배우는리눅스 프로그래밍-2. C 언어 빌드

huiyu 2017. 7. 2. 18:14

1. 컴파일과 링크와 실행 파일 만들기
  - C프로그램 소스를 컴파일러로 컴파일.
  - gcc는 '컴파일러 드라이버'라고 불리며, 소스 코드의 빌드 과정을 순차적으로 실행하는 명령이다.
  - 아래와 같은 과정 수행
   1) 프리프로세서(전처리기)에 의한 파일 포함과 매크로 처리
   2) 어셈블리 코드로 컴파일하고 어셈블 과정을 거쳐 오브젝트 파일로 변환
   3) 오브젝트 파일 결합과 라이브러리 링크

 *프리프로세서(Preprocessor)의 처리
   - 원시코드를 컴파일러에 인도하기 전에 특정한 변수를 그것에 대응하는 정의된 문자열로 치환하는 등의 일을 하는 프로그램
    1) 매크로 정의(Macro Definition) - #define 매크로명 치환문자열, 매크로 명이 기록된 곳을 모두 치환문자열로 변환하는 기능
    2) 매크로 확장(Macro Expansion) - #define SQUARE(x) (x*y), 인수를 동반하는 문자열 치환 매크로 허용.
    3) 파일 포함(File inclusion) - #include "파일명" or <파일명>, 파일명에 의해 지시되는 파일 전체가 프로그램내에 삽입
    4) 기타 프로세서 명령어 : #undef-define 해제, #ifdef, #else, #endif, #if
   출처 : 요기

* 어셈블리어(Assembly Language) : 기계어와 일대일 대응 되는 컴퓨터 프로그래밍의 저급언어
  - 컴퓨터 구조에 따라 사용하는 기계어가 달라지며, 따라서 기계어에 대응되어 만들어지는 어셈블리어도 각각 다름.
  --> 기계어는 실제 컴퓨터의 CPU가 읽어서 실행할 수 있는 0과 1로 이루어진 명령어의 조합. 이러한 각 명령어에 대해 사람이 알아보기 쉬운 니모닉(mnemonic symbol)를 정해 사람이 좀 더 쉽게 컴퓨터의 행동을 제어할 수 있도록 한 것.
    출처 : 위키

* 오브젝트 파일(Object File) : PE(Portable Executable)- windows, COFF(Common Object File Format), EFL(Executable and Linking Format)- Unix, Linux, Solaris. 등등
   - 목적코드 또는 목적파일, 컴파일러나 어셈블러가 소스코드 파일을 컴파일 또는 어셈블해서 생성하는 파일. 목적파일들은 기계어나 혹은 이에 준하는 RTL과 같은 이진 코드로 이루어져 있다.
   - 오브젝트 파일의 세가지 타입
      1) 재배지 가능한 오브젝트 파일(Relocatable object file) : 실행가능한 오브젝트 파일을 만들기 위해 컴파일타임떄 재배치 가능한 다른 오브젝트 파일들과 결합될 수 있는 것
      2) 실행 가능한 오브젝트 파일(Executable Object File) : 바이너리 코드와 데이터를 갖고 있으며 메모리로 직접 로드되어 실행
      3) 공유 오브젝트 파일(Shared Object File) : 로드타임이너 런타임 시 동적으로 메모리로 로드되고 링킹될수 있다.
   출처 : 위키,  KLDP 위키

* Linking : 여러 개로 나눠진 오브젝트 파일을 하나로 합치는 역할
   - 일반적으로 하나의 프로그램은 여러 개의 오브젝트 파일과 공용 라이브러리의 조합. 하나의 실행할 수 있는 프로그램을 완성하기 위해 링킹(Linking)이란 작업 수행. 코드를 컴파일과정과 링킹 과정을 거치면 사용자가 실행할 수 있는 실행파일이 생성.

* Loader : 완성된 실행파일을 사용자가 실행하게 되면 해당 프로그램을 메모리에 적재(Load)시키고 내용에 따라 프로그램 수행. 이러한 일을 수행하는 프로그램
  출처 :요기


2. gcc 컴파일
  - 일반적으로 명령어를 수행하는 방법은 아래와 같다.

1
$ gcc test.c -o test
cs

  * -c 옵션을 사용하면 오브젝트 파일을, -s  옵션을 사용하면 어셈블리 파일까지 생성된다.

 - 파일 분할 ( 두개의 파일 test1.c test2.c 준비)
   test1.c

1
2
3
4
5
6
#include <stdio.h>
 
void function1()
{
    printf("hello from function No.1\n");
}
cs

  test2.c

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
 
void function2()
{
    printf("Hello from function No.2\n");
}
 
int main()
{
    function1();
    function2();
    return 0;
}
cs

아래와 같이 컴파일하면 결과 생성.

1
gcc test1.c test2.c -o test
cs


그러나 분할한 각 소스는 개별적인 오브젝트 파일로 컴파일이 가능하다.

1
2
3
$ gcc test1.c test2.c -c
$ ls
test1.c  test1.o  test2.c  test2.o
cs

각각의 오브젝트 파일을 별도로 링크해서 실행파일을 만들 수도 있다.

1
$ gcc test1.o test2.o -o test
cs


파일의 부분적인 수정을 할 경우, test1.c를 아래와 같이 수정한다.

test1.c

1
2
3
4
5
6
#include <stdio.h>
 
void function1()
{
    printf("hello from function No.1!!!!\n");
}
cs

아래와 같이 test1.c의 오브젝트파일만을 생성하고 링킹시켜주면 된다.

1
2
3
4
5
$ gcc test1.c -c
$ gcc test1.o test2.o -o test
$./test
hello from function No.1!!!!!!
Hello from function No.2
cs


*프로그램 소스를 분할하는 이유
  1) 프로그램의 구조화 : 프로그램 전체를 각각의 파일로 나누면 프로그램 전체의 구성 파악이 쉬워진다.
  2) 빌드 시간 단축 : 일반적으로 컴파일에는 시간이 걸린다. 그러나 위에서 본 분할 컴파일 예와같이 소스코드를 나눠두면 일부분을 수정했을 때 그 파일만 다시 컴파일하면 된다. 새로 컴파일된오브젝트파일을 링크하면 전체를 갱신할 수 있기 때문이다.
  3) 여러 개발자에 의한 개발
  4) 유지보수 향상
  

728x90
반응형