리눅스마스터1급: 리눅스 GNU컴파일러 gcc에 대하여
작성자 정보
- 관리자 작성
- 작성일
컨텐츠 정보
- 2,967 조회
- 0 추천
- 목록
본문
리눅스마스터1급: 리눅스 GNU컴파일러 gcc에 대하여
GNU 컴파일러 GCC
일반적으로 GNU C 컴파일러로 알려진 GCC는 “GNU Compiler Collection”의 약자로서 C, C++, Objective-C, Fortran, Java, Ada 및 Go에 대한 프런트엔드와 이들 언어들을 위한 라이브러리들(libstdc++, libgcj 등)을 포함한다.
GCC는 원래 GNU 운영체제를 위한 컴파일러로서 작성되었고, GNU 시스템은 사용자의 자유를 반영하는 측면에서 100% 자유 소프트웨어로서 개발되었다.
GCC의 저작권은 FSF(자유 소프트웨어 재단)에 있지만 GPL이 적용된 자유 소프트웨어이다.
그렇지만 GCC는 주로 C 또는 C++ 언어로 프로그래밍하는 데 사용되기 때문에 C 컴파일러로 알려져 있다.
게다가 리눅스 운영체제와 관련하여 커널의 대부분이 C 언어로 되어 있고, 대부분의 응용프로그램도 C 언어로 되어 있다.
따라서 자의든 타의든 리눅스 사용자들은 GCC를 주로 C 컴파일러로 사용하고 있는 것이다.
이런 점에서 이 교재는 GCC를 활용하여 C 소스코드를 컴파일하는 방법에 대해서 간략히 다루고자 한다.
GCC를 이용한 프로그래밍과 컴파일에 대한 사항들은 전문서적을 참고하기 바란다.
먼저, 명령 행 상태에서 “gcc –v”라고 명령을 내리면 gcc 버전을 보여준다.
배포판에 따라 다를 수 있지만, SULinux, RockyLinux, CentOS, Redhat 등은 다음과 같이 출력된다.
여기에서 GCC의 설치 시의 설정 내용 및 버전을 알 수 있다.
설치 시의 설정 내용에서 GCC의 설치 내역을 비교적 상세하게 알 수 있다.
|
|
|
| [root@RockyLinux01 ~]# gcc -v Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/11/lto-wrapper OFFLOAD_TARGET_NAMES=nvptx-none OFFLOAD_TARGET_DEFAULT=1 Target: x86_64-redhat-linux Configured with: ../configure --enable-bootstrap --enable-host-pie --enable-host-bind-now --enable-languages=c,c++,fortran,lto --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugs.rockylinux.org/ --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-gcc-major-version-only --with-linker-hash-style=gnu --enable-plugin --enable-initfini-array --without-isl --enable-offload-targets=nvptx-none --without-cuda-driver --enable-gnu-indirect-function --enable-cet --with-tune=generic --with-arch_64=x86-64-v2 --with-arch_32=x86-64 --build=x86_64-redhat-linux --with-build-config=bootstrap-lto --enable-link-serialization=1 Thread model: posix Supported LTO compression algorithms: zlib zstd gcc version 11.3.1 20220421 (Red Hat 11.3.1-2) (GCC) [root@RockyLinux01 ~]# |
|
|
|
|
그럼 간단한 C 프로그램을 작성하여 gcc로 컴파일을 해보도록 한다.
아래의 C 소스코드의 파일명은 superuser.c 라는 C 프로그램으로서 gcc의 예를 들기 위해 간단히 작성한 것이다.
|
|
|
| [root@RockyLinux01 test]# ls -l linux.c -rw-r--r-- 1 root root 90 6월 2 16:34 linux.c [root@RockyLinux01 test]# [root@RockyLinux01 test]# cat linux.c #include <stdio.h>
int main (void) { printf ("Welcome, www.linux.co.kr \n"); return 0; } [root@RockyLinux01 test]# |
|
|
|
|
위의 예는 linux.c라는 C 소스코드 파일의 내용을 확인한 것이다.
그리고 다음은 앞에서 확인한 linux.c 파일을 ls 명령어로 확인한 것이다.
|
|
|
| [root@RockyLinux01 test]# ls -l 합계 4 -rw-r--r-- 1 root root 90 6월 2 16:34 linux.c [root@RockyLinux01 test]# |
|
|
|
|
gcc로 컴파일을 하는 가장 기본적인 형식은 다음과 같다.
|
|
|
| 컴파일 형식 : gcc [C 소스코드 파일명] |
|
|
|
|
아래의 예는 linux.c 라는 C 소스코드 파일을 gcc로 컴파일하는 간단한 예이다.
|
|
|
| [root@RockyLinux01 test]# gcc linux.c [root@RockyLinux01 test]# [root@RockyLinux01 test]# ls -l 합계 32 -rwxr-xr-x 1 root root 25792 6월 2 16:37 a.out -rw-r--r-- 1 root root 90 6월 2 16:34 linux.c [root@RockyLinux01 test]# |
|
|
|
|
위에서 gcc로 컴파일 할 때에 아무 에러 메시지 없이 종료되었다면 컴파일이 정상적으로 종료되었다는 것을 의미하며 만약 에러가 발생하였다면 그에 대한 에러 메시지를 출력하게 된다.
그리고 생성된 실행 파일은 a.out이라는 파일이다.
즉, gcc로 컴파일 할 때에 생성된 실행파일을 지정하지 않으면 a.out 이라는 파일이 기본적으로 생성된다.
그리고 아래의 예는 생성된 실행 파일을 실행한 예이다.
즉, a.out 이라는 파일을 실행하면 컴파일되어 생성된 a.out 일는 실행 파일이 실행되어 그 결과를 출력한다.
즉, a.out 의 실행 결과 “Welcome, www.linux.co.kr”이 출력된 것이다.
|
|
|
| [root@RockyLinux01 test]# ./a.out Welcome, www.linux.co.kr [root@RockyLinux01 test]# |
|
|
|
|
그리고 gcc로 컴파일을 할 때에 생성될 실행 파일명을 지정하여 컴파일 하면 지정한 파일명으로 실행 파일이 생성된다.
아래의 예는 linux.c 소스코드 파일을 gcc로 컴파일하여 생성되는 실행 파일로 superuser를 지정한 것이다.
|
|
|
| [root@RockyLinux01 test]# ls -l 합계 4 -rw-r--r-- 1 root root 90 6월 2 16:34 linux.c [root@RockyLinux01 test]# [root@RockyLinux01 test]# gcc -o linux linux.c [root@RockyLinux01 test]# [root@RockyLinux01 test]# ls -l 합계 32 -rwxr-xr-x 1 root root 25792 6월 2 16:39 linux -rw-r--r-- 1 root root 90 6월 2 16:34 linux.c [root@RockyLinux01 test]# |
|
|
|
|
위와 같이 gcc로 컴파일 시에 생성될 실행 파일명을 지정할 때에는 –o 옵션 다음에 파일명을 지정하면 된다.
위의 예에서는 생성될 실행 파일명으로 linux 를 지정하였기 때문에 linux라는 실행 파일이 생성된 것이다.
그리고 다음은 생성된 linux 파일을 실행한 예이다.
|
|
|
| [root@RockyLinux01 test]# ./linux Welcome, www.linux.co.kr [root@RockyLinux01 test]# |
|
|
|
|
이번 예는 linux.c 라는 소스코드 파일을 gcc로 컴파일하여 그 결과로 생성된 linux라는 실행 파일을 실행한 것이다.
실행 결과 “Welcome, www.linux.co.kr”라는 문장이 출력된 것을 확인할 수 있다.
앞의 예에서는 gcc로 컴파일하는 가장 일반적이고 기본적인 방법을 설명하였다.
그럼 이제 컴파일 과정에 대해서 좀 더 자세히 알아보도록 한다.
즉, 우리가 생각하고 있는 컴파일이라는 개념을 다시 정리해 본다.
깊이 있고 세부적인 설명은 생략하고 gcc를 이용한 컴파일 과정의 이해에 필요한 부분만을 설명한다.
결론적으로, 앞의 예에서 gcc를 수행했던 컴파일은 “컴파일 작업 + 링크 작업”을 동시에 수행한 것이다.
이를 조금 더 정리하면 다음과 같다.
∙컴파일 작업 :
C 소스코드 파일을 입력으로 하여 목적 파일(object file)을 생성해 내는 과정을 의미한다.
∙링크 작업 : 컴파일 결과 생성된 목적 파일들에 필요한 함수들과 라이브러리들을 연결하여 바이너리 형태의 실행 파일을 만들어 내는 과정이다.
즉, 앞의 예에서 gcc로 linux.c 파일을 컴파일하면 컴파일 과정 후에 링크 작업을 자동으로 해 준다.
그리고 링크 작업의 결과 실행 파일이 생성된 것이다.
그렇다면 이번 예에서는 컴파일 작업과 링크 작업을 별도로 수행해 보도록 한다.
아래의 예는 gcc 컴파일러에 –c 옵션을 사용한 것으로 –c 옵션은 컴파일 작업만 수행할 뿐 링크 작업은 수행하지 말라는 옵션이다.
즉, 목적 파일을 생성하라는 옵션이다.
|
|
|
| [root@RockyLinux01 test]# gcc -c linux.c [root@RockyLinux01 test]# [root@RockyLinux01 test]# ls -l 합계 36 -rw-r--r-- 1 root root 90 6월 2 16:34 linux.c -rw-r--r-- 1 root root 1512 6월 2 16:59 linux.o [root@RockyLinux01 test]# |
|
|
|
|
위의 “gcc –c linux.c”의 결과 생성된 linux.o 파일은 컴파일 결과로 생성된 목적 파일로서 링크 작업이 수행되지 않은 단지 목적 파일일 뿐이다.
그리고 이번에는 위에서 생성된 linux.o 라는 목적 파일을 가지고 링크 작업을 수행하는 예이다.
|
|
|
| [root@RockyLinux01 test]# gcc -o linux linux.o [root@RockyLinux01 test]# [root@RockyLinux01 test]# ls -l 합계 36 -rwxr-xr-x 1 root root 25792 6월 2 17:01 linux -rw-r--r-- 1 root root 90 6월 2 16:34 linux.c -rw-r--r-- 1 root root 1512 6월 2 16:59 linux.o [root@RockyLinux01 test]# |
|
|
|
|
즉, 위의 예에서 “gcc –o linux linux.o”는 목적 파일(linux.o)을 가지고 링크 작업을 수행한 것이다.
그 결과 –o 옵션에서 지정한 실행 파일명(linux)으로 실제 실행 파일을 생성하였다.
위와 같이 gcc를 이용하여 컴파일 작업과 링크 작업을 각각 분리하여 수행하였다.
이번 예에서 말하고자 하는 것은 우리가 흔히 C 소스코드 파일을 가지고 실행 파일을 만들어내는 컴퓨터 파일이라는 것은 컴파일 작업과 링크 작업을 함께 수행한 것이라는 점이다.
다음은 gcc 컴파일러를 이용하여 두 개 이상의 C 소스코드 파일을 각각 컴파일하고 그 결과 생성된 각각의 목적 파일로 링크하는 예이다.
아래의 예를 보면 main.c와 print.c 두 개의 C 소스코드 파일이 있다.
그리고 이 두 개의 소스코드 파일의 내용을 살펴보기 위하여 cat 명령어로 main.c와 print.c를 각각 살펴보았다.
|
|
|
| [root@RockyLinux01 test]# ls -l 합계 8 -rw-r--r-- 1 root root 85 6월 2 17:04 main.c -rw-r--r-- 1 root root 63 6월 2 17:05 print.c [root@RockyLinux01 test]# [root@RockyLinux01 test]# cat main.c #include <stdio.h>
void print(void);
int main(void) { print() return() } [root@RockyLinux01 test]# [root@RockyLinux01 test]# [root@RockyLinux01 test]# cat print.c void print(void) { printf("Welcome www.superuser.co.kr \n") } [root@RockyLinux01 test]# |
|
|
|
|
위의 소스코드 파일을 보면 main.c에서 print.c 파일에 있는 print() 함수를 불러들이고 있다.
이제 위의 C 소스코드 파일 두 개를 동시에 컴파일 해보도록 한다.
즉, 아래의 예는 main.c와 print.c를 동시에 컴파일하는 예이다.
그 결과 목적 파일인 main.o 파일과 print.o 파일이 각각 생성되었다.
|
|
|
| [root@sulinux ~]# gcc –c main.c print.c [root@sulinux ~]# ls –l total 16 -rw-r--r-- 1 root root 80 Mar 25 23:53 main.c -rw-r--r-- 1 root root 704 Mar 25 23:57 main.o -rw-r--r-- 1 root root 64 Mar 25 23:54 print.c -rw-r--r-- 1 root root 808 Mar 25 23:57 print.o [root@sulinux ~]# |
|
|
|
|
그리고 위의 컴파일 결과로 생성된 main.o 파일과 print.o 파일을 가지고 링크 작업을 하도록 한다.
즉, 아래의 예는 main.o와 print.o 파일을 가지고 링크 작업을 수행하며 –o 옵션에서 지정한 superuser라는 실행 파일을 그 결과로서 생성한다.
|
|
|
| [root@sulinux ~]# gcc –o superuser main.o print.o [root@sulinux ~]# ls –l total 16 -rw-r--r-- 1 root root 80 Mar 25 23:53 main.c -rw-r--r-- 1 root root 704 Mar 25 23:57 main.o -rw-r--r-- 1 root root 64 Mar 25 23:54 print.c -rw-r--r-- 1 root root 808 Mar 25 23:57 print.o -rwxr-xr-x 1 root root 11675 Mar 25 23:59 superuser [root@sulinux ~]# |
|
|
|
|
이렇게 생성된 superuser 실행 파일을 실행한 결과 “Welcome www.superuser.co.kr”가 실행이 되었다.
또한 위의 컴파일 과정과 링크 과정을 한꺼번에 하기 위해서는 다음과 같이 한다.
뒤의 소스코드 파일들은 2개 이상 나열할 수 있다.
다만, 이 경우에는 각각의 소스코드 파일에 작은 변경을 하는 경우에도 전체를 컴파일해야 하는 단점이 있다.
따라서 위와 같이 각각의 소스코드를 별도로 컴파일하여 링크를 하는 것이 효율적일 수 있다.
|
|
|
| [root@sulinux ~]# gcc –o superuser main.c print.c |
|
|
|
|
관련자료
-
이전
-
다음