336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
 
LN2440SBC 보드에 포팅한 ARToolKit!!

WM이 아닌 linux상에서 올렸다는게 나자신도 신기할 따름

하지만....

ARM은 FPU가 없기 때문에 플로팅 연산이 뷁스럽고 결과는 이러하다



그래서

이번 임베디드 공모대전 보드지원에서 모바일 과제로 전향하며 얻은

PXA270칩을 사용하고 3D연산 모듈도 달려 있는 녀석!!

사용자 삽입 이미지

바로 이녀석!!!

3D모듈이 openGL이 아닌 openGL ES를 지원하고

PXA칩 아키텍쳐를 잘 모르기에 과연 ARToolKit이 잘 돌아갈지는 미지수 이지만

기대를 걸어본다
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
openGL을 보드로 포팅시
TinyX, MESA, GLUT 최소한 이렇게 세가지를 컴파일 해줘야 한다

TinyX같은경우 검색해보면 쉽게 포팅관련 문서를 구할 수 있고
MESA의 경우도 어렵지 않기에 생략하고 GLUT만 설명하겠다

http://www.opengl.org/resources/libraries/glut/glut_downloads.php
여기서 GLUT-3.7을 다운받아 적당한곳에 압축을 푼다

참고로 GLUT포팅하면서 얻어야 할것은 공유 라이브러리 libglut.so.3.7이다

$GLUT_HOME/linux 디렉토리의 README파일을 열어보면
$GLUT_HOME/lib/glut/ 디렉토리에 $GLUT_HOME/linux/Makefile을
복사해 넣으라고 한다

복사후 $GLUT_HOME/lib/glut/Makefile을 열어보자

....
#-----------------------------------------------------------------------
# start of Imakefile
# $XConsortium: Library.tmpl,v 1.34 95/06/16 17:44:51 gildea Exp $
# $XFree86: xc/config/cf/Library.tmpl,v 3.3 1995/07/08 10:21:44 dawes Exp $

         CC = arm-linux-gcc


  CCOPTIONS = -ansi -fPIC


STD_DEFINES = -Dlinux -D__i386__ -D_POSIX_SOURCE -D_BSD_SOURCE -D_GNU_SOURCE -DX_LOCALE
CDEBUGFLAGS = -O2 -msoft-float #-m486

# Copyright (c) Mark J. Kilgard, 1994.
# Glut.cf - GLUT distribution Imakefile configuration info

EXTRA_INCLUDES = -I$(TOP)/include -I$(TOP)

GLUT_DEPLIBS = $(DEPGLUT) $(DEPXMULIB) $(DEPXLIB)
GLUT_LIBS = $(GLUT) $(GLU) $(OPENGL) $(XMULIB) $(XLIB) -lm
OPENGL = $(TOP)/../lib/libMesaGL.so
GLU = $(TOP)/../lib/libMesaGLU.so
INVENTOR = -lInventor
GLUT_DEPLIBS = $(DEPGLUT) $(DEPXMULIB) $(DEPXILIB) $(DEPXLIB)
GLUT_LIBS = $(GLUT) $(GLU) $(OPENGL) $(XMULIB) $(XILIB) $(XLIB) -lm
....

이부분이 GLUT라이브러리 컴파일 할때 사용되는 부분이다

빨간색으로 표시된 부분만 수정해주고 gl.h, glu.h경로만 잘 설정해 주고
$GLUT_HOME/lib/glut/ 경로에서 make를 하면 문제없이 컴파일이 될것이고

그 폴더에 libglut.so.3.7파일이 생성돼 있을 것이다

혹시라도 -msoft-float 옵션에 대해 알고싶으면 밑에글
gcc 컴파일 옵션.(에서 붉은색 부분)을 읽어 볼것을 권장한다.
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
gcc는 예전에는 GNU C Compiler의 약자였으나 지금은 GNU Compiler Collection의 약자로 다양한(?) 언어의 컴파일러들의 집합체이다. gcc는 한마디로 GNU에서 개발된 ANSI C 표준을 따르는 C 언어 컴파일러라고 말할 수 있다. gcc는 ANSI C 표준에 따르기는 하지만 ANSI C 표준에는 없는 여러 가지 확장 기능이 있다. 또한 gcc는 통합개발환경(IDE)을 가지고 있지 않은 command line 컴파일러이다. 옛날 Turbo-C를 주로 사용해 보셨던 분들은 tcc.exe와 비슷하다고 생각하면 된다.

(*) -v 옵션
현재 사용되고 있는 gcc의 버전을 나타내는 옵션이다. 특정 소프트웨어 패키지를 컴파일하기 위해 어느 버전 이상의 gcc를 쓰도록 션권장하는 경우가 있는데 시스템에 깔려있는 gcc의 버전을 파악하려고 할때 사용한다.

이제 직접 프로그램 하나를 컴파일하면서 설명하도록 하겠다. 아래는 hello.c의 소스이다.
#include〈stdio.h〉

int main()
{
  printf(“hello gccn”);
  return 0;
}

$ gcc -o hello hello.c
로 컴파일하면 실행파일 hello가 만들어진다.

(*) -o 파일이름 옵션
gcc의 수행 결과 파일의 이름을 지정하는 옵션이다. 위의 예제를 단순히
$ gcc hello.c
로 컴파일 하면 hello라고 하는 실행파일이 만들어 지는 것이 아니라 보통의 경우 a.out이라는 이름의 실행파일이 만들어진다.
-o hello 옵션을 줌으로써 결과물을 hello라는 이름의 파일로 만들어지게 하였다.

위의 컴파일 과정을 외부적으로 보기에는 단순히 hello.c파일이 실행파일 hello로 바뀌는 것만 보이지만 내부적으로는 다음과 같은 단계를 거쳐 컴파일이 수행된다.

 (1) C Preprocessing
 (2) C 언어 컴파일
 (3) Assemble
 (4) Linking

C Preprocessing은 C 언어 배울 때 배운 #include, #define, #ifdef 등 #으로 시작하는 여러 가지를 처리해 주는 과정이다. 그 다음 C 언어 컴파일은 C Preprocessing이 끝난 C 소스 코드를 assembly 소스코드로 변환하는 과정이다. Assemble은 그것을 다시 object 코드(기계어)로 변환하고 printf()함수가 포함되어 있는 라이브러리와 linking을 하여 실행파일이 되는 것이다.
위의 네 가지 과정을 모두 gcc라는 실행파일이 해 주는 것일까? 겉으로 보기에는 그렇게 보일지 모르지만 실제로는 gcc는 소위 말해 front-end라고 하여 껍데기에 지나지 않고 각각을 해 주는 다른 실행파일을 gcc가 부르면서 수행된다.
C Preprocessing을 전담하고 있는 실행파일은 cpp라고 하여 /usr/bin 디렉토리와 /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.95.12 디렉토리(당연히 gcc버전과 시스템에 따라 디렉토리 위치가 다르다. gcc -v로 확인하길 바란다.)에 존재한다. C 언어 컴파일은 cc1이라는 실행파일이 담당하는데 /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.95.12 디렉토리에 존재한다. Assemble과 linking은 각각 as와 ld라는 실행파일이 담당하고 /usr/bin 디렉토리에 존재하는 파일이다. (참고 : 시스템에 따라 /usr/bin이 아니라 /bin또는 /usr/local/bin 디렉토리에 존재할 수도 있다.)

gcc라는 실행파일이 하는 일을 정리해 보면 다음과 같다.
 (1) 사용자에게 옵션과 소스 파일명들의 입력을 받는다.
 (2) 소스 파일명의 확장자를 보고 어떤 단계를 처리해야 할지 결정한다.
 (3) 사용자의 옵션을 각각의 단계를 맡고 있는 실행파일의 옵션으로 변경한다.
 (4) 각각의 단계를 맡고 있는 실행파일을 호출(fork, exec)하여 단계를 수행하도록 한다.

=== C Preprocessing(cpp)
C preprocessing을 우리말로 하면 "C 언어 전처리"라고 할 수 있을 것이다. 모든 C 언어 문법책에서 정도의 차이는 있지만 C preprocessing에 대한 내용을 다루고 있다. C preprocessing에 대한 문법은 C 언어 문법의 한 부분으로 가장 기본이 되는 부분이다. C preprocessing에 관한 문법은 모두 '#'으로 시작된다. '#' 앞에는 어떠한 문자(공백 문자 포함)도 오면 안된다. 하지만 대부분의 compiler가 '#'앞에 공백 문자가 오는 경우에도 처리를 한다.

== C preprocessing이 하는 일
 (1) 입력 : C 언어 소스 코드
 (2) 출력 : 전처리가 완료된 C 언어 소스 코드
 (3) 하는 일
  - 파일 포함(file inclusion - 헤더파일 및 기타파일)
  - 매크로(macro) 치환
  - 선택적 컴파일(conditional compile)
  - 기타(#line, #error, #pragma)

cpp 는 C 언어 소스코드를 입력 받아서 C preprocessing에 관련된 문법 사항을 적절히 처리하고 결과로 C 언어 소스코드를 출력하는 프로그램이다. 입력은 작성된 C 언어 소스 코드이고, 출력으로 나온 C 언어 소스 코드에는 C preprocessing 문법에 관련된 어떠한 것도 남아있지 않는다. 즉, #define, #include 등을 찾을 수 없다. 남아 있는 정보가 있다면 file 이름과 줄수(line number)에 관한 정보이다. 그 이유는 추후의 컴파일 과정에서 에러가 날 때 그 정보를 이용해서 error를 리포팅할 수 있도록 하기 위해서이다. 그렇다면 C preprocessing을 직접 해보자.
$ gcc -E -o hello.i hello.c
결과로 hello.i라는 파일이 생긴다. 그 파일을 에디터로 열어보면 hello.c의 첫번째 줄에 있는 #include 를 처리한 결과가 보일것이다.

(*) -E 옵션
-E 옵션은 gcc의 컴파일 과정 중에서 C preprocessing까지만 처리하고 나머지 단계는 처리하지 말라는 것을 지시하는 것이다. 평소에는 별로 쓸모가 있는 옵션이 아니지만 다음과 같은 경우에 유용하게(?) 사용할 수 있다.
 (1) C 언어 소스 코드가 복잡한 선택적 컴파일을 하고 있을 때, 그 선택적 컴파일이 어떻게 일어나고 있는지 알고 싶은 경우.
 (2) preprocessing의 문제가 C 언어 에러로 나타날 경우. 다음과 같은 소스코드를 고려해 보자.

#define max(x, y) ((x) 〉(y) ? (x) : (y) /* 마지막에 ")"가 없다!!! */
int myMax(int a, int b)
{
  return max(a, b);
}

$ gcc -c cpperr.c
다음과 같은 에러가 난다.
cpperr.c: In function `myMax':
cpperr.c:4: parse error before `;'
cpperr.c파일의 4번째 줄에서 ';'가 나오기 전에 parse error가 났다. 하지만 실제 에러는 #define에 있었으므로 그것을 확인하려면 -E 옵션으로 preprocessing을 하여 살펴 보면 쉽게 알 수 있다.

(*) 참고 : parse error before x(어떤 문자) 에러는 소스코드를 parsing 할 때 발생한 에러를 말한다. parsing이란 syntax analysis(구문해석) 과정인데 쉽게 말하면 C 언어 소스코드를 읽어들여 문법적 구성요소들을 분석하는 과정이라고 할 수 있다. 보통 gcc에서 parse error라고 하면 괄호가 맞지 않았거나 아니면 ';'를 빼먹거나 했을 때 발생한다. 보통의 경우 before x라고하여 x라는 것이 나오기 전에 parse error가 발생하였음을 알려주기 때문에 그 x가 나오기 전에 있는 C 소스 코드를 잘 살펴보면 문제를 해결할 수 있다.

C preprocessing의 문법과 나머지 C 언어의 문법과는 거의 관계가 없다. 관계가 있는 부분이 있다면 정의된 macro가 C 언어의 문법 상의 char string literal에는 치환되지 않는다는 정도이다. (좀 더 쉽게 이야기 하면 큰 따옴표 안에서는 macro 치환이 되지 않는다.) 또한 c preprocessing은 architecture dependent하지 않다. 즉, i386용으로 컴파일된 cpp를 다른 architecture에서 사용해도 무방하다. 조금 문제가 있을 수 있는 부분이 있다면 gcc의 predefined macro(i386의 경우 i386용 자동으로 define된다.)가 다를 수 있다는 점 뿐이다. 따라서 cpp를 C 언어 소스코드가 아닌 다른 부분에서 사용하는 경우도 있다. 대표적으로 assembly 소스 코드에서도 사용한다. assembler가 사용하고 있는 macro 문법이 c preprocessing의 macro문법 보다는 배우기 쉽기 때문이다.

이제 preprocessing이 하는 일에 대해서 좀더 알아 보자.
== 파일 포함(file inclusion)
#include 〈stdio.h〉
#include "config.h"
위 와 같이 많은 C 언어 소스코드에서 헤더 파일을 포함한다.〈〉와 ""의 차이는 기본적인 헤더파일과, 사용자 정의 헤더파일을 구분하는 정도이다. include한 헤더 파일은 default로 특정 디렉토리를 찾게 된다. Linux 시스템의 경우 /usr/include가 default 디렉토리이다. (실제로도 그곳에 stdio.h라는 파일이 있다.) 그 다음은 현재 디렉토리를 찾게 된다.(물론〈〉와 ""에 따라서 다르다.) 파일이 없으면 당연히 에러가 발생한다. gcc의 경우 다음과 같은 에러가 발생한다.
>>소스코드파일명:line number: 헤더파일명: No such file or directory
또는(LANG=ko일때)
>>소스코드파일명:line number: 헤더파일명: 그런 파일이나 디렉토리가 없음

그렇다면 include하고 싶은 파일이 default 디렉토리와 현재 디렉토리에 없으면 어떻게 할까? 그런 문제를 해결하기 위해서 다음과 같은 옵션이 존재한다.

(*) -Idir 옵션
여 기서 dir은 디렉토리 이름이고 -I와 디렉토리 이름을 붙여 써야 한다. 그럼 include한 헤더 파일을 그 디렉토리에서도 찾아 주게 된다. 당연히 옵션을 여러 번 다른 디렉토리 이름으로 사용할 수도 있어서 헤더 파일을 찾을 디렉토리를 여러 개로 지정할 수 있다. 꼭 알아 두어야 할 옵션이다.

(*) -nostdinc
이 옵션은 default 디렉토리(standard include 디렉토리)를 찾지말라고 지시하는 옵션이다. 어플리케이션 프로그래머는 관심을 둘 필요가 없지만 kernel 프로그래머는 관심 있게 볼 수 있는 옵션이다.

== macro 치환
macro 치환에 대해서는 특별히 일어날 만한 에러는 없다. 가끔 문제가 되는 부분이 macro 정의가 한 줄을 넘어선 경우 역슬레쉬('')로 이어져야 하는데 그 소스 파일이 windows용 에디터로 편집 되었으면 parse error가 나는 경우가 있다. 그것은 개행문자(new line character)가 서로 달라서 그런 것인데...음 자세히 이야기하자면 끝이 없으므로 그냥 넘어가도록 해야한다. 또한 macro가 define된 상황에서 macro를 undef하지 않고 다시 define하면 다음과 같은 Warning이 난다.
'xxxx' redefined

macro 치환에서 대한 옵션 두개를 알아보도록 하자.

(*) -Dmacro 또는 -Dmacro=defn 옵션
gcc 의 command line에서 macro를 define할 수 있도록 하는 옵션이다. 예를 들어 -D__KERNEL__이라는 옵션을 주면 컴파일 과정 중에 있는 C 언어 소스코드의 맨 처음에 #define __KERNEL__이라고 해준 것과 같이 동작한다. 또한 -DMAXLEN=255라고하면 C 언어 소스코드의 맨 처음에 #define MAXLEN 255 라고 한 것과 동일한 결과를 준다. 선택적 컴파일을 하는 경우에 많이 이용하는 옵션으로 꼭 알아야 할 옵션이다.

(*) -Umacro 옵션
이 옵션은 #undef하고 하는 일이 똑같은데 C 언어 소스코드와는 하등의 관계가 없다. -Dmacro옵션처럼 C 언어 소스코드의 맨처음에 #undef macro를 해주는 것은 아무런 의미가 없기 때문이다.(어짜피 #define은 그 이후에 나올 것이므로...) 이 옵션의 목적은 위의 -Dmacro옵션으로 define된 macro를 다시 undef하고자 할 때 쓰는 옵션이다. 평상시에는 별로 쓸 일이 없는 옵션이지만 그냥 -Dmacro와 같이 짝으로 알아 두길 바란다.

== 선택적 컴파일
#if 시리즈와 #else, #elif, #endif 등으로 선택적 컴파일을 수행할 수 있다. 위에서 설명한 -Dmacro 옵션과 같이 쓰는 경우가 많다. 특별히 설명할 옵션은 없고 #if와 #else, #endif의 짝이 잘 맞아야 한다. 그렇지 않으면 당연히 에러가 발생한다. 단순히 parse error라고 나오는 경우는 드물고, #else, #if 에 어쩌고 하는 에러가 난다. 많이 경우의 수가 있으므로 직접 에러가 발생되도록 코딩을 해보고 확인해 보는 것이 좋다.

== 기타(#line, #error, #pragma)
#line, #error, #pragma라는 것이 있는지도 모르는 사람들이 꽤 있것이다. 자세한 것은 C 언어 문법 책을 찾아보길 바란다. #line의 경우 C 언어 소스코드 직접 쓰이는 경우는 거의 없으니까 무시하고 #pragma는 compiler에 dependent하고 gcc에서 어떤 #pragma를 사용하는지도 알 수 없으므로 그냥 넘어가도록 한다. #error의 경우 C preprocessing 과정에서 강제로 에러를 만드는 지시어이다. 선택적 컴파일 과정에서 도저히 선택되어서는 안 되는 부분에 간혹 쓰인다. 당연히 #error를 만나면 에러가 생긴다. linux kernel 소스코드에서 include 디렉토리를 뒤져 보시면 사용하는 예를 만날 수 있다.

== predefined macro
사 용자가 C 언어 소스코드에서 #define을 하지 않아도 이미 #define된 macro가 있다. ANSI C에서는 __LINE__, __FILE__, __TIME__, __DATE__, __STDC__ 다섯 가지는 이미 define되어 있는 macro로 강제적인 사항이다.(문법책 참조) gcc도 당연히 다섯 가지 macro를 predefine 한다. 뿐만 아니라 GCC의 버전에 대한 macro, architecture에 관한 사항 등을 -Dmacro 옵션 없이도 predefine 한다. -v 옵션을 실행하여 출력되는 specs파일을 열어보면 쉽게 알 수 있을 것이다.(specs파일이 어떻게 해석되는지는 나도 잘 모른다.)

== 꼭 알아두면 좋은 옵션 한가지
다음과 같이 shell 상에 입력해 보라.(hello.c는 계속되는 그 녀석이다.)
$ gcc -M hello.c
어떤 것이 출력되나? "hello.o: hello.c /usr/include/stdio.h 어쩌구저쩌구"가 출력될 것이다. 어디서 많이 본 듯한 형식 아닌가?

(*) -M 옵션
-M 옵션은 cpp에게 makefile로 만들 수 있는 rule을 만들어 달라고 하는 요청을 보내는 명령이다. file을 include하는 녀석은 cpp이므로 rule은 cpp가 만들 수 있다. 당연히 -Dmacro, -Umacro, -Idir 옵션 등을 같이 사용할 수 있고 그에 따라 결과가 달라질 수도 있다. makefile을 좀 쉽고 정확하게 만들 때 쓰는 옵션이므로 알아두면 좋다. 단점은 default 디렉토리에 있는 보통 사용자는 고칠 수도 없는 파일까지도 만들어 준다는 것이다.

=== C 언어 컴파일 과정
C 언어 컴파일 과정은 gcc라고 하는 frontend가 cc1이라는 다른 실행파일을 호출(fork & exec)하여 수행하게 된다. 사용자가 cc1이라는 실행파일을 직접 실행해야 할 하등의 이유도 없고 권장되지도 않는다. gcc의 입력으로 여러 개의 파일(C 소스 파일, object 파일 등)을 준다고 하더라도 컴파일 과정 중 앞 3단계, 즉 cpp, C 컴파일, assemble은 각각의 파일 단위로 수행된다. 서로 다른 파일의 영향을 받지 않고 진행된다. 특정 C소스 코드에서 #define된 macro가 다른 파일에는 당연히 반영되면 안된다. header 파일의 존재 의미를 거기서 찾을 수 있다.

이제 C 언어 컴파일 과정이 하는 일을 알아보도록 하자.

== C 언어 컴파일 과정이 하는 일
 (1) 입력 : C 언어 소스 코드(C preprocessing된)
 (2) 출력 : Assembly 소스 코드
 (3) 하는 일 : 컴파일(너무 간단한가?)

C preprocessing과 마찬가지로 너무 간단하다. 하지만 위의 “컴파일” 과정은 cc1 내부에서는 여러 단계로 나누어져 다음과 같은 순서로 일어난다. Parsing(syntax analysis)이라고 하여 C 언어 소스 코드를 파일로부터 읽어 들여 컴파일러(여기서는 cc1)가 이해하기 쉬운 구조로 바꾸게 된다. 그 다음에 그 구조를 컴파일러가 중간 형태 언어(Intermediate Language)라고 하는 다른 언어로 변환하고 그 중간 형태 언어에 여러가지 최적화를 수행하여 최종 assembly 소스 코드를 만들게 된다.

직접 수행해 보자. 다음과 같이 shell의 command line에 입력하라. 역시 지긋지긋한 hello.c를 이용하도록 한다.
$ gcc -S hello.c
결 과로 출력된 hello.s를 에디터로 열어서 살펴보라 (혹시 위의 command로 hello.s가 만들어 지지 않는다면 gcc -S -o hello.s hello.c로 하라.). “.”으로 시작하는 assembler directive와 “:”로 끝나는 label명, 그리고 몇 개의 assembly mnemonic이 보이나? Assembly 소스를 읽을 줄 몰라도 그게 assembly 소스 코드구나 생각하면 된다.

(*) -S 옵션
-S 옵션은 gcc의 컴파일 과정 중에서 C 언어 컴파일 과정까지만 처리하고 나머지 단계는 처리하지 말라는 것을 지시하는 것이다. 평소에는 거의 사용하지 않는 옵션이지만 다음과 같은 경우에 유용하게 사용할 수 있다.
 (1) 어셈블리 코드가 어떻게 생겼는지 보고 싶은 호기심이 발동한 경우
 (2) C calling convention을 알아보거나 stack frame이 어떻게 관리되고 있는 지 보고 싶은 경우

보 통의 경우는 아니지만 사용자가 직접 assembly 코딩을 하는 경우가 종종 있다. 아무래도 사람이 기계보다는 훨씬 똑똑하기 때문에 사람이 직접 assembly 코딩을 해서 최적화를 시도하여 소프트웨어의 수행 시간을 단축시키거나, 아니면 linux kernel이나 bootloader 등과 같이 꼭 assembly가 필요한 경우가 있다. 이때도 보통의 경우는 소프트웨어의 전 부분을 assembly 코딩하는 것이 아니라 특정 부분만 assembly 코딩을 하고 나머지는 C 언어나 다른 high-level 프로그래밍 언어를 써서 서로 연동을 하도록 한다. 그럼 C 언어에서 assembly 코딩된 함수를 호출할 때(반대의 경우도 마찬가지), 함수의 argument는 어떻게 전달되는 지, 함수의 return 값은 어떻게 돌려지는지 등을 알아볼 필요가 있다. 이렇게 argument와 return 값의 전달은 CPU architecture마다 다르고 그것을 일정한 약속(convention)에 따라서 처리해 주게 된다. 위의 hello.s를 i386용 gcc로 만들었다면 파일 중간에 xorl %eax,%eax라는 것이 보일 것이다. 자기 자신과 exclusive or를 수행하면 0(zero)이 되는데 이것이 바로 hello.c에서 return 0를 assembly 코드로 바꾼 것이다. 결국 i386 gcc에서는 %eax 레지스터에 return 값을 넣는다는 convention이 있는 것이다.(실제로는 gcc뿐 아니라 i386의 convention으로 convention을 따르는 모든 compiler가 %eax 레지스터를 이용하여 return값을 되돌린다.) argument의 경우도 test용 C 소스를 만들어서 살펴볼 수 있을 것이다. 물론 해당 CPU architecture의 assembly 소스코드를 어느 정도 읽을 수 있는 사람들에게만 해당하는 이야기 이다. stack frame도 비슷한 얘기 쯤으로 알아 두길 바란다.

== Parsing(Syntax Analysis)
위 에서 cc1이 컴파일을 수행하는 과정 중에 맨 첫 과정으로 나온 Parsing에 대해서는 좀더 언급을 한다. Parsing과정은 그야말로 구문(Syntax)을 분석(Analysis)하는 과정이다. Parsing의 과정은 파일의 선두에서 뒤쪽으로 한번 읽어 가며 수행된다. Parsing 중에 컴파일러는 구문의 에러를 찾는 일과 뒤에 수행될 과정을 위해서 C 언어 소스 코드를 내부적으로 다루기 쉬운 형태(보통은 tree형식을 이용)로 가공하는 일을 수행한다. 이 중에 구문의 에러를 찾는 과정은 (1) 괄호 열고 닫기, 세미콜론(;) 기타 등등의 문법 사항을 체크하는 것 뿐만 아니라, (2) identifier(쉽게 말해 변수나 함수 이름 들)의 type을 체크해야 한다.
 (1) 괄호 열고 닫기, 세미콜론(;) 기타 등등의 문법 사항에 문제가 생겼을 때 발생할 수 있는 에러가 전에 이야기한 parse error이다. 보통 다음과 같이 발생한다.
>> 파일명과 line number: parse error before x
당연히 에러를 없애려면 ‘x’ 앞 부분에서 괄호, 세미콜론(;) 등을 눈 빠지게 보면서 에러를 찾아 없애야 한다.
 (2) type checking
구문 에러를 살필 때 type 체크를 왜 해야 할까? 다음과 같은 예를 보자.
var3 = var1 + var2;
앞 뒤에서 parse error가 없다면 위의 C 언어 expression은 문법적인 문제가 없는가? 하지만 var1이 파일의 앞에서 다음과 같이 정의(definition)되었다면 어떻게 될까?
struct point { int x; int y; } var1;
당연히 ‘+’ 연산을 수행할 수 없다.(C 언어 문법상) 결국은 에러가 난다. 이렇게 identifier(여기서는 var1, var2, var3)들의 type을 체크하지 않고서는 구문의 에러를 모두 찾아낼 수 없다.
만 약 var1과 var3가 파일의 앞에서 int var1, var3;로 정의되어 있고 var2가 파일의 앞에 어떠한 선언(declaration)도 없이 파일의 뒤에서 int var2;로 정의되어 있다면 에러가 발생할까? 정답은 “발생한다”이다. 위에서 언급했듯이 Parsing은 파일의 선두에서 뒤쪽으로 한번만(!!!) 읽으면서 진행하기 때문이다.(모든 C 컴파일러가 그렇게 동작할지는 의심스럽지만 ANSI C 표준에서는 그렇게 되어 있는 것으로 알고 있다. Assembler는 다르다.)
그 렇다면 어떤 identifier를 사용하려면 반드시 파일 중에 사용하는 곳 전에 identifier의 선언(declaration) 또는 정의(definition)가 있어야 한다. 하지만 identifier가 함수 이름일 경우(즉 identifier뒤에 (…)가 올 경우)는 조금 다르다. C 컴파일러는 함수 이름 identifier의 경우는 int를 return한다고 가정하고 Error가 아닌 Warning만 출력한다.(Warning옵션에 따라 Warning조차 출력되지 않을 때도 있다.) 그럼 다음과 같은 소스 코드를 생각해 보자.
int var3, var2;
….
var3 = var1() + var2;
….
struct point var1(void) { … }
위 와 같은 경우도 문제가 생긴다. 맨 처음 var1이라는 함수 이름 identifier를 만났을 때 var1 함수는 int를 return한다고 가정했는데 실제로는 struct point를 return하므로 에러 또는 경고를 발생한다.
결국 권장하는 것은 모든 identifier는 사용하기 전(파일 위치상)에 선언이나 정의를 해 주는 것이다. 다음과 같은 에러 메시지들을 짧막하게 설명해 본다.
 파일명 line number: ‘x’ undeclared …. 에러 --> ‘x’라는 이름의 identifier가 선언되어 있지 않았다.
  파일명 line number: warning: implicit declaration of function `x' … 경고 --> ‘x’라는 이름의 함수가 선언되어 있지 않아 int를 return한다고 가정했다는 경고(Warning) 메시지이다.

변수나 함수의 선언(declaration)과 정의(definition)에 대해서 알지 못한다면 C 언어 문법책을 찾아서 숙지하길 바란다. 그런 내용이 없다면 그 문법책을 휴지통에 버리길 바란다.

Parsing 과정에는 위의 identifier 에러 및 경고를 비롯한 수많은 종류의 에러와 경고 등이 출력될 수 있다. 에러는 당연히 잡아야 하고 경고도 무시하지 않고 찾아서 없애는 것이 좋은 코딩 습관이라고 할 수 있다. 경고 메시지에 대한 gcc 옵션을 살펴보도록 하자.

(*) -W로 시작하는 거의 모든 옵션
이 옵션들은 어떤 상황 속에서 경고 메시지를 내거나 내지 말라고 하는 옵션이다. -W로 시작하는 가장 강력한 옵션은 -Wall 옵션으로 모든 경고 메시지를 출력하도록 한다. 보통은 -Wall 옵션을 주고 컴파일 하는 것이 좋은 코딩 습관이다.

== Parsing 이후 과정
특 별한 경우가 아닌 이상 Parsing을 정상적으로 error나 warning없이 통과한 C 소스 코드는 문법적으로 완벽하다고 봐야 한다. 물론 논리적인 버그는 있을 수 있지만 이후 linking이 되기 전까지의 과정에서 특별한 error나 warning이 나면 안된다. 그런 경우가 있다면 이제는 사용자의 잘못이 아니라 gcc의 문제로 추정해도 무방하다. Parsing이후에 assembly 소스 코드가 생성되는데, 당연히 이 과정에는 특별히 언급할 만한 error나 warning은 없다. 그냥 중요한 옵션 몇 가지만 집고 넘어가도록 하겠다.

(*) -O, -O2, -O3 등의 옵션
이 옵션은 컴파일러 최적화를 수행하라는 옵션이다. -O 뒤의 숫자가 올라갈수록 더욱 많은 종류의 최적화를 수행하게 된다. 최적화를 수행하면 당연히 코드 사이즈도 줄어 들고 속도도 빨라지게 된다. 대신 컴파일 수행 시간은 길어진다. 그리고 linux kernel을 위해 언급하고 싶은 것은 inline 함수들은 이 옵션을 주어야 제대로 inline 된다는 것이다.

(*) -g 옵션
이 옵션은 소스 레벨 debugger인 gdb를 사용하기 위해 debugging 정보(파일명, line number, 변수와 함수 이름들과 type 등)를 assembly code와 같이 생성하라는 옵션이다. 당연히 gdb를 이용하고 싶으면 주어야 한다. -g 옵션을 주지 않고 컴파일한 프로그램을 gdb로 디버깅하면 C 소스 레벨이 아닌 assembly 레벨 디버깅이 된다. 즉 C 소스 코드에 존재하는 변수 이름, line number 등이 없는 상황에서 디버깅을 해야 한다. 또한 -g 옵션을 -O 옵션과 같이 사용할 수도 있다. 단 그런 경우 최적화 결과, C 소스 코드에 존재하는 심볼(symbol; 쉽게 말해 함수와 변수 이름)중에 없어지는 것들이 발생한다.

(*) 여기서 잠깐
identifier 와 symbol이 모두 “쉽게 말해 함수와 변수 이름”이라고 했는데 어떻게 차이가 날까? 엄밀히 말하면 차이가 조금 있다. symbol이 바로 “쉽게 말해 함수와 변수 이름”이며 각 symbol은 특정 type과 연계되어 있다. 하지만 identifier는 그냥 “이름” 또는 “인식어”일 뿐이다. 예를 들어 struct point { int x; int y; };라는 것이 있을 때 point는 symbol은 아니지만 identifier이다. 보통 identifier라는 말은 parsing에서만 쓰인다는 정도만 알아두면 된다.

(*) -p 옵션과 -pg 옵션
profiling 을 아는가? 수행시간이 매우 중요한 프로그램(real time 프로그램이라고 해도 무방할 듯)을 작성할 때는 프로그램의 수행 시간을 함수 단위로 알아야 할 필요가 있는 경우가 많다. 프로그램의 수행 시간을 함수 단위나 더 작은 단위로 알아보는 과정을 profiling이라고 하는데, profiling은 프로그램 최적화에 있어서 중요한 기능을 담당한다. 대부분의 개발 툴이 지원하고 Visual C++에도 존재한다. 옛날 turbo C에는 있었나? 아무튼 gcc도 역시 profiling을 지원한다. -p 옵션 또는 -pg 옵션을 주면 프로그램의 수행 결과를 특정 파일에 저장하는 코드를 생성해 주게 된다. 그 특정 파일을 적당한 툴(prof또는 gprof 등)로 분석하면 profiling 결과를 알 수 있게 해 준다. 당연히 linux kernel 등에서는 사용할 수 없다.(이유는 특정 파일에 저장이 안되므로…) 초보자들은 이런 옵션도 존재하고 profiling을 할 수 있다는 정도만 알아 두면 좋을 듯 싶다. 나중에 필요하면 좀 더 공부해서 사용하면 된다.

(*) 기타 옵션(-m과 -f시리즈)
중 요한 옵션들이기는 하지만 초보자가 알아둘 필요가 없는 옵션 중에 f또는 m으로 시작하는 옵션들이 있다. f로 시작되는 옵션은 여러 가지 최적화와 assembly 코드 생성에 영향을 주는 architecture independent한 옵션이다.(assembly 코드 생성이 architecture dependent 이므로 정확히 말하면 f로 시작되는 옵션이 architecture independent라고 할 수는 없다.) m으로 시작되는 옵션은 보통 architecture dependent 하며 주로 CPU의 종류를 결정하는 옵션으로 assembly 코드 생성에 영향을 주게 된다. 하지만 대부분은 초보자는 그런 것이 있다는 정도만 알아두면 되고 특별히 신경 쓸 필요는 없다고 생각된다. m으로 시작되는 옵션 중에 -msoft-float옵션이 있다.(물론 특정 architecture에만 존재하는 옵션이다.) -msoft-float 옵션은 CPU에 FPU(floating point unit)가 없고, kernel에서 floating-point emulation을 해 주지 않을 때 C 소스 코드 상에 있는 모든 floating-point 연산을 특정 함수 호출로 대신 처리하도록 assembly 코드를 생성하라고 지시하는 옵션이다. 이 옵션을 주고 라이브러리를 linking시키면 FPU가 없는 CPU에서도 floating 연산을 할 수 있다.(대신 엄청 느리다. 어찌보면 kernel floating-point emulation보다는 빠를 것 같은데 확실하지는 않다.)

=== Assemble 과정
Assemble 과정은 앞선 과정과 동일하게 gcc라는 frontend가 as라는 실행 파일을 호출하여 수행된다. 그런데 as는 cpp와 cc1과는 달리 gcc 패키지 안에 존재하는 것이 아니라 별도의 binutils라고 하는 패키지에 존재한다. binutils 패키지 안에는 as를 비롯해 linking을 수행하는 ld, library 파일을 만드는 ar, object 파일을 보거나 복사할 수 있는 objdump, objcopy 등 여러 가지 툴이 들어 있다.

이제 Assemble 과정이 하는 일을 알아보도록 하자.

== Assemble 과정이 하는 일
 (1) 입력 : Assembly 소스 코드
 (2) 출력 : relocatable object 코드
 (3) 하는 일 : assemble(너무 간단한가?)

입 력은 당연히 C 언어 컴파일 과정을 거치면 나오는 Assembly 소스 코드이다. Assemble 과정을 거치면 소위 기계어(machine language)라는 결과가 relocatable object 형식으로 나온다. “relocatable”이라는 말이 어려우면 그냥 object 코드라고 해 두자. 이제 직접 수행해자. shell의 command line에 다음과 같이 입력하면 된다.
$ gcc -c hello.c
결 과는 hello.o라고 하는 파일이 나온다. hello.o는 binary형식의 파일이니깐 editor로 열어봐야 정보를 얻기 힘들다. 당연히 위의 예는 assemble 과정만 수행한 것이 아니라 C preprocessing 과정, C 언어 컴파일 과정, Assemble 과정을 수행했다. Assemble 과정만 수행하고 싶으면 다음과 같이 입력하면 된다.
$ gcc -c hello.s
역시 hello.o가 생긴다. hello.s는 C 언어 컴파일 과정에서 -S 옵션으로 만들었던 그 파일이다. 별로 관심이 안 생기면 as를 직접 수행할 수도 있다. 다음과 같다.
$ as -o hello.o hello.s
역시 hello.o가 생긴다.

(*) -c 옵션
많 이 쓰는 옵션이다. Assemble 과정까지의 과정만 수행하고 linking 과정을 수행하지 말라는 옵션이다. 여러 개의 C 소스 파일로 이루어진 프로그램을 컴파일 할 때 모든 소스 파일을 assemble 과정까지 수행하고 맨 마지막에 linking한다. 보통은 Makefile을 많이 이용하는데 그 때 많이 쓰이는 옵션이다.

Assemble 과정에서는 더 이상 기억해야 하는 옵션도 없고 이게 끝이다. C 언어 컴파일 과정에서 말한 바대로 C 언어 컴파일 과정이 끝난 C 소스 파일은 문법적으로 완전하다고 볼 수 있으므로 assemble 과정에서 Error나 Warning 나는 경우는 없다. 만약 Error나 Warning이 나는 경우가 있다면 gcc의 inline assemble을 이용했을 때, 그 inline assemble 소스 코드에 있는 문제 때문에 생길 수 있다. 안타깝지만 error나 warning 메시지가 나온 다면 C 소스 파일과 line number 정보는 없다. 잘 알아서 처리하는 수 밖에 다른 방법은 없는 것 같다. inline assemble 같은 것을 사용하지 않았는데도 error나 warning이 난다면 gcc의 버그라고 생각해도 무방하다.

== relocatable object 코드 파일 내용
어 떤 정보가 object 파일 안에 들어있을까? 당연히 code와 data가 들어 있다. C 컴파일 과정에서 C 언어 함수 안에 있는 내용들이 assembly mnemonic 들로 바뀌었고 그것이 assemble되어 기계어(machine language)가 되었을 것이다. 그 부분이 code를 이룬다. C 언어 소스 코드에 있는 나머지는 전역 변수(external variable)와 정적 변수(static variable)들이 data를 이룰 것이다. 또한 문자열 상수를 비롯한 상수도 data에 들어 있다. 또한 프로그램 수행에 쓰이지는 않고 단순한 정보로서 들어 있는 data들도 있다. 예를 들어 -g 옵션을 주고 컴파일 하면 프로그램의 디버깅 정보(변수, 함수 이름, C 소스 파일이름, line number 등)가 data에 속한다고 볼 수 있다. 그런데 code와 data가 무질서하게 섞여 있는 것은 아니고 section이라고 불리우는 단위로 서로 구분되어 저장되어 있다. Code는 text section에 들어 있고, data는 성격에 따라 data section, bss section, rodata section 등에 나누어져 저장되어 있다.(text, data, bss, rodata 등의 section 이름은 그냥 관습적인 것이다.) 아무튼 section 이야기는 이 정도만 우선 알아두면 될 듯 싶다.

== Symbol 이야기
relocatable object code안에 code와 data가 들어 있다고 했는데, 아주 중요한 것을 빠뜨렸다. 이 이야기는 linking 과정을 이해하기 위해 꼭 필요한 부분이므로 반드시 읽어야 할 것이다. 우선 Symbol이 무엇인지 알 것이다. C 언어 컴파일 과정에서 identifier와 함께 설명했는데 잠시 다시 말씀하자면 Symbol은 함수와 변수 이름이다. 변수 중에 특히 관심두어야 할 것 들은 자동 변수(?,auto variable)들이 아닌 전역 변수(external variable)와 정적 변수(static variable)이다. 자동 변수는 함수의 stack frame에 존재하는 변수이기 때문에 현재 stack pointer(sp, 보통의 CPU의 register중에 하나)에 대한 offset으로 표현된다. 즉 현재 함수에서 자동 변수(auto variable)를 access(read/write)하고 싶으면 sp+상수의 어드레스를 access하면 된다. 하지만 전역 변수와 정적 변수는 그냥 32bit(32bit CPU기준) 어드레스를 읽어야 한다. stack pointer랑은 전혀 관계 없다. 아무튼 여기서 관심을 두는 Symbol은 함수, 전역 변수와 정적 변수의 이름이라고 할 수 있다.
이제 생각해 볼 것은 C 언어 소스 파일을 C preprocessing, C 언어 컴파일, assemble 과정을 거치면 완전한 기계어로 바꿀 수 있느냐 하는 점이다. 완전히 기계어로 바꿀 수 있을까? C 언어 소스 파일 하나로 이루어지는 프로그램이라면 완전히 기계어로 바꾸는 것이 가능하겠지만 일반적으로는 불가능 하다. 다음과 같은 예제를 살펴보자.
int func3(void); /* func3 선언 */
extern int mydata; /* mydata 선언 */

int func2(void) /* func2 정의 */
{
….
}

int func1(void) /* func1 정의 */
{
int i;
…..
func2();
…..
func3();
….
i= mydata+3;
…..
}
-- end of test1.c
-- start of test2.c
int mydata = 3; /* mydata 정의 */
int func3(void) /* func3 정의 */
{
…..
}

위 의 예제를 컴파일 한다고 생각해보자. test1.c에서 func1()의 내용을 기계어로 바꾸고 싶은데 func2()를 호출하는 시점에서는 별로 문제가 안된다. func2()는 같은 소스 코드 내에 존재하고 func2()를 호출하는 instruction과 func2()의 실제 위치(어드레스)의 차이를 계산해 낼 수 있으므로 상대 어드레스를 이용하는 함수 호출 instruction으로 완전히 기계어로 바꿀 수 있다. 그런데 문제는 func3()를 호출할 때는 func3()의 실제 위치(address)를 계산할 수 없다는 문제점이 있다. 당연히 동일한 파일에 존재하는 함수가 아니므로 그 함수가 존재하게 될 어드레스를 계산할 수 없다. 어드레스를 모르는데 함수 호출 instruction을 완전히 만들 수 있을까? 만들 수 없다. 당연히 전역 변수 mydata를 access하는 부분도 마찬가지로 mydata의 어드레스를 모르므로 완전히 instruction으로 바꿀 수 없다. 그럼 어떻게 해야 될까?
그때 assembler는 그냥 함수 어드레스 없는 함수 호출 instruction을 기계어로 바꾸어 놓는다. 그런 다음에 그 instruction에 “func3()를 호출한다”라는 표지를 붙여 놓는다. 그럼 그 후의 과정(linking)에서 func3()의 address를 계산했을 때 그 빈 공간을 채워 넣게 된다. mydata와 같은 전역 변수도 마찬가지로 동작한다. test1.c을 컴파일할 때는 “func3()”, “mydata” 라는 표지를 사용해야 한다. 그럼 test2.c를 컴파일 할 때는 무엇이 필요할까? 상식적으로 생각하면 “func3()”, “mydata”가 여기 있다라는 정보를 가지고 있어야한다.
정리하면 object 파일 안에는 그 object 파일에 들어있는 symbol들(test1.o에서는 func1과 func2, test2.o에서는 func3와 mydata)에 대한 정보가 들어있고, 그 object 파일이 reference하고 있는 symbol들(test1.o에서 func3와 mydata 사용)에 대한 정보가 들어 있다.

== Relocatable의 의미
위 에서 object 코드라고 하지 않고 relocatable object 코드라고 지칭했는데 relocatable이 뜻하는 것을 잠시 집고 넘어 가자. Relocatable을 사전에서 찾아보면 “재배치가 가능한” 정도의 뜻이다. “재배치가 가능한” 이라는 의미는 상당히 모호하다. 좀 더 구체적으로 말하자면 위에서 설명된 symbol들의 절대 어드레스가 정해지지 않았다는 뜻이다. 즉 test1.c의 func1()이 절대 어드레스 0x80000000에 존재해야 한다라고 정해지지 않고 어떤 절대 어드레스에 존재해도 관계 없다는 뜻이다. 그런데 이 말과 헷갈리는 말이 한가지 더 있는데 그것은 position independent code이다. C 언어 컴파일 과정에서 설명한 옵션중에 -f 시리즈가 있었다. 그 중에 -fpic라는 position independent code를 만들라고 강제하는 옵션이 있다. position independent code도 역시 절대 어드레스상에 어느 위치에 있어도 무방한 code를 지칭한다. 하지만 두 가지는 분명 차이가 있는데, 그냥 넘어가기로 하자. 쉽게 relocatable은 절대 어드레스가 결정되지 않았다는 뜻, 그러나 position independent code와는 다른 말이다.

=== Linking 과정
Linking 과정은 ld라고 하는 실행파일이 담당하고 있다. Assemble을 담당하는 as와 마찬가지로 binutils 패키지의 일부분이다. 보통 어플리케이션을 컴파일하는 경우에는 gcc(실행파일)를 이용하여 ld를 호출하나, 특별한 경우에 있어서는 ld를 직접 수행하여 linking을 하는 경우가 종종 있다.

== Linking 과정이 하는 일
 (1) 입력 : 하나 이상의 relocatable object 코드 와 library
 (2) 출력 : 실행파일(executable) 또는 relocatable object 코드
 (3) 하는 일 : symbol reference resolving & location
Linking 과정은 하나 또는 그 이상의 object 파일과 그에 따른 library를 입력으로 받는다. 출력은 보통의 경우는 실행파일(executable file)이지만, 경우에 따라서 object 파일을 생성하게 할 수도 있다. 여러 개의 object 파일을 합쳐서 하나의 object 파일로 만드는 과정을 partial linking이라고 부르기도 한다. Linking 과정이 하는 일은 symbol reference resolving하고 location이라고 했는데, 저도 정확한 단어를 적은 것인지 의심스럽다. 정확한 용어를 사용한다면 좋겠지만 그렇지 못하더라도 내용을 정확히 이해하는 것이 중요하니깐 내용에 대해서 살펴보도록 하겠다.

== symbol reference resolving
어 떤 C 소스 파일에서 다른 파일에 있는 함수와 전역 변수(symbol)에 대한 참조(reference)를 하고 있다면 assemble 과정에서 완전한 기계어로 바꿀 수 없다.(실제로는 같은 소스 파일에 있는 전역 변수를 참조하는 것도 보통의 경우, 완전한 기계어로 바꿀 수 없다.) 그 이유는 당연히 assemble 까지의 과정은 단일 파일에 대해서만 진행되고, 다른 파일에 있는 해당 함수와 전역 변수의 address가 상대적이든 절대적이든 결정될 수 없기 때문이다. 따라서 완전히 기계어로 바꿀 수 없는 부분은 그대로 “공란”으로 남겨두고 표시만 해 두게 된다.
Linking 과정에서 그 “공란”을 채워 넣게 된다. 그 과정을 보통 “resolve한다”라고 말한다. 어떻게 할까? 당연히 실행 파일을 이루는 모든 object 파일을 입력으로 받기 때문에 object 파일들을 차곡 차곡 쌓아 나가면(아래 location 참조) object 파일 안에 있는 모든 symbol(함수나 전역 변수 이름)의 address를 상대적이든 절대적이든 계산할 수 있다. 이제 각 symbol의 address가 계산되었으므로 표시가 남아 있는 “공란”에 해당하는 symbol의 address를 잘 넣어주면 된다.
linking 과정에서 나올 수 있는 에러는 대부분 여기에서 발생한다. 표시가 남아 있는 “공란”을 채울 수 없는 경우가 있다. 크게 두 가지로 나누어지는데, 우선 reference하고 있는 symbol을 찾을 수 없는 경우와 reference하고 있는 symbol의 정의가 여러 군데에 있는 경우이다.

>> object파일명: In function ‘func’:
>> object파일명: undefined reference to ‘symbolname’
위 의 에러 메시지는 함수 func 안에서 사용되고 있는 symbolname이란 이름의 symbol이 어디에도 정의되지 않아서 “공란”을 채울 수 없다는 뜻이다. 당연히 symbolname을 잘못 입력하였던지 아니면 그 symbol이 속해있는 object 파일이나 library와 linking되지 않았기 때문이다.

>> object파일명1: multiple definition of ‘symbolname’
>> object파일명2: first defined here
위 의 에러 메시지는 symbolname이란 이름의 symbol이 여러 번 정의되고 있다는 뜻이다. object파일1에서 정의가 있는데 이미 object파일2에서 정의된 symbol이므로 그 symbol을 reference하고 있는 곳에서 정확하게 “공란”을 채울 수 없다는 뜻이다. 당연히 두 symbol중에 하나는 없애거나 static으로 바꾸거나 해야 해결될 것이다.

== location(용어 정확하지 않을 수 있음)
이 전 까지 object 코드를 모두 relocatable이라고 표현했다. 아직 절대 address가 결정되지 않았다는 의미로 사용된다.(position independent code와는 다른 의미) object 코드의 절대 address를 결정하는 과정이 “location”이다. Symbol reference resolving 과정에서 입력으로 받은 모든 object 파일들을 차곡 차곡 쌓아 나간다고 했다. 그런데 object 파일이 무슨 벽돌도 아닌데 차곡 차곡 쌓는 다는 것이 말이 되나? 여기서 쌓는 다는 말을 이해하기 위해서 다음과 같은 그림(?)을 살펴 보도록 하자.

많은 object code들
----------------- address(0xAAAAAAAA+0x5000)
test2.o(size 0x3000)
----------------- address(0xAAAAAAAA+0x2000)
test1.o(size 0x2000)
----------------- address(0xAAAAAAAA)

절 대 address 0xAAAAAAAA에 test1.o의 내용을 가져다 놓는다. test1.o의 크기(파일 크기와는 의미가 조금 다르지만 그냥 무시하고 파일 크기라고 생각하기 바람)가 0x2000이므로 다음에 test2.o를 쌓을 수 있는 address는 0xAAAAAAAA+0x2000가 된다. 그곳에 다시 test2.o를 쌓고 또 test2.o의 크기를 보고 새로운 address 계산하고 또 object 코드 쌓고, 계속 반복이다. 이렇게 쌓을 때 초기 절대 address 0xAAAAAAAA가 무슨 값을 가지게 되면 모든 object 파일에 있는 symbol의 절대 address도 계산해 나갈 수 있을 것이다. 그걸로 symbol reference를 resolve하게 된다. 그 초기 절대 address 0xAAAAAAAA의 값을 정하는 것을 location이라고 한다. 그럼 왜 절대 address를 결정해야 할까? 꼭 그래야 할 필요는 없지만 CPU의 instruction이 대부분의 경우 절대 address를 필요로 하는 경우가 많기 때문이라고 할 수 있다.
(주의) object 를 쌓는 것은 위의 예처럼 단순하지는 않다. 실제로는 object 전체를 쌓지 않고 object안에 있는 section별로 쌓게 된다.

그럼 이제 직접 수행해 보자.
$ gcc -o hello hello.o
object 파일이 하나라서 너무 단순하다고 생각하는가? 물론 hello.o 하나만 command line에 나타나지만 실제로는 조금 많은 object 파일이 linking되고 있다. (아래에서 좀더 자세하게 설명한다.) 지겹지만 hello를 실행해 보라. 제대로 동작하는가? 제대로 동작한다면 그 사이 어떤 일이 벌어졌을까? 그 사이에 벌어진 일을 간단히 적어보면 다음과 같다. shell이 fork() 시스템콜을 호출하고 자식 process는 exec() 시스템콜을 통해 hello라는 파일 이름을 kernel에 넘긴다. kernel에서는 hello파일을 보고 linking할 때 location된 address(여기서는 absolute virtual address 이다.)상의 메모리로 hello 파일을 복사하고 PC(program counter)값을 바꾸면 수행되기 시작한다.
(주의) 실제로 위의 hello가 수행되는 과정은 많은 생략과 누락이 있다. 실제로는 hello 파일을 완전히 메모리로 복사하는 것도 아니고, dynamic linking & loading 등의 개념이 완전히 빠져 있지만 그냥 이해하기 쉽게 하기 위해서 간단하게 적어 본 것이다.

= library
hello.o 를 linking하여 hello라고 하는 실행파일을 만드는데 command line에서는 아무것도 없지만 library가 같이 linking되고 있다. 그것은 지극히 당연하다. hello.c의 main함수에서 printf함수를 호출(linking이니깐 참조 혹은 reference라고 해야 좋겠다.)하고 있는데 printf함수 자체는 소스 중에 그 어디에도 없다.(물론 stdio.h에 printf함수의 선언은 있습니다만 정의는 어디에도 없다.) 잘 알다시피 printf함수는 C standard library 안에 있는 함수이다. C standard library가 같이 linking되었기 때문에 제대로 동작하는 hello 실행파일이 생긴 것이다.
library라는 것은 아주 간단한 것이다. relocatable object 파일들을 모아 놓은 파일이다. 소스로 제공할 수도 있으나, 그러면 매번 cpp, c 컴파일, assemble 과정을 거쳐야 하므로 컴파일 시간이 매우 증가하게 된다. 그래서 그냥 relocatable object 파일로 제공하는 것이 컴파일 시간 단축을 위해서 좋다. 그런데 필요한 relocatable object 파일이 너무 많으면 귀찮으니까 그것을 묶어서 저장해 놓은 녀석이 바로 library라고 할 수 있다.
Linux를 비롯한 unix 계열에서는 대부분의 library 파일의 이름이 lib로 시작된다. 확장자는 두 가지가 있는데, 하나는 .a이고 또 하나는 .so입니다.(뒤에 library 버전 번호가 붙는 경우가 많다.) .a로 끝나는 library를 보통 archive형식의 library라고 말하며 .so로 끝나는 library를 보통 shared object라고 부른다. /lib 디렉토리와 /usr/lib 디렉토리에 가면 많이 볼 수 있다.
archive library 안에 있는 symbol를 reference하게 되면 library중에 해당 부분(object 파일 단위)을 실행 파일 안에 포함시켜 linking을 수행한다. 즉, 해당 object 파일을 가지고 linking을 수행하는 것과 동일한 결과를 가진다. 보통 이런 linking을 static linking이라고 부른다.
그 런데 시스템 전체에 현재 수행되고 있는 실행파일(실행파일이 수행되고 있는 하나의 단위를 process라고 한다.)들에서 printf함수를 사용하고 있는 녀석들이 매우 많으므로 그것이 모두 실행 파일에 포함되어 있다면 그것은 심각한 메모리 낭비를 가져온다는 문제점을 가지고 있다. 그래서 생각해 낸 것이 dynamic linking이라는 개념이다. 예를 들어 실행파일이 printf함수를 사용한다면 실행파일이 메모리로 loading될 때 printf가 포함되어 있는 library가 메모리 상에 있는 지 검사를 해 보고 있으면 reference resolving만 수행하고, 아니라면 새로 loading과 reference resolving을 하게 된다. 그렇게 되면 printf가 포함되어 있는 library는 메모리 상에 딱 하나만 loading되면 되고 메모리 낭비를 막을 수 있다. 그런 일을 할 수 있도록 도입된 것이 shared object 이다. MS Windows쪽의 프로그래밍을 하시는 사람이라면 DLL과 동일한 개념이라고 보면 된다.
그런 shared object를 이용하여 dynamic linking을 하면 실행파일의 크기가 줄어든다. 반면에 당연히 실행파일이 메모리에 loading될 때는 reference resolving을 위해서 CPU의 연산력을 사용한다. 하지만 MS Windows의 DLL과는 달리 shared object 파일과 static linking을 할 수도 있다.(반대로 archive library를 이용하여 dynamic linking을 수행할 수는 없다.)

(*) -static 옵션
dynamic linking을 지원하고 있는 시스템에서 dynamic linking을 수행하지 않고 static linking을 수행하라는 옵션이다. dynamic linking을 지원하고 있는 시스템에서는 dynamic linking이 default 이다.

직접 수행해 보자.
$ gcc -o hello_static -static hello.o
실행파일 hello, hello_static 을 수행하면 결과는 똑같다. 파일의 크기를 비교해 보면 차이가 난다는 것을 알 수 있을 것이다.

/lib, /usr/lib에는 엄청 많은 library 파일들이 존재한다. 그럼 linker가 찾아야 하는 symbol을 모든 library 파일에 대해서 검사를 하는 것일까? CPU하고 HDD가 워낙 빠르면 그래도 무방하겠지만, 그렇게 하지 않는다.(“사용자가 쉽게 할 수 있는 일을 컴퓨터에게 시키지 말라.”라는 컴퓨터 사용 원칙이다.) 우선 gcc는 기본적인 library만 같이 linking을 하게 되어 있다. 나머지 library는 사용자의 요구가 있을 때만 같이 linking을 시도하도록 되어 있다. 그럼 기본적인 library가 무엇인지 알아야 하고 gcc에게 사용자의 요구를 전달할 옵션을 있어야 할 것이다. 기본적인 library는 당연히 C standard library 이다. C standard library의 이름은 libc.a또는 libc.so 이다. 최근의 linux 머신은 /lib/libc.so.6 이라는 파일을 찾아 볼 수 있을 것이다 (symbolic link되어 있는 파일이다). 그리고 libgcc라고 하는 것이 있는데… 생략하고. 이제 옵션을 알아보자.

(*) -nostdlib 옵션
이 름에서 의미하는 바대로 standard library를 사용하지 말고 linking을 수행하라는 뜻이다. 실제로는 standard library 뿐 아니라 startup file이란 녀석도 포함하지 않고 linking이 수행된다. startup file에 대해서는 좀 있다가 알아보도록 하겠다.

(*) -l라이브러리이름 옵션
특 정 이름의 library를 포함하여 linking을 수행하라는 뜻이다. 예를 들어 -lmyarchive라고 하면 libmyarchive.a(또는 libmyarchive.so)라는 library파일과 같이 linking을 수행하는 것이다. library 파일 이름은 기본적으로 lib로 시작하니깐 그것을 빼고 지정하도록 되어 있다.

library에 대해서 또 하나의 옵션을 알아야 할 필요가 있다. 다름 아닌 “어느 디렉토리에서 library를 찾는가”이다. 모든 library가 /lib와 /usr/lib에 있으라는 보장이 없다. 그 디렉토리를 정하는 방법은 두 가지 인데 LD_LIBRARY_PATH라고 하는 이름의 환경 변수를 셋팅하는 방법이 있고 또 한 가지는 gcc의 옵션으로 넘겨 주는 방법이 있다.

(*) -Ldir 옵션
library 파일을 찾는 디렉토리에 “dir”이란 디렉토리를 추가하라는 옵션이다.(-Idir 옵션처럼 -L과 dir을 붙여서 적습니다.) 예를 들어 -L/usr/local/mylib 라고 하면 /usr/local/mylib라는 디렉토리에서 library 파일을 찾을 수 있게 된다.

== entry 이야기
application 을 작성하고 compile, linking 과정이 지나면 실행 파일이 만들어진다. 그리고 그 실행 파일이 수행될 때는 메모리로 load되어 수행이 시작된다는 사실을 알고 있다. 여기서 한가지 의문이 생기는데, “과연 코드의 어떤 부분에서 수행이 시작되는가?”이다. 답이 너무 뻔한가? main함수부터 수행된다고 답할 것인가? 다소 충격적이겠지만 “땡”이다. main함수부터 수행되지 않고 그전에 수행되는 코드가 존재한다. 그 먼저 수행되는 코드에서 하는 일은 여러 가지가 있는데 그냥 건너 뛰도록 하겠다. 아무튼 그 코드에서 main함수를 호출해 주고 main함수가 return하면 exit 시스템호출을 불러 준다. 그래서 main이 맨 처음 수행되는 것처럼 보이고 main이 return하면 프로그램 수행이 종료되는 것이다. 그럼 그 코드는 어디 있을까? 시스템에 따라서 다르겠지만 일반적으로 /lib혹은 /usr/lib 디렉토리에 crt1.o라는 이름의 object 파일이 있는데 그 object 파일 안에 있는 _start라는 이름의 함수(?)가 맨 먼저 수행되는 녀석이다. 결국 보통 application의 entry는 _start함수가 된다.
그럼 crt1.o object 파일 역시 같이 linking 되어야 한다. gcc를 이용해 linking을 수행할 때 command line에 아무 이야기를 해주지 않아도 자동으로 crt1.o 파일이 함께 linking 된다. 실제로는 crt1.o 뿐 아니라 비슷한 crt*.o 파일들도 같이 linking 된다. 그렇게 같이 linking 되고 있는 object파일들을 startup file이라고 부르는 것 같다.(-nostdlib 옵션 설명할 때 잠시 나왔던 startup file이 바로 이 녀석들이다.) 그럼 ld는 _start파일이 entry인지 어떻게 알고, 다른 이름의 함수를 entry로 할 수는 없는것일까? 그것에 대한 해답은 아래 linking script부분에서 해결될 것이다.

== 실행 파일에 남아 있는 정보
linking 의 결과 실행파일이 생겼는데, 보통 linux에서는 실행파일 형식이 ELF라는 포멧을 가진다.(linux 시스템에 따라 다를 수 있다.) ELF는 Executable and Linkable Format의 약자이다. 보통 linux 시스템에서의 relocatable object 파일의 형식도 ELF이다. 실제로 실행파일과 relocatable object 파일과는 조금 다른 형식을 가진다. 암튼 그건 상식으로 알아두고, 그럼 실행파일에 있는 정보는 무엇일까?
이제까지의 알아낸 정보들을 모두 종합하면 알 수 있다. 우선 실행 파일이라는 녀석이 결국은 relocatable object 를 여러 개 쌓아놓은 녀석이므로 원래 relocatable object 파일이 가지고 있던 code와 data 정보는 모두 남아있을 것이다. 그리고 entry를 나타내는 address가 있어야 수행을 할 수 있을 것이다. 또, dynamic linking을 했을 경우 관련된 shared object 정보도 남아있어야 한다.
실행 파일 속에 남아있는 data는 relocatable object에 있는 data처럼 프로그램 수행에 필요한 data가 있고 그냥 실행 파일을 설명하는 정보로서의 data가 있다. 예를 들어 -g 옵션을 주고 컴파일한 실행파일에서 디버깅 정보들은 실행과는 전혀 관계 없다. 따라서 그러한 정보들은 실행 파일 수행시에 메모리에 load될 필요도 없다.(load하면 메모리 낭비니깐) 실행 파일 속에 남아있는 code와 data는 relocatable object 처럼 특별한 단위로 저장되어 있다. ELF 표준에서는 segment라고 부르는데 보통의 경우는 object 파일처럼 section이라는 말이 쓰인다. reloctable object 파일과 마찬가지로 code는 text section에 저장되고 프로그램 수행 중에 필요한 data가 성격에 따라 나누어져 data, rodata, bss section이란 이름으로 저장되어 있다. 그 section단위로 메모리로 load될 필요가 있는지에 대한 flag정보가 있고 각 section이 load될 address(location과정에서 정해진다.)가 적혀 있어야 정확하게 loading을 할 수 있다.
기타로 symbol reference resolving이 끝났는데도 ELF형식의 실행파일은 보통의 경우 많은 symbol 정보를 그냥 가지고 있는 경우가 있다. symbol 정보 역시 수행에는 하등 관계가 없으므로 없애도 되는데, strip이라고 하는 binutils안에 있는 tool로 없앨 수 있다.

== linking script
흠 이제 좀 어려운 이야기를 할 차례이다. Location과정에서 어떤 절대 address를 기준으로 각 section들을 쌓는지, 그리고 entry는 어떤 symbol인지에 대한 정보를 linker에게 알려줄 필요가 있다. 보통 application의 경우는 시스템 마다 표준(?, 예를 들어 entry는 _start 다 하는 식)이 있는지라 별로 문제될 것은 없는데, bootloader나 kernel을 만들 때는 그런 정보를 사용자가 넘겨 주어야 할 필요가 있다. 그런 것들을 ld의 command line argument로 넘길 수도 있지만 보통의 경우는 linking script라고 하는 텍스트 형식의 파일 안에 저장하여 그 script를 참조하라고 알려준다. (아무래도 command line argument로 넘겨 줄 수 있는 정보가 한계가 있기 때문이라고 생각이 든다. location과 entry에 관한 내용 중에 ld의 command line argument로 줄 수 있는 옵션이 몇가지 있으나 한계가 있다.) ld의 옵션 -T으로 linking script 파일 이름을 넘겨 주게 된다.(gcc의 옵션 아님) linux kernel source를 가지고 있는 사람은 arch/*/*.lds 파일을 한번 열어 보길 바란다. 그게 linking script고, 초기 절대 address 하고 section 별로 어떻게 쌓으라는 지시어와 entry, 실행 파일의 형식 등을 적어 놓은 내용이 보일 것이다. 물론 한 줄 한 줄 해석이 된다면 이런 글을 읽을 필요가 없다. 그 script를 한 줄 한 줄 정확히 해석해 내려면 GNU ld manual 등을 읽어야 할 것이다.

== linux의 insmod
linux kernel을 구성하고 device driver 등은 linux kernel module(이하 module) 형식으로 run-time에 올릴 수 있다는 것을 알고 있을 것이다. module을 run-time에 kernel에 넣기 위해서 사용하는 명령어가 insmod이다.(modprobe도 가능) 이 module 이라는 것이 만들어 지는 과정을 잘 살펴 보면 gcc의 옵션중에 -c옵션으로 컴파일만 한다는 것을 알 수 있다. 확장자는 .o를 사용한다. relocatable object 파일이고, ELF형식이다. 그럼 이 module이 linux kernel과 어떻게 합쳐질까? 당연히 linking 과정을 거쳐야 된다. 일종의 run-time linking 이다. 당연히 module은 kernel내의 많은 함수와 전역 변수를 참조한다. 그렇지 않다면 그 module은 linux kernel의 동작과는 전혀 관계 없는 의미 없는 module이 될것이다. 그럼 참조되고 있는 symbol을 resolving하기 위해서는 symbol의 절대 address를 알아야한다 . 그 내용은 linux kernel 내부에 table로 존재한다. /proc/ksyms라고 하는 파일을 cat 해보면 절대 address와 symbol 이름을 살펴볼 수 있을 것이다. 살펴보면 알겠지만 생각보다 적은 양이다. 적은 이유는 그 table이 linux kernel source에 있는 전역 symbol의 전부를 포함한 것이 아니라 kernel source 내부나 module 내부에서 EXPORT_SYMBOL()과 같은 특별한 방법으로 선언된(?, 이 선언은 C 언어 문법의 declaration과는 다르다.) symbol들만 포함하기 때문이다. 다른 전역 symbol 들은 module 프로그래밍에 별 필요가 없다고 생각되어 지는 녀석들이기 때문에 빠진 것이다. 따라서 EXPORT_SYMBOL()등으로 선언된 symbol들만 사용하여 module을 작성해야 한다. 당연히 linking 과정을 거치기 때문에 앞서 설명한 linking에서 발생할 수 있는 에러들이 발생할 수 있다. 제일 많이 발생할 수 있는 것은 역시 undefined reference 에러이다. gcc의 에러와는 조금 다른 메시지가 나오겠지만 결국은 같은 내용이다.

== map 파일
linking 과정을 끝내면 당연히 모든 symbol에 대한 절대 address가 정해지게 된다. 그 정보를 알면 프로그램 디버깅에 도움이 될 수도 있으니 알면 좋을 것이다. ld의 옵션중에 '-Map 파일이름'이라는 옵션이 있는데 우리가 원하는 정보를 문서 파일 형식으로 만들어 준다. 그 파일을 보통 map 파일이라고 부른다. symbol과 address 정보 말고 section에 대한 정보도 있고 많은 정보가 들어 있다.
linux kernel을 컴파일을 하고 나면 나오는 결과 중에 System.map이라는 파일이 있는데 이 녀석이 바로 ld가 만들어 준 map 파일의 내용 중에 symbol과 symbol의 절대 address가 적혀 있는 파일이다. linux kernel panic으로 특정 address에서 kernel이 죽었다는 메시지가 console에 나오면 이 System.map 파일을 열어서 어떤 함수에서 죽었는지 알아볼 수도 있다.

== 옵션 넘기기
gcc 의 이야기 맨 처음에 gcc는 단순히 frontend로 command line으로 받은 옵션을 각 단계를 담당하고 있는 tool로 적절한 처리를 하여 넘겨준다고 말했었다. 위에서 나온 ld의 옵션 -T와 -Map 과 같은 옵션은 gcc에는 대응하는 옵션이 존재하지 않는다. 이런 경우 직접 ld를 실행할 수도 있고 gcc에게 이런 옵션을 ld에게 넘겨 주라고 요청할 수 있다. 하지만 application을 컴파일할 때는 ld를 직접 실행하는 것은 조금 부담이 되므로, gcc에 옵션을 넘기라고 요청하는 방법이 조금 쉽다고 볼 수 있다. 그런 경우 사용되는 것이 -Wl 옵션인데 간단히 이용해 보도록 하자.
$ gcc -o hello -static -Wl,-Map,hello.map hello.c
그럼 hello.map이라는 매우 큰 문서 파일이 만들어진다. 한번 살펴 보도록 하자.(-static 옵션을 안 넣으면 살펴볼 내용이 별로 없을까봐 추가했다.)
실제로는 -Wl 옵션처럼 as에게도 옵션을 넘겨 줄 수 있는 -Wa와 같은 옵션이 있는데 쓰는 사람을 본 적이 없다

'Programming > Linux' 카테고리의 다른 글

Centos7 custom build (1)  (0) 2016.03.25
yum을 이용한 rpm 다운로드 방법  (0) 2016.03.25
V4L??  (0) 2008.06.26
Linux find grep 명령사용하기  (0) 2008.06.24
Fedora8사전에서 Microsoft를 검색하면...  (0) 2008.05.26
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

아래 사이트에서 Linux용 ARToolKit을 다운 받는다http://www.hitl.washington.edu/artoolkit/documentation/usersetup.htm
※참고로 호스트에서 컴파일할경우 위 링크의 설명대로 컴파일 하면 간단히 된다

다운받은후 압축을 푼다

파일중 Configure파일이 있는데 바로 실행하지 말고 에디터를 이용하여
파일을 열어보면 다음과 같다.


   ...

    echo "Select a video capture driver."
    echo "  1: Video4Linux"
    echo "  2: Video4Linux+JPEG Decompression (EyeToy)"
    echo "  3: Digital Video Camcoder through IEEE 1394 (DV Format)"
    echo "  4: Digital Video Camera through IEEE 1394 (VGA NONCOMPRESSED Image Format)"
    echo "  5: GStreamer Media Framework"
    echo -n "Enter : "
    read ANS
    if [ "$ANS" = "1" ]
    then
                echo
                echo "Color conversion should use x86 assembly (choose 'n' for 64bit systems)?"
                echo -n "Enter : "
                read ANS
                if [ "$ANS" = "y" ]
                then
                        CCVT_OBJ="ccvt_i386.o"
                elif [ "$ANS" = "n" ]
                then
                        CCVT_OBJ="ccvt_c.o"
                else
                        echo "Please enter y or n."
                        exit 0
                fi
        VIDEO_DRIVER="VideoLinuxV4L"
        #CFLAG="-O -I/usr/X11R6/include"
        CFLAG="-O -I/root/work/kernel/rebis2.6/include/"
        LDFLAG="-L/usr/X11R6/lib"
        ARFLAG="rs"
        RANLIB=""

        LIBS="-lglut -lGLU -lGL -lXi -lXmu -lX11 -lm"
        CONFIG="AR_INPUT_V4L"
   ...
1: Video4Linux"
2: Video4Linux+JPEG Decompression (EyeToy)"
3: Digital Video Camcoder through IEEE 1394 (DV Format)"
4: Digital Video Camera through IEEE 1394 (VGA NONCOMPRESSED Image Format)
이렇게 네개의 메뉴가 있고 선택에 따라 생성되는 Makefile 이 달라진다
때문에 자시가 사용할 모드(아마도 주로 1번을 사용하게 될 것이다)의 부분을 찾아
위의 예(빨간부분) 처럼 수정을 해야 한다

물론 CFLAG외에도 LIBS관련된 옵션들도 수정을 해야하는데 후에 이야기를 하겠다

자!! 일단 ./Configure를 실행해보자!!
그럼 각각 폴더에 Makefile이 생길 것이다
일단 해야 할일은 Makefile을 추적하여 컴파일 해야할부분을
arm-linux-gcc로 바꿔줘야 한다.

   ./lib 경로 의 Makefile들은 CC=arm-linux-gcc로 바꿔주기만하면 쉽게 컴파일 될것이고
   ./util 과 examples 경로는 cc를 $(cc) 로바꾸고 cc=arm-linux-gcc를 추가해주면 끝!

이제 make를 실행해보자!!
제일 상위의 Makefile 경로대로 일단 ./lib 경로의 폴더들 부터 하나하나 make 를 해보자
정확한 기억은 아니지만 ./lib폴더는 문제없이 쉽게 크로스 컴파일이 될것이다.

삽질의 시작은 여기부터 이다!! 이제 ./util디렉토리와 ./examples폼더를 컴파일 해야한다!!

....그런데

좀 길어 질것 같은 관계로 일단 여기서 1부끝!!!
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
장치(Device)
Video4Linux는 다음의 장치(Device) 파일들을 제공한다. 이것들은 보통 /dev/bttv로서 알려져 있는 캐릭터형의 장치(Device)이며,
많은 사람들을 위해서 /dev/bttv는 /dev/video0 에의 기호 연결(Symlink)이 되어 있다.


장치(Device) 파일명 마이너 번호의 범위 기능
/dev/video 0-63 비디오 capture를 위한 인터페이스(Interface)
/dev/radio 64-127 AM/FM 라디오 장비
/dev/vtx 192-223 Teletext 인터페이스(Interface) 칩(Chips)
/dev/vbi 224-239 Raw VBI 데이터(Intercast/Teletext)

Video4Linux를 이용하는 프로그램은, 우선 장치(Device) (파일)을 열고 검색해서, 이용하길 원하는 기능을 찾아낸다. 기능의 검색으로 각 인터페이스(Interface)가 무엇을 지원하고 있는지를 알 수 있다. 이 API는 비디오 캡쳐 카드를 위해서만 정의되었다. 라디오 카드(Radio Card)의 경우에는 이 부분집합을 이용한다. Teletext의 인터페이스(Interface)에는 이미 정의되고 있는 VTX API를 이용한다.

기능의 검색의 Ioctl(Capability Query Ioctl)
비디오 장치(Device)가 지원하는 기능에 관한 정보를 얻으려면 VIDIOCGCAP ioctl()을 이용한다. ioctl에 struct video_capability를 건네주면 내용을 돌려준다. struct video_capability에는 이하에 주는 항목이 포함되어 있다.

name[32] 이 장치(Device)의 규범적인(Canonical) 이름
type 인터페이스(Interface)의 타입
channels radio/tv 의 입력 소스의 수(만약 사용 가능하면)
audios 오디오 장치(Device)의 수(만약 사용 가능하면)
maxwidth 최대의 capture폭(단위:픽셀)
maxheight 최대의 capture 높이(단위:픽셀)
minwidth 최소의 capture폭(단위:픽셀)
minheight 최소의 capture 높이(단위:픽셀)

type의 항목은 장치(Device)의 기능에 관한 정보가 플래그(Flag)로서 리스트(List)되어 있다. 이것에는 다음의 것이 있다.

이름 설명
VID_TYPE_CAPTURE 메모리상에 capture 하는 기능이 있다
VID_TYPE_TUNER 어떤 형식(Some Form)의 Tuner가 있다
VID_TYPE_TELETEXT teletext의 기능을 가지고 있다.
VID_TYPE_OVERLAY frame buffer상에 이미지를 오버레이 하는 기능이 있다
VID_TYPE_CHROMAKEY chroma-key로 오버레이 하는 기능이 있다
VID_TYPE_CLIPPING 오버레이의 클리핑을 하는 기능이 있다
VID_TYPE_FRAMERAM 오버레이는 frame buffer의 메모리를 덧쓰기하는 기능이 있다
VID_TYPE_SCALES 하드웨어가 이미지의 크기변환(Image Sacling)을 지원한다.
VID_TYPE_MONOCHROME 그레이 스케일(Grey Scale)만 Captuer한다.
VID_TYPE_SUBCAPTURE 이미지의 일부분만을 capture 할 수 있다

capture 장치(Device)가 돌려주는 최소와 최대의 사이즈는, 그 범위내에서 가능한 모든 높이와 폭의 비율이나 사이즈를 사용할 수 있는 것은 아니다. capture 사이즈를 설정하는 경우, 요구된 사이즈를 넘지 않는 범위에서 가능한 최대의 사이즈로 설정된다. 예를 들면 quickcam은 3개의 고정적인 설정 밖에 사용할 수 없다.

Frame buffer
capture 카드로부터 frame buffer에 직접 데이터를 쓰게 하기 위해서는, frame buffer의 베이스 주소(Base Address), 사이즈(Size), 구조(Organisation)를 장치(Device) 드라이버에 가르쳐 두지 않으면 안된다. 이것은 특권 모드(Proviliged)의 ioctl()이며, 사실은 X서버 자신이 설정해야 할 것이다.
VIDIOCSFBUF ioctl로 capture 장치(Device)를 위한 frame buffer의 파라미터를 설정한다. capture 카드가 frame buffer에의 직접적인 쓰기를 지원하고 있지 않는 경우, 이 ioctl()는 지원되지 않는다. VIDIOCGFBUF ioctl( ) 는 현재 설정되어 있는 파라미터를 돌려준다. 두 경우 모두 struct video_buffer를 사용해 파라미터를 얻어온다.

void *base 버퍼의 물리 베이스 주소(Base Physical Address)
int height frame buffer의 높이
int width frame buffer의 폭
int depth frame buffer의 깊이(Depth)
int bytesperline 인접한 다음의 같은 옆위치까지의 메모리상에서의 바이트(Byte) 수

이러한 값은 frame buffer의 물리적인 레이아웃을 반영한다. 실제로 표시되고(Visible) 있는 영역은 실제의 frame buffer보다 작을수도 있다. 사실, XFree86에서 이런 것은 보통이다. XFree86 의 DGA는 이 ioctl 로 설정하는 파라미터를 제공할 수 있다. 베이스 주소를 NULL로 설정해서, frame buffer에 액세스를 할 방법이 없다는 것을 나타낼 수 있다.

Capture Windows
capture하는 영역은 struct video_window를 사용해서 결정한다. 이 구조체는 capture 하는 영역과 필요하면 클리핑(Clipping)하는 영역을 설정한다. VIDIOCGWIN ioctl 로 현재의 설정된 값을 가져 올 수 있어서 VIDIOCSWIN 로 새로운 값을 설정할 수 있다.
VIDIOCSWIN 의 ioctl 에 성공했을 경우, 적당한(Suitable) 파라미터들이 선택되었다는 것이다. 이것은 요구된 파라미터 대로에 정확하게 설정했다고 하는 의미가 아니다. 유저 프로그램은, VIDIOCGWIN를 사용해서 실제로 설정된 파라미터가 정확한지 확인할 필요가 있다. struct video_window 에는 다음과 같은 항목이 있다.

x X윈도우에서의 X 좌표
y X윈도우에서의 Y 좌표
width capture 하는 이미지의 폭
height capture 하는 이미지의 높이
chromakey chroma-key의 값(호스트 순위(Host Order)에서의 RGB32의 값)
flags 추가 capture 플래그
clips 클리핑 하는 직사각형의 리스트(설정만)
clipcount 클리핑 직사각형의 수(설정만)

클리핑 직사각형은 배열로 넘겨준다. 클리핑 직사각형의 각 요소에는 다음의 항목이 있다.

x 스킵(Skip)하는 직사각형의 X좌표
y 스킵(Skip)하는 직사각형의 Y좌표
width 스킵(Skip)하는 직사각형의 폭
height 스킵(Skip)하는 직사각형의 높이

단지 capture 영역을 설정한 것 만으로는, capture하는 것은 유효하지 않는다. VIDIOCCAPTURE ioctl 로 1을 설정해서 건내주는 것으로써, 오버레이 capture 하는 것을 시작한다. 그리고 0을 설정하는 것으로써, 멈출 수가 있다.
몇몇의 capture-장치(Device)에 따라서는, 실제로 보이고 있는 영역의 일부를 capture 할 수 있는 것도 있다. 이 경우, VIDEO_TYPE_SUBCAPTURE가 설정되어 있다. video_capture 구조체는 시간과 Capture할 특별한 부분 영역을 지정한다.
video_capture 구조체에는 다음의 항목이 있다.

x 붙잡는(Grab) 영역의 X좌표
y 붙잡는(Grab) 영역의 Y좌표
width 붙잡는(Grab) 영역의 폭
height 붙잡는(Grab) 영역의 높이
decimation 적용될 스킵할 영역(decimation)
flags 붙잡을(Grab) 때의 플래그

가능한 플래그(flags)에는 다음의 것이 있다.

이름 설명
VIDEO_CAPTURE_ODD 홀수 프레임만을 capture 한다
VIDEO_CAPTURE_EVEN 짝수 프레임만을 capture 한다

비디오 소스(Video Source)
각각의 video4linux의 비디오나 오디오 드라이버는 한개 혹은 여러 소스 채널로부터 capture 한다. 각각의 채널은 VIDIOCGCHAN ioctl호출해서 정보를 얻을 수 있다. 이 함수(VIDIOCGCHAN ioctl)를 사용하기 전에, 호출하는 측은 반드시 channel의 항목에 정보를 얻고 싶은 채널의 번호를 설정해야 한다. 채널 자신에 대한 정보를 struct video_channel 에 저장하여 값을 돌려준다.
VIDIOCSCHAN ioctl 는 정수(integer) 형태의 인수를 취해서, capture하는 입력으로 변환한다. 색의 설정이나 튜닝에 관한 파라미터가 채널 변환을 건너서 유지되는 것은 정의되지 않는다. 호출측에서, 채널마다 이것들을 관리해 다시 설정해 줘야한다.(다른 비디오 입력마다 다른 설정을 보존하는 것이 합리적이다. (reasonable))
struct video_channel 은 다음과 같이 구성되어 있다.

channel
채널 번호(Channel Number) name 입력의 이름 - 카드의 입력 자신의 라벨(Label)이 반영되는 것이 바람직하다.
tuners 이 입력으로 연결되어 있는 튜너(Tuner)의 수
flags 튜너(Tuner)가 가지고 있는 설정(Properties)
type 입력의 타입(만약 알고 있는 경우만)
norm 이 채널의 표준(TV신호의 모드)

flags의 정의는

VIDEO_VC_TUNER 채널에 튜너(Tuner)가 있다.
VIDEO_VC_AUDIO 채널에 오디오가 있다.
VIDEO_VC_NORM 채널은 표준(TV신호의 모드)의 설정을 가지고 있다.

types의 정의는

VIDEO_TYPE_TV 입력은 TV 입력이다
VIDEO_TYPE_CAMERA 입력은 카메라이다

이미지 속성의 설정
화상(Picture)의 이미지 속성은 VIDIOCGPICT ioctl를 사용하면 struct video_picture에서 얻을 수 있다. VIDIOCSPICT ioctl 를 호출하여 이러한 값을 변경할 수 있다. palette의 형(Type)을 제외한 모든 값은 0~65535의 사이에서 조정된다.
struct video_picture 에는 다음의 항목이 있다.

brightness 화상의 밝음
hue 화상의 색조(hue)(칼라의 경우만)
colour 화상의 색(Color)(칼라의 경우만)
contrast 화상의 대조(Contrast)
whiteness 백색도(Whiteness) (그레이 스케일의 경우만)
depth 캡처하는 깊이(Capture Depth)(frame buffer의 깊이(depth)와 대조(match)할 필요가 있겠지요)
palette 이 이미지에서 사용될 팔레트를 알려준다.

팔레트의 값에는 다음의 것이 있다.

VIDEO_PALETTE_GREY 선형적으로 증가하는 gray scale(255가 가장 밝은 흰색)
VIDEO_PALETTE_HI240 BT848의 8 bit 칼라 큐브(cube)
VIDEO_PALETTE_RGB565 RGB565를 16 bit 워드(Word)에 채운다.(Packed)
VIDEO_PALETTE_RGB555 RGB555를 16 bit 워드(Word)에 채운다.(Packed), 맨 위의 비트는 미정의
VIDEO_PALETTE_RGB24 RGB888를 24 bit 워드(Word)에 채운다.(Packed)
VIDEO_PALETTE_RGB32 RGB888를 하위 3바이트에 넣은 32bit. 맨 위의 아르바이트는 미정의
VIDEO_PALETTE_YUV422 YUV422의 비디오의 형태(Style) - 4bits를 Y, 2bits를 U, 2bits를 V에 할당한 8bits
VIDEO_PALETTE_YUYV Describe me
VIDEO_PALETTE_UYVY Describe me
VIDEO_PALETTE_YUV420 YUV420 캡처(capture)
VIDEO_PALETTE_YUV411 YUV411 캡처(capture)
VIDEO_PALETTE_RAW RAW 캡처(capture) (BT848)
VIDEO_PALETTE_YUV422P YUV 4:2:2 Planar
VIDEO_PALETTE_YUV411P YUV 4:1:1 Planar

튜닝(Tuning)
각각의 비디오 입력 채널에는 그것과 관련된 하나 또는 다수 개의 튜너(Tuner)가 연결되어 있다. 많은 장치(Device)는 튜너(Tuner)를 가지고 있지 않는다. TV카드나, 라디오(Radio)카드에는 하나 또는 다수 개의 튜너(Tuner)가 붙어 있다.
튜너(Tuner)에 대한 정보는 VIDIOCGTUNER ioctl를 사용해서 struct video_tuner에서 얻을 수 있다. 튜너(Tuner)의 번호를 설정해 ioctl를 호출하면, 내용을 구조체로 보내준다. 튜너(Tuner)의 변환은 사용하고 싶은 튜너(Tuner)의 정수형의 번호(int)를 인수로 VIDIOCSTUNER를 이용한다.

struct video_tuner은 다음의 항목으로 구성된다.

tuner 튜너(Tuner)의 번호
name 튜너(Tuner)의 규범적인(Canonical) 이름 (예 FM/AM/TV)
rangelow 설정 가능한 최저 주파수
rangehigh 설정 가능한 최고 주파수
flags 튜너(Tuner)의 상태를 알려주는 플래그(Flags)
mode TV신호의 모드(관련하는 경우만)
signal 신호의 강도(아는 경우) - 0~65535의 사이

플래그(flags)에는 다음의 것이 있다.

VIDEO_TUNER_PAL PAL를 수신(tuning)을 지원한다.
VIDEO_TUNER_NTSC NTSC를 수신(tuning)을 지원한다.
VIDEO_TUNER_SECAM SECAM를 수신(tuning)을 지원한다.
VIDEO_TUNER_LOW 주파수가 저역대(Lower range)이다.
VIDEO_TUNER_NORM 튜너(Tuner)의 표준(TV신호의 모드)를 설정할 수 있다
VIDEO_TUNER_STEREO_ON 튜너(Tuner)의 오디오가 스테레오가 되어 있다
VIDEO_TUNER_RDS_ON 튜너(Tuner)는 RDS 데이터스트림(Datastream)을 보인다.
VIDEO_TUNER_MBS_ON 튜너(Tuner)는 MBS 데이터스트림(Datastream)을 보인다.

mode 에는 다음의 것이 있다.

VIDEO_MODE_PAL 튜너(Tuner)는 PAL 모드 이다.
VIDEO_MODE_NTSC 튜너(Tuner)는 NTSC 모드 이다.
VIDEO_MODE_SECAM 튜너(Tuner)는 SECAM 모드 이다.
VIDEO_MODE_AUTO
자동적으로 변환(switches)하는 모드 또는 모드를 설정할 수 없는 경우

튜너(Tuner)로 설정하는 주파수는 1/16 MHz 내에서의 unsigned 의 32bit 의 값이지만 VIDEO_TUNER_LOW 의 플래그가 설정되어 있는 경우에는 1/16 KHz의 값이다. 현재의 주파수는 VIDIOCGFREQ ioctl를 통해서 unsigned long 값으로 없을 수 있고 VIDIOCSFREQ ioctl 로 설정할 수 있다.

오디오(Audio)
TV와 라디오 장치(Device)는 선택할 수 있는 하나 또는 다수 개의 오디오 채널을 가지고 있다. 오디오의 설정은 struct video_audio을 VIDIOCGAUDIO ioctl에 건네주는 것으로 얻을 수 있다. 또 VIDIOCSAUDIO ioctl로 오디오의 특성(Properties)를 설정할 수 있다.
struct video_audio은 다음의 항목으로 구성된다.

audio 채널 번호
volume 볼륨 레벨(Level)
bass 중저음(bass) 레벨(Level)
treble 고음(treble) 레벨(Level)
flags 오디오 채널의 상태를 알려주는 플래그(Flags)
name[16] 오디오 입력을 위한 규범적인(Canonical) 이름
mode 오디오 입력의 모드
balance 좌/우의 밸런스
step 하드웨어로 설정 가능한 단계(Step)

플래그(flags)에는 다음과 같이 정의된다.

VIDEO_AUDIO_MUTE 오디오가 단음(mute)되고 있다
VIDEO_AUDIO_MUTABLE 단음(mute)를 지원한다.
VIDEO_AUDIO_VOLUME 음량(Volume)의 컨트롤을 할 수 있다
VIDEO_AUDIO_BASS 중저음(bass)의 컨트롤을 할 수 있다
VIDEO_AUDIO_TREBLE 고음(treble)의 컨트롤을 할 수 있다
VIDEO_AUDIO_BALANCE 좌우 밸런스의 컨트롤을 할 수 있다

디코딩(Decoding) 모드는 다음과 정의된다.

VIDEO_SOUND_MONO 모노(Mono) 신호
VIDEO_SOUND_STEREO 스테레오(Stereo)의 신호(NICAM for TV)
VIDEO_SOUND_LANG1 언어 1을 바꿀 수 있는 유럽 TV(European TV alternate language 1)
VIDEO_SOUND_LANG2 언어 2을 바꿀 수 있는 유럽 TV(European TV alternate language 2)

이미지 읽어오기(Reading Image)
read의 시스템 콜(System Call)에 대한 각각의 콜(Call)은 장치(Device)로부터 다음의 가능한 이미지를 가져온다. 포맷을 설정하고 함수에 대한 올바른 크기의 버퍼를 준비하는 것은 호출자의 책임이다. 모든 장치(Device)가 read 시스템 콜을 지원하지 않는다.
두번째 방법으로 장치(Device)가 mmap을 지원하면 mmap 인터페이스(interface)를 통해서 이미지 가져오는 것(Capture)을 조정할 수 있다. mmap 인터페이스를 사용하기 위해서 사용자는 우선 첫 번째로 원하는 이미지와 깊이(Depth)의 성질을 결정해야 한다.
그 다음 VIDIOCGMBUF ioctl를 호출한다. 이 ioctl 는, mmap 해야 할 버퍼(Buffer)의 크기와 각각의 프레임(Frame)에 대한 버퍼에서의 오프셋(offset)을 알려준다. 지원되는 프레임의 수는 장치(Device)에 의해서 결정되고 대체적으로 한 프레임이다.
video_mbuf structure은 다음의 항목으로 구성된다.

size 잡으려고(Map) 하는 바이트(Bytes) 수
frames 프레임(Frame)의 수
offsets 각각의 프레임 마다의 오프셋(offset)

mmap를 실시하면, VIDIOCMCAPTURE ioctl로 (최초의 설정과 같거나 작은) 사용하고 싶은 이미지의 사이즈를 설정한다. 설정이 끝나면 메모리 맵(Map)된 버퍼에 capture가 시작된다. 프로그램에 의해 버퍼가 사용(used) 될 때마다, VIDIOCSYNC ioctl를 호출해서 이 프레임을 해체(free)하고 계속(continue) 한다. 추가로 : VIDIOCSYNC는 해체(free)하고 싶은 프레임 번호를 인수로 가지고 있다.
버퍼가 unmapped 되거나, 모든 버퍼가 다 채워지면, capture는 멈춘다. 드라이버에 메모리와 화면의 양쪽 모두에 capture할 수 있게 설정했을 경우, 메모리에 캡처(Capture)하는 동안에 드라이버는 가능한 한 최선의 방법(Best Effort)으로 캡처한 것을 화면에 표시한다. 이것은 일반적으로 메모리 맵으로 Captuer할 수 없었던 모든 프레임(frame)이 화면에 표시될 수 있다.
마지막의 ioctl는, 만약 하나의 드라이버가 여러 개의 부속품(Components)으로 되어 있을 경우, 하나의 장치(Device)가 관련되어 있는 장치(Device)들을 얻게 해준다. (예를 들면, video0는 나쁜 문제를 만드는 intercast 표시 로그램의 원인이 될 수 있는 vbi0와 항상 관련이 있다고는 할 수 없다.)VIDIOCGUNIT ioctl은 관련되어 있는 장치(Device)가 있는 경우에 그 단위 번호들(unit Number)을 알려준다. video_unit 구조체에는 다음의 항목이 있다.


video 비디오 capture 장치(Device)
vbi VBI capture 장치(Device)
radio 라디오 장치(Device)
audio 오디오 믹서
teletext Teletext 장치(Device)

RDS 데이터스트림(Datastream)
RDS를 지원하는 라디오 장치(device)를 위해서, 장치에 read() 시스템 콜을 이용하여 Radio Data System(RDS) 정보를 받아오는 것은 가능하다. 이 데이터들은 다음과 같은 3개의 그룹으로 합쳐있다.

First Octet RDS 블록의 LSB(Least Significant Byte)
Second Octet RDS 블록의 MSB(Most Significant Byte)
Third Octet
Bit 7 : 에러 비트(Error bit). 이 블록을 받는 도중에 복구할 수 없는 에러가 발생했다는 것을 나타낸다.
Bit 6 : Corrected bit. 이 데이터 블록을 위해서 에러가 수정되었다는 것을 나타낸다.
Bit 5~3 : Received Offset. 싱크 시스템(Sync System)에 의해서 오프셋이 받아졌다는 것을 나타낸다.
Bit 2~0 : Offset Name. 이 데이터에 적용되는 오프셋을 나타낸다.

'Programming > Linux' 카테고리의 다른 글

yum을 이용한 rpm 다운로드 방법  (0) 2016.03.25
gcc 컴파일 옵션.  (0) 2008.08.22
Linux find grep 명령사용하기  (0) 2008.06.24
Fedora8사전에서 Microsoft를 검색하면...  (0) 2008.05.26
printf를 잘 쓰자  (0) 2008.05.21
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

1. 문자열 찾기(영어 전용)

# grep -rw "찾는문자열" ./

2. 문자열 찾기

# grep -i -l "찾는문자열" * -r 2> /dev/null

2>/dev/null : 에러출력을 /dev/null 로 보내라는 의미

3. 문자열 찾기(한영 공용)

# find . -exec grep -l "찾는문자열" {} \; 2>/dev/null

4. 문자열 찾기(한영, 대소문자 무시)

# find . -exec grep -i -l "찾는문자열" {} \; 2>/dev/null

옵션 i는 대소문자를 무시하라는 의미

5. 문자열 찾은 후 치환

# find . -exec perl -pi -e 's/찾을문자열/바꿀문자열/g' {} \; 2>/dev/null

6. 파일 찾기

# find / -name 파일명 -type f

7. 파일 찾기(대소문자 무시)

# find / -iname 파일명 -type f

8. 디렉터리 찾기

# find / -name 파일명 -type d

9. 디렉터리 찾기(대소문자 무시)

# find / -iname 파일명 -type d

10. 하위 디렉터리에서 모든 파일 찾기


find . | xargs grep '파일명'

'Programming > Linux' 카테고리의 다른 글

gcc 컴파일 옵션.  (0) 2008.08.22
V4L??  (0) 2008.06.26
Fedora8사전에서 Microsoft를 검색하면...  (0) 2008.05.26
printf를 잘 쓰자  (0) 2008.05.21
리눅스 디렉토리  (0) 2008.05.21
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.
■ 리눅스 디렉토리 구조

□ / (root)
- 마운트 되는 리눅스 파일 시스템이 있는 최상위 디렉토리
- 시스템의 근간을 이루는 가장 중요한 디렉토리
- 파티션 설정 시 반드시 존재하여야 함
- 절대경로의 기준이 되는 디렉토리
    ※ 절대경로 - / 디렉토리 기준   예) /usr/local
       상대경로 - 현재 작업 디렉토리 기준 예) ./local

□ /bin
- 리눅스의 기본 명령어(binary)들이 들어있는 디렉토리
- 시스템을 운영하는데 기본적인 명령어들이 들어 있음.

□ /sbin
- 시스템 관리에 관련된 실행 명령어들이 들어있는 디렉토리
- 시스템 점검 및 복구 명령, 시스템 초기 및 종료 명령 등 시스템 관리에
   관련된 실행파일들 존재.

□ /lib
- 프로그램들이 의존하고 있는 라이브러리 파일들 존재.
- /lib/modules : 커널 모듈 파일들 존재.
- 대부분의 라이브러리들은 링크로 연결되어 있음.

□ /proc
- 시스템에 대한 정보를 제공하는 가상 파일 시스템.
- 커널의 어떤 기능을 제어할 수 있는 역할을 가지고 있음.
- 대부분 읽기 전용이나, 일부 파일중에는 쓰기가 가능한 파일이 존재
   하는데 이러한 파일들에 특정 값을 지정하면 커널 기능이 변하게 됨.
- 이 디렉토리 내에 있는 파일을 cat 명령을 이용하여 보면 시스템 정보를
   확인 할 수 있음.
   예) 인터럽트 정보 확인 ---> cat /proc/interrupts

□ /etc
- 시스템 환경 설정 파일이 있는 디렉토리
- 네트워크 관련 설정파일, 사용자 정보 및 암호정보, 파일 시스템 정보,
   보안파일, 시스템 초기화 파일등 중요 설정 파일들의 위치한 디렉토리
- /etc/CORBA : Common Object Request Broker Architecture (COBRA)에
                관한 파일이 들어있음.
- /etc/X11 : 엑스 윈도우 설정에 관련된 파일들이 있음.
- /etc/cron.d : crontab 명령의 설정파일이 있음.
- /etc/cron.daily : 매일 작동하게 하는 crontab 스크립트 파일이 존재.
- /etc/gnome : GTK+ 정의파일들이 있음.
- /etc/httpd : 아파치 웹 서버의 설정 및 로그파일이 있음.
- /etc/logrotate.d : logrotate 설정 파일들이 있음.
- /etc/mail : 센드메일과 관련된 파일이 있음.
- /etc/ppp : ppp 설정에 관련된 파일들이 있음.
- /etc/profile.d : 쉘 로그인 하여 프로파일의 실행되는 스크립트에
                    대한 정의가 있음.
- /etc/rc.d : 시스템 초기화와 관련된 스크립트가 존재.
- /etc/samba : 삼바에 관련된 파일들이 있음.
- /etc/security : 터미널 보안에 관련된 설정이 있음.
- /etc/skel : 새로운 사용자를 추가할 때 자동적으로 생성되는 디렉토리와
               파일이 있음.
- /etc/squid : squid 프록시 서버에 관련된 파일이 있음.
- /etc/ssh : secure shell 설정 파일이 있음
- /etc/sysconfig : 시스템과 네트워크 설정을 담고 있음.
- /etc/xinetd.d : 슈퍼데몬 xinetd.d의 서비스 영역을 설정하는 디렉토리.

□ /var
- 가변 자료 저장 디렉토리
- 시스템 운영중에 시스템 자료 데이터가 변경될 때 변경된 자료들이
   저장되는 곳.
- 주로 시스템 작동기록(log)들을 저장.
- /var/log : 시스템에 발생된 일들에 대한 기록 파일이 있음
- /var/named : 네임서버 설정 파일들 존재
- /var/spool/mail : 수신 메일을 사용자 명으로 기록하는 디렉토리

□ /usr
- 일반 사용자들을 위한 대부분의 프로그램 라이브러리 파일들이 위치.
- /usr/bin : 응용 프로그램의 실행 파일이 위치
- /usr/sbin : 주로 네트워크 관련 실행 명령어와 실행 데몬들을 많이
   포함하고 있음.
- /usr/X11R6 : X-window 시스템에 관련된 파일 존재.
- /usr/include : 기본 C 라이브러리 헤더 파일과 각종 라이브러리
   헤더파일들이 있음.
- /usr/lib : /usr/bin과 /usr/sbin에 있는 실행 바이너리를 실행하기 위한
   라이브러리 존재.
- /usr/src : 프로그램소스 및 커널 소스들이 보관되어 있는 곳.
- /usr/man : 매뉴얼 페이지가 담겨있는곳.
- /usr/local : 새로운 프로그램들이 설치되는 곳
    (windows의 Program Files 와 유사)

□ /mnt
- 다른 장치들을 마운트 할때 일반적으로 사용하는 디렉토리
- 다른 디렉토리를 사용하여도 됨.

□ /home
- 일반 사용자의 홈 디렉토리가 만들어 지는 디렉토리
- 사용자 계정을 만들면 게정과 같은 이름으로 새로운 사용자 디렉토리가
   /home 디렉토리의 하위 디렉토리로 생성됨.
예) test 사용자 추가 후 홈 디렉토리 확인하기.
root@test />$adduser test
root@test />$cd /home
root@test home>$ls

     test   <-- 디렉토리 생성

□ /boot
- 부팅에 핵심적인 커널 이미지와 부팅 정보 파일을 담고 있는 디렉토리
- /etc/lilo.conf에서 지정한 커널 부팅 이미지 파일이 들어 있으며 부팅시
   매우 중요한 디렉토리

□ /root
- 슈퍼유저(root) 사용자의 홈 디렉토리.
- / 와 /root 디렉토리는 부르는 이름은 같지만 서로 다름.

'Programming > Linux' 카테고리의 다른 글

Fedora8사전에서 Microsoft를 검색하면...  (0) 2008.05.26
printf를 잘 쓰자  (0) 2008.05.21
Data Types  (0) 2008.04.03
유닉스/리눅스 명령어 레퍼런스  (0) 2008.03.26
Man Page/sigaction  (0) 2008.03.26
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

1 Data Types

우리가 사용하는 데이터는 컴퓨터의 메모리에 연속된 비트의 나열로 저장이 된다. 이러한 비트의 나열은 컴퓨터입장에서는 문제가 안 되겠지만 인간의 입장에서는 알아보기 힘들다는 문제가 발생한다. 그래서 데이터 타입을 두어서 인간이 좀더 쉽게 사용할 수 있도록 하고 있다.

데이터 타입은 다음과 같은 특징을 가지고 있다.
  • 데이터가 어떻게 표현되고 사용 될지를 결정한다.
  • 데이터 타입에 따라 컴퓨터가 어떻게 데이터를 다룰지를 알려주다. (숫자로 다룰지 아니면 문자로 다룰지 등..)
  • 모든 값은 데이터 타입에 의해서 표현될 수 있다.

예를 들어서 메모리에 다음과 같은 비트패턴이 저장되어 있다고 가정해보자.
  0000 0000 0110 0111 
 

이 값이 어떻게 표현될까 ? 이것은 데이터 타입을 어떻게 정의하느냐에 따라 달라진다. 만약 데이터 타입을 'int로 하기로 했다면 숫자 103으로 표현될 것이다. 그러나 문자를 저장하는 char로 하기로 했다면 영문자 g로 표현이 된다.

데이터 타입은 이렇게 인간의 입장에서 컴퓨터 메모리에 저장된 데이터를 어떻게 다룰 것인지를 결정하기 위해서 사용이 된다.

아래의 코드를 실행시켜보기 바란다.
#include <stdio.h> 
 
int main(int argc, char **argv) 
{ 
  int a = 103; 
  char b = 103; 
 
  printf("%d\n", a); 
  printf("%c\n", b); 
} 
 
똑같은 103인데, 서로 다르게 출력되는걸 확인할 수 있을 것이다.

2 Primitive Data Types

인간은 매우 다양한 형태의 데이터를 다루기를 원하고, 그런일을 할 수 있는 프로그램을 만들어 낼 수 있어야 한다. 다루는 데이터의 형태가 다양하니, 데이터 타입 역시 다양하면 좋을 것이다. 그러나 컴퓨터는 매우 단순한 기계다. 쓸데없이 데이터 타입을 이것 저것 만들면, 이것을 다루는 컴퓨터 역시 달가워하지 않을 것이다. 프로그래머 역시 각 데이터 타입에 따른 고려사항이 늘어나니 권장할만한 사항이 아니다.

그래서 데이터타입을 다루어야 하는 프로그래밍 언어는 Primitive Data Type이라고 불리우는 최소한의 반드시 필요한 데이터 타입을 지원하고 있다. Primitive Data Type은 원시 데이터 타입 이라고 부르기도 한다.

각 원시 데이터 타입은 고유의 크기를 가지고 있으며, 표현할 수 있는 데이터의 한계가 정의되어 있다. 다음은 C 언어에서 지원하는 원시 데이터 타입과 크기, 데이터 범위를 보여주는 표다.

2.1 숫자형 원시 데이터 타입

숫자를 표현하기 위해서 사용되는 데이터 타입이다. 크게 정수형 데이터를 표현하기 위한 정수형 원시 데이터 타입부동소숫점형 원시 데이터 타입으로 나눌 수 있다.
  • 정수형 원시 데이터 타입
    char 1byte -128 127
    short 2byte -32768 32767
    long int 4byte 2,147,483,648 2,147,483,647
    long long int 8byte 9,223,372,036,854,775,808 9,223,372,036,854,775,807

이들 정수형 데이터 타입들은 signed비트라는 것을 가지고 있어서, 음수까지 표현할것인지를 정의할 수 있다. 음수까지 표현할 거라면 signed를 양수만 표현할거라면 unsigned를 타입의 앞에 붙여준다. 따로 붙여주지 않았다면 signed 가 붙은걸로 해석을 한다. 즉 위의 데이터 타입은 실제로는 signed char, signed short, signed long int, signed long long int와 동일하다.

unsigned를 명시하게 되면 양의 정수만 표현하게 된다. 음의 정수를 표현할 필요가 없으니 그만큼 양수 쪽으로 표현범위가 늘어날 것이다. unsigned char 이라면 255, unsigned long int 라면 4,294,967,295 가 된다.

  • 부동소숫점형 원시 데이터 타입
    float 4byte +/- 10E-37 +/- 10E38
    double 8byte +/- 10E-307 +/- 10E308

2.2 enumerated type

일반적으로 enumerated 타입은 숫자로 나열된 카테고리 같은 데이터를 만들기 위해서 사용한다. C에서는 enum을 이용해서 enumerated 타입의 데이터를 정의할 수 있다.
enum cardsuit { 
   CLUBS, 
   DIAMONDS, 
   HEARTS, 
   SPADES 
}; 
 
이제 enum 을 이루는 각각의 요소들은 숫자 0,1,2... 로 차례대로 대응되게 된다. 다음의 프로그램을 실행시켜 보자.
#include <stdio.h> 
enum cardsuit { 
  CLUBS, 
  DIAMONDS, 
  HEARTS, 
  SPACES 
}; 
int main() 
{ 
  printf("Card CLUBS is %d\n", CLUBS); 
  printf("Card DIAMONDS is %d\n", DIAMONDS); 
  printf("Card HEARTS is %d\n", HEARTS); 
  printf("Card SPACES is %d\n", SPACES); 
} 
 

다음과 같은 결과를 확인할 수 있을 것이다.
# ./enum 
Card CLUBS is 0 
Card DIAMONDS is 1 
Card HEARTS is 2 
Card SPACES is 3 
 

그렇지만 CLUBS 가 0이 아닌 다른 수로 대응되어야 할 경우도 생길 것이다. 그럴 땐, 필요한 값을 대입시켜 주면 된다. 즉 값을 명시하지 않으면 0부터 시작해서 1씩 증가하고, 값을 명시하면 명시된 값을 시작으로 1씩 증가하는 것으로 보면 된다.

문제
cardsuit를 다음과 같이 정의 했을 때, 어떤 값이 출력될지 생각해 보라.

enum cardsuit { 
  CLUBS = 1, 
  DIAMONDS, 
  HEARTS = 100, 
  SPACES 
}; 
 

2.3 Pointer type

컴퓨터는 계산을 하기 위한 기계다. 이때 계산에 사용될 모든 데이터는 일단 메모리로 읽혀져서 필요한 계산을 하게 된다. 예를 들어 하드디스크에 A 라는 문서가 있다고 가정해 보자. 이 문서를 편집하기 위한 프로그램을 가동시키면, 프로그램은 하드디스크에 있는 문서를 모두 읽어서 컴퓨터의 메모리로 불러들인 다음 필요한 일을 하게 된다. 다른 모든 연산들역시 마찬가지다.

그렇다면 문제가 발생한다. 컴퓨터에는 여러개의 프로그램이 떠 있을테니, 각각의 프로그램이 사용하는 데이터가 메모리의 여기저기 저장되어 있을 것이다. 이때 프로그램은 자신이 사용할 데이터가 메모리상의 어느 위치에 있는지 알고 있어야 한다.

이를 위해서 메모리에는 아래 그림과 같이 주소 값이 부여되어 있다.

pointer.png

프로그램은 이 기억값을 기억해서 데이터의 위치를 정확히 찾아내어서 읽어오게 되는 것이다. Pointer는 이러한 주소값을 저장하는 데이터 타입이다.

다른 데이터 타입들이 그렇듯이 pointer도 고유의 크기를 가지고 있다. pointer 데이터 타입의 크기는 4byte이다. 메모리상의 주소에는 마이너스 값이 필요가 없으므로 0 - 4,294,967,295의 숫자가 저장된다. 32bit 컴퓨터에 사용가능한 메모리의 총크기가 4Giga다 라는 얘기가 여기에서 나온다. 가리킬수 있는 숫자의 범위가 4,294,967,295 (약 4giga)이므로, 이를 초과한 영역에 저장된 데이터는 읽어올 수가 없기 때문이다.

이는 C 와 같은 프로그래밍 언어에도 그대로 적용된다. 포인터의 크기가 4byte 이니, 최대다룰 수 있는 메모리의 크기가 4Giga로 제한이 된다. 물론 여러분이 64bit 컴퓨터운영체제 그리고 컴파일러를 사용한다면 테라 byte급의 데이터를 다룰 수 있다. 64bit 컴퓨터가 대용량 데이터 처리에 유리하다는 얘기가 나오는 이유다.

포인터는 이쯤에서 끝내도록 하겠다. 포인터에 대한 자세한 내용은 따로 한장 정도를 할애해서 자세히 다루도록 하겠다.

3 type casting - 형변환

아래 프로그램을 컴파일 후 실행시켜 보자.
#include <stdio.h> 
 
int main(int argc, char **argv) 
{ 
  unsigned char ch = 'c'; 
 
  printf("%c\n", ch); 
  ch = ch+1; 
  printf("%c\n", ch); 
  ch = ch+1; 
  printf("%c\n", ch); 
} 
 
결과로 c d e 가 출력될 것이다.

이상하군. 분명히 두개는 서로 다른 데이터 타입인데, 더하기가 되는군 ?

앞서 언급했지만, 데이터형이란 표현방식에 따른 것일 뿐이다. 컴퓨터 입장에서는 모두가 비트일 뿐이다. 즉 ch+1은 컴퓨터 입장에서 다음과 같이 계산이 된다.
                               0110 0011   = 'a' 
 0000 0000 0000 0000 0000 0000 0000 0001   = '1' 
 ========================== 
 0000 0000 0110 0100   = 'b' 
 
만약 printf("%d\n" ch+1) 을 한다면, 100 이 출력될 것이다. 비트패턴을 숫자로 표현하도록 표현방식을 바꿨기 때문이다.

이렇게 모두 동일한 비트일 뿐임으로 서로 다른 타입간의 계산이 가능해진다. 그러나 이건 어디까지나 가능하다 일뿐 실제로는 의도하지 않는 다양한 문제가 발생할 수 있다.

  • 데이터타입의 크기에 따른 문제
    데이터 타입은 서로 다른 크기를 가지고 있다. 위의 코드에서 ch 에 1000을 더하면 어떻게 될까. 1099가 나오길 예상할 수 있겠지만 ch의 테이터 타입인 char는 1byte의 크기로 255까지만 표현이 가능하다. 때문에 데이터 저장공간을 초과하게 될 것이다. 실제로는 1byte의 상위 비트는 버려지게 된다. 고로 0-255까지의 값이 출력이 될것이다. 뭐.. 값이 255를 초과하지 않는 범위에서 연산을 한다면 문제가 없기는 하겠지만 실수로 문제가 발생할 소지가 다분하다.

    다른 데이터 타입끼리라도 주의해서 프로그래밍을 하면 문제 없겠지만 가능하면, 타입을 맞추어서 계산을 하는게 좋다.

  • signed bit 문제
    정수형 데이터 타입은 signed bit 를 가지고 있어서 이걸로 양수인지 음수인지를 판단하게 된다. 다음의 코드를 확인해 보자.

    #include <stdio.h> 
     
    int main(int argc, char **argv) 
    { 
      unsigned int i = 100; 
     
      if (i < -10) 
      { 
        printf("Large\n"); 
      } 
    } 
     
    상식적으로 100 은 -10보다 크기 때문에 if문의 블럭은 실행되지 않아야 겠지만, 컴파일해서 실행시켜 보면 블럭문이 실행이 되는걸 알 수 있다. 이는 i 가 unsigned 형으로 -10 을 unsigned 형으로 보고 비교를 하기 때문이다. -10이 unsigned 형이 되면 4,294,967,285 으로 표현이 된다. 이를 이해하기 위해서는 2의 보수를 통한 singed 데이터 처리에 대해서 알고 있어야 하는데, 5장 데이터와 비트문서를 읽어보기 바란다.

    이 문제를 해결하기 위해서 형변환을 수행한다. 위의 프로그램의 경우 문제를 피해가는 가장 좋은 방법은 i를 signed 형으로 선언하는게 될것이다. 그러나 불가피 하게 unsigned 형을 고집해야 할 경우가 발생한다. 그때는 casting(형변환)연산자를 통해서 형변환을 하도록 한다.

    if ((signed)i < -10) 
    { 
    ... 
    } 
     
    이제 제대로 되는걸 확인할 수 있을 것이다.

프로그램을 작성할 때는 데이터가 어디에 쓰일 것인지를 명확히 해서, 그에 맞는 데이터 타입을 지정해 줘야 한다. 그렇지만 사람이다 보니, 위에서 처럼 사소한 실수를 하기도 한다. 문제는 이런 프로그램도 문제 없이 컴파일이 된다는 것이다. 결국 프로그램이 실행하는 도중에 문제를 일으키게 될 것이다. 다행히 gcc 컴파일러는 컴파일 옵션을 통해서 저러한 문제를 사전에 잡아낼 수 있게 하고 있다. 위 프로그램을 type.c로 저장하고 아래와 같은 옵션을 주고 컴파일 해보자.
# gcc -W -Wall -o type type.c 
... 
type.c:7: warning: comparison between signed and unsigned 
... 
 
comparison between signed and unsigned와 같은 경고메시지를 출력함을 알 수 있다. 자세한 내용은 리팩토링 : 모든 경고메시지를 제거하라 문서를 읽어보기 바란다. 아직 읽기 버겁다면, 그냥 대충 저런게 있나보다 하는 수준에서 읽어두어도 도움이 될 것이다.

4 문자와 문자열 표현

데이터는 크게 문자와 숫자로 이루어져 있음을 알고 있다. 그런데 정작 문자문자열을 표현하기 위한 데이터 타입을 다룬거 같지를 않다. 이에 대해서 얘기해 보고자 한다.

C 언어에서 문자를 위한 데이터 타입으로는 char 데이터 타입을 사용한다. char 는 1byte 256의 크기를 가지는데, 1byte 문자권의 영어와 수십개의 특수문자를 충분히 표한할 수 있는바 char를 문자를 저장하기 위한 데이터 타입으로 사용하고 있다.

컴퓨터에서 표현되는 문자는 0에서 255까지의 각 크기에 대응되는 문자들이 표준으로 정의되어 있다. 각 값에 대응되는 문자는 ASCII 테이블로 정리되어 있다.

http://www.joinc.co.kr/albums/album01/age.gif

http://www.joinc.co.kr/albums/album01/agf.gif

그러나 ASCII 테이블만 가지고는 일본어, 한글, 중국어와 같은 2byte 문자는 표현할 수 없다. 2byte 문자는 char를 2개 이상 사용해서 저장해야 한다.

이제 마지막으로 문자열이 남았다. C는 문자열을 위한 데이터 타입을 가지고 있지 않다. C에서 문자열을 처리하기 위해서는 배열을 사용해야만 한다. 이것은 원시 데이터 타입을 여러개를 포함하고 있는 데이터 구조다. 예를 들어 문자열은 char를 여러개 포함할 수 있는 데이터 구조를 이용하면 표현할 수 있을 것이다. 배열은 다음장에서 자세히 다루도록 하겠다.

5 typedef 를 이용한 타입 재정의

데이터 타입은 타입에 맞는 고유한 이름을 가지고 사용하게 된다. 그런데 타입의 이름이 너무 길 경우, 이를 이용해서 프로그램을 작성할 경우 불편한 점이 발생한다. 다음의 경우를 보자.
int main() 
{ 
  unsigned long int a; 
  unsigned long int b; 
  unsigned long long c; 
  ... 
} 
 
사용하는데 문제는 없겠지만, 저렇게 긴 타입의 변수가 여기저기에 생성된다면 가독성이 떨어지는 지저분한 코드가 만들어질 가능성이 있다. 이럴경우 typedef 를 이용해서 기존의 데이터 타입을 다른 짧은 이름으로 재정의 해서 사용할 수 있다. typedef는 다음과 같이 사용할 수 있다.
typedef [원래 데이터 형] [재정의될 데이터 타입의 이름] 
 
typedef를 이용하면, 다음과 같이 코드를 깔끔하게 만들 수 있다.
typedef unsigned long int uint; 
typedef unsigned long long ulint; 
 
int main() 
{ 
    uint a; 
    uint b; 
    ulint c; 
} 
 

6 문제

  1. char 데이터 타입을 이용해서 hello world를 화면에 출력해 보자.
    • char를 여러개 써야함.
    • printf 를 통해서 출력할 수 있음.
  2. char 데이터 타입을 이용해서 hello world를 출력해보자. 단 정수형 숫자를 사용해야 한다.
    • ASCII 테이블을 이용하면 됨.
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

유닉스/리눅스 명령어 레퍼런스


파일 명령어

ls - 디렉토리 목록보기
ls -al - 숨은 파일까지 정렬된 형태로 보기
cd dir - dir 디렉토리로 이동
cd - home 디렉토리로 이동
pwd - 현재 위치한 디렉토리 보여주기
mkdir dir - dir라는 디렉토리 만들기
rm file - file을 지우기
rm -r dir - dir 디렉토리를 지우기
rm -f file - 강제로 file 삭제
rm -rf dir - dir 디렉토리와 디렉토리 아래에 있는 모든 파일 삭제
cp file1 file2 - file1을 file2라는 이름으로 복사
cp -r dir1 dir2 - dir1 디렉토리에 있는 것들을 dir2 디렉토리로 복사; dir2가 존재하지 않는다면 만듬
mv file1 file2 - file1을 file2로 이름을 바꾸거나 옮김,
file2가 디렉토리로 존재한다면 file1을 file2 디렉토리로 옮김
ln -s file link - file로 연결된 link라는 심볼릭 링크를만듬
touch file - file을 생성하거나 업데이트
cat > file - 입력을 file로 저장
more file - file의 내용을 출력
head file - file의 첫 10줄을 출력
tail file - file의 마지막 10줄을 출력
tail -f file - file에 추가되는 내용을 출력,마지막10줄부터 출력함


프로세스 관리
ps - 현재 활성화된 프로세스 보여주기
top - 실행중인 모든 프로세스 보여주기
kill pid-프로세스id pid를 종료
killall proc - proc로 시작하는 모든 프로세스 종료
bg - 정지되있거나 화면에서 안보이게 실행중인 프로세스 보여주기; 정지된 프로세스를 화면에 출력하지 않고 계속 진행하기
fg - 화면에 보이지 않게 작동하던 작업 중 최근의 것을 화면에출력하면서 작동시키기
fg n - 화면에 보이지 않게 작동하던 작업 중 n 번째 작업을 화면에 출력하면서 작동시키기


파일 퍼미션
chmod 숫자 file - file의 퍼미션값을 숫자로 바꿈. 숫자는 3자리이며 첫 번째는 소유자,두 번째는 그룹,
세 번째는 익명의권한을 더해서 나타냄.
파일 퍼미션
chmod 숫자 file - file의 퍼미션값을 숫자로 바꿈. 숫자는 3자리이며 첫 번째는 소유자,두 번째는 그룹, 세 번째는 익명의
권한을 더해서 나타냄.

SSH
ssh user@host - user로 host에 접속
ssh -p 포트넘버 user@host - host의 지정한 포트넘버에
user로 접속
ssh-copy-id user@host-사용자명,암호를 입력하지 않고
로그인 할 수 있도록 ssh key를 복사


검색
grep pattern files - file안의 pattern을 찾기
grep -r pattern dir - dir 디렉토리 안에서 재귀적으로pattern 찾기
command | grep pattern - command 명령의 출력에서pattern을 찾는다
locate file - 파일을 찾음


시스템 정보보기
date - 현재 날짜와 시각을 출력
cal - 이번달 달력을 출력
uptime - 현재 기동시간을 출력
w - 온라인인 사용자를 출력
whoami - 어느 사용자로 로그인 하였는지 출력
finger user -user에 관한 정보 출력
uname -a - 커널 정보 출력
cat /proc/cpuinfo - cpu 정보 출력
cat /proc/meminfo - 메모리 정보 출력
man command - command에 대한 매뉴얼 출력
df - 디스크 사용량 출력
du - 디렉토리 사용량 출력
free - 메모리와 스왑 정보 출력
whereis app - app를 실행가능한 위치 출력
which app - app가 기본으로 실행되는 곳을 보여줌


압축
tar cf file.tar files - files들을 포함한 file.tar를 만듬
tar xf file.tar - file.tar을 압축해제
tar czf file.tar.gz files - Gzip 압축을 사용한 압축
tar zxf file.tar.gz - Gzip을 이용해 압축해제
tar cjf file.tar.bz2 - Bzip2 압축을 사용한 압축
tar xjf file.tar.bz2 - Bzip2 압축을 사용한 압축해제
gzip file - file을 압축해서 file.gz로 이름변경
gzip -d file.gz - file.gz를 fiel로 압축해제


네트워크
ping host - host에 핑을 보내 결과 출력
whois domain - domain에 대한 whois 정보 출력
dig domain - domain에 대한 DNS 정보를 출력
dig -x host - 호스트까지의 경로를 되찾아가기


설치
소스로부터 설치
./configure
make
make install
dpkg -i pkg.deb - 패키지 설치(Debian)
rpm -Uvh pkg.rpm - 패키지 설치(RPM)


단축키
Ctrl+C - 현재 명령의 실행을 강제로 마침
Ctrl+Z-현재 명령을 멈춤,fg를 이용해서 계속해서 화면에서 보
이도록 실행하거나 bg 를 이용해서 안보인채 계속 실행
Ctrl+D-현 세션에서 로그 아웃,exit와 비슷
Ctrl+W - 현재 라인에서 한 단어 삭제
Ctrl+U - 현재 줄 전체 삭제
Ctrl+R - 최근 입력한 명령어 보여주기
!! - 마지막 명령어 반복실행
exit - 현재 세션에서 로그 아웃

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

SIGACTION

Section: Linux Programmer's Manual (2)
Updated: 8 May 1999
Index
Return to Main Contents
 

이름

sigaction, sigprocmask, sigpending, sigsuspend - POSIX 시그널 처리 함수  

사용법

#include <signal.h>

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

int sigpending(sigset_t *set);

int sigsuspend(const sigset_t *mask);  

설명

sigaction 시스템 호출은 특정 시그널이 수신되었을 때, 프로세스가 취할 액션을 변경하는데 사용된다.

signum 는 시그널을 가리키며, SIGKILLSIGSTOP 를 제외한 모든 시그널 값이 될 수 있다.

act 가 null이 아닐 때, 시그널 signum 에 대한 새로운 액션은 act 가 되며, oldact 이 null이 아닐 때, 기존의 액션은 oldact 에 저장된다.

sigaction 구조는 다음과 같이 정의된다.


struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
}

아키텍쳐에 따라 공용체로 되어 있기도 하므로, sa_handlersa_sigaction 을 모두 지정하지 말라.

sa_restorer 요소는 쓰이지 않으며, 사용되어서는 안된다. POSIX는 sa_restorer 요소를 갖지 않는다.

sa_handlersignum 시그널이 오면 실행되는 액션을 명시하며, 디폴트 액션을 취하라는 SIG_DFL , 시그널을 무시하라는 SIG_IGN , 시그널을 처리하는 특정 함수에 대한 포인터 중의 하나가 될 수 있다. 세번째의 경우 시그널 처리 함수는 시그널 번호만을 유일한 인수로 갖는다.

sa_sigaction 또한 signum 시그널과 연결된 액션을 명시한다. 처리 함수는 시그널 번호를 첫번째 인수로, siginfo_t 에 대한 포인터를 두번째 인수로, 그리고 void형 포인터로 캐스트된 ucontext_t 에 대한 포인터를 세번째 인수로 갖는다.

sa_mask 는 시그널 핸들러의 실행 동안 블록화되어야 하는 시그널의 마스크를 제공한다. 또한, SA_NODEFER 또는 SA_NOMASK 이 사용되지 않으면, 핸들러를 시동시켰던 그 시그널도 블록화된다.

sa_flags 는 시그널 처리 프로세스의 행위를 변경시키는 일련의 플래그들을 명시한다. 이는 bitwise 혹은 플래그는 아래 플래그 들의 0개 이상의 OR 비트 연산으로 만들어진다.


SA_NOCLDSTOP
signumSIGCHLD 이면 자식 프로세스가 중지되어도 통지를 받지 않는다. (즉, 자식 프로세스들이 SIGSTOP, SIGTSTP, SIGTTIN , SIGTTOU 중 하나를 수신할 때)
SA_ONESHOT 또는 SA_RESETHAND
시그널 처리기가 호출되어 한번 실행된 후, 시그널 액션을 원래의 디폴트 액션으로 되돌려 놓는다. (이는 signal(2) 호출에 대한 기본 행위이다.)
SA_RESTART
일부 시스템 호출들이 시그널을 통해 재시작할 수 있도록 함으로서 BSD 시그널과의 호환성을 제공한다.
SA_NOMASK or SA_NODEFER
어떤 시그널 처리기의 동작동안 그 시그널 자신을 막지 않는다.
SA_SIGINFO
시그널 처리기가 한 개가 아닌 3개의 인수를 취한다. 이 경우, sa_handler 대신에 sa_sigaction 이 설정되어야 한다. (sa_sigaction 필드는 리눅스 2.1.86에서 추가되었다.)

sa_sigactionsiginfo_t 변수는 다음의 요소들을 갖는 구조체(struct)이다.


siginfo_t {
int si_signo; /* 시그널 넘버 */
int si_errno; /* errno 값 */
int si_code; /* 시그널 코드 */
pid_t si_pid; /* 프로세스 ID 보내기 */
uid_t si_uid; /* 프로세스를 전송하는 실제 사용자 ID */
int si_status; /* Exit 값 또는 시그널 */
clock_t si_utime; /* 소모된 사용자 시간 */
clock_t si_stime; /* 소모된 시스템 시간 */
sigval_t si_value; /* 시그널 값 */
int si_int; /* POSIX.1b 시그널 */
void * si_ptr; /* POSIX.1b 시그널 */
void * si_addr; /* 실패를 초래한 메모리 위치 */
int si_band; /* 밴드 이벤트 */
int si_fd; /* 파일 기술자 */
}

si_signo, si_errno 그리고 si_code 는 모든 시그널에 대해 정의되었다. kill(2), POSIX.1b 시그널과 SIGCHLD은 si_pidsi_uid 을 채운다. SIGCHLD 은 또한 si_status, si_utime, si_stime 을 채운다. si_int 그리고 si_ptr 는 POSIX.1b 시그널의 송신자에 의해 명시된다. 좀더 자세한 사항을 보려면, sigqueue(2) 을 참조하라. SIGILL, SIGFPE, SIGSEGV 그리고 SIGBUS은 si_addr 를 오류의 주소로 채운다. SIGPOLL 은 si_bandsi_fd 를 채운다. si_code 는 왜 시그널이 보내졌는지에 대해 지시한다. 이는 bitmask가 아닌 값이다. 나올 수 있는 모든 시그널 값은 이 테이블에 나열되어 있다.

si_code
Value Signal origin
SI_USER kill, sigsend or raise
SI_KERNEL The kernel
SI_QUEUE sigqueue
SI_TIMER timer expired
SI_MESGQ mesq state changed
SI_ASYNCIO AIO completed
SI_SIGIO queued SIGIO
SIGILL
ILL_ILLOPC illegal opcode
ILL_ILLOPN illegal operand
ILL_ILLADR illegal addressing mode
ILL_ILLTRP illegal trap
ILL_PRVOPC privileged opcode
ILL_PRVREG privileged register
ILL_COPROC coprocessor error
ILL_BADSTK internal stack error
SIGFPE
FPE_INTDIV integer divide by zero
FPE_INTOVF integer overflow
FPE_FLTDIV floating point divide by zero
FPE_FLTOVF floating point overflow
FPE_FLTUND floating point underflow
FPE_FLTRES floating point inexact result
FPE_FLTINV floating point invalid operation
FPE_FLTSUB subscript out of range
SIGSEGV
SEGV_MAPERR address not mapped to object
SEGV_ACCERR invalid permissions for mapped object
SIGBUS
BUS_ADRALN invalid address alignment
BUS_ADRERR non-existant physical address
BUS_OBJERR object specific hardware error
SIGTRAP
TRAP_BRKPT process breakpoint
TRAP_TRACE process trace trap
SIGCHLD
CLD_EXITED child has exited
CLD_KILLED child was killed
CLD_DUMPED child terminated abnormally
CLD_TRAPPED traced child has trapped
CLD_STOPPED child has stopped
CLD_CONTINUED stopped child has continued
SIGPOLL
POLL_IN data input available
POLL_OUT output buffers available
POLL_MSG input message available
POLL_ERR i/o error
POLL_PRI high priority input available
POLL_HUP device disconnected

sigprocmask 호출은 현재 블록화된 시그널들을 변경시키는데 사용된다. 호출은 지정된 how 값에 따라 다르게 동작한다.


SIG_BLOCK
set 인수가 지정한 시그널 집합이 블록시킬 시그널 집합에 더해진다.
SIG_UNBLOCK
set 에 포함된 시그널들이 블록시킬 시그널들의 집합에서 삭제된다. 블록하고 있지 않은 시그널을 삭제하려 해도 괜찮다.
SIG_SETMASK
블록화할 시그널 집합을 set 으로 설정한다.

oldset 이 null이 아닐 때, 기존의 마스크 시그널 집합은 oldset 에 저장된다.

sigpending 호출은 블록화에 막혀 기다리고 있는 시그널을 살펴볼 수 있도록 해준다. 기다리는 시그널들의 시그널 마스크는 set 내에 저장된다.

sigsuspend 호출은 프로세스가 막고 있는 시그널 마스크를 지정한 mask 로 잠시 대체하고, 다음 시그널이 수신될 때까지 프로세스를 중지시킨다.  

반환값

sigaction , sigprocmask , sigpending 는 성공하면 0을 실패하면 -1을 리턴한다. sigsuspend 항상 -1을 리턴한다. 보통 에러 EINTR 과 함께.

에러

EINVAL
지정한 시그널이 부적절하다. 혹은 받을 수 없는 SIGKILL 또는 SIGSTOP 에 대한 액션을 변경하려고 했다.
EFAULT
act, oldact, set 또는 oldset 이 프로세스의 메모리 영역이 아닌 곳을 가리키고 있다.
EINTR
시스템 호출이 인터럽트되었다.
 

주의

sigprocmask 호출로 SIGKILL 또는 SIGSTOP 을 블록화할 수 없다. 이런 명령은 무시된다.

POSIX에 따르면, 프로세스가 kill() 또는 raise() 함수가 만들어 낸 것이 아닌 GFPE, SIGILL, 혹은 SIGSEGV를 무시한 이후의 프로세스의 동작은 정의되지 않는다. 정수를 0으로 나눈 결과 또한 정의되지 않는다. 일부 아키텍쳐에선 0으로 나누기가 SIGFPE 시그널을 만들어 낼 것이다. (또한 가장 큰 음의 정수를 -1로 나누어도 SIGFPE를 생성할 수 있다.) 이 시그널을 무시하면 무한 루프를 초래할 수 있다.

POSIX 스펙은 오직 SA_NOCLDSTOP 만을 정의한다 . 다른 sa_flags 의 사용은 이식이 불가능하다.

SA_RESETHAND 플래그는 동일한 이름의 SVr4 플래그와 호환가능하다.

SA_NODEFER 플래그는 커널 1.3.9과 새로운 버전하에서 동일한 이름의 SVr4 플래그와 호환가능하다.

SVr4 호환성을 위한 SA_RESETHANDSA_NODEFER 이름들은 오직 라이브러리 버전 3.0.9 그리고 그 이후의 버전에서만 존재한다.

SA_SIGINFO 플래그는 POSIX.1b에 의해서만 명시된다. 이에 대한 지원은 리눅스 2.2에 추가되었다.

sigaction 현재 시그널 처리기에 쿼리를 하기 위해 널 두번째 인수로 호출될 수 있다. 이를 널 두번째 그리고 세번째 인수들로 이를 호출함으로서 현재 머신에 대한 주어진 시그널이 타당한가를 체크하는데 사용될 수 있다.

시그널 체계 조작에 대한 자세한 정보를 얻으려면, sigsetops(3) 을 참조하라.  

호환

POSIX, SVr4. SVr4 는 EINTR 조건에 대한 문서를 제공하지 않는다.  

관련 항목

kill(1), kill(2), killpg(2), pause(2), raise(3), siginterrupt(3), signal(2), signal(7), sigsetops(3), sigvec(2)  

번역

ASPLINUX <man@asp-linux.co.kr> 2000년 7월 29일
한글 Manpage 프로젝트 (http://man.kldp.org) 2005년 2월 13일

+ Recent posts