= 임베디드 리눅스 실전 가이드 = -- [김도집] [[DateTime(2005-08-24T02:41:25)]] [[TableOfContents]] == 개요 == == 저작권 == 본 문서에 대한 저작권은 김도집에 있으며, 자유롭게 배포가 가능하다. 단, 출처는 명시해야 한다. == 용어 == == 툴체인 == === 라이브러리 === ==== zlib ==== zlib은 널리 사용되는 압축 라이브러리이다. zlib 1.2.2 버전 이하에서는 보안 문제가 있으므로 반드시 최신 버전을 사용하기 권한다. zlib는 다음의 사이트에서 구할 수 있다: [http://www.zlib.net] ===== 빌드 하기 ===== 다음은 zlibc 1.2.3의 빌드 과정이다. 1. ./configure 또는 ./configure --shared 를 실행한다. --shared 옵션은 zlib 라이브러리를 동적으로 만들라는 의미이다. {{{ ./configure --shared --prefix=/usr/local/arm/arm-uclibc-3.3.5 }}} 1. Makefile 을 다음과 같이 수정한다: {{{ CROSS=arm-linux- CC=$(CROSS)gcc LDSHARD=$(CROSS)gcc -shared -Wl,-soname,libz.so.1 CPP=$(CROSS)gcc -E AR=$(CROSS)ar rc RANLIB=$(CROSS)ranlib }}} 1. make install 를 실행하여 빌드된 것을 설치한다. === 사용자 정의 라이브러리 === 사용자가 직접 라이브러리를 만들어 사용할 수 있다. 이럴 때는 라이브러리를 만들어 사용하는 것이 좋다: * 소스 코드를 공개하고 싶지 않을 때 * 동일한 코드를 여러 프로그램에서 반복적으로 사용할 때 라이브러리는 두 가지 타입이 있다. 정적 라이브러리와 동적 라이브러리이다. 정적 라이브러리를 이용하는 경우엔 이 라이브러리를 사용하는 실행 파일에 라이브러리가 포함이 된다. 반면에, 동적 라이브러리를 이용하는 경우엔 실행 파일에 라이브러리가 포함되지 않고 동적으로 라이브러리를 참조하여 사용하게 된다. 즉, 여러 프로그램에서 동일한 라이브러리를 반복적으로 사용하는 경우엔 동적 라이브러리로 생성하여 사용하는 것이 좋다. 그외에도 라이선스 문제도 있는데, 정적 라이브러리의 경우 실행 파일에 라이브러리 자체가 포함되므로 GPL 라이선스의 영향을 받지만, 동적 라이브러리의 경우엔 이러한 문제를 피해갈 수도 있다. ==== 정적 라이브러리 만들기 ==== 정적 라이브러리는 object 파일들을 하나의 파일로 만든 것이라고 보면 된다. ar 이라는 명령을 이용하여 만든다. {{{ ar rcs 정적라이브러리명.a 파일1.o 파일2.o 파일3.o ... }}} 예를 들어 파일1.o, 파일2.o를 libmy.a라는 정적 라이브러리로 만든다면 다음과 같이 만든다: {{{ ar rcs libmy.a 파일1.o 파일2.o }}} ==== 동적 라이브러리 만들기 ==== 동적 라이브러리를 만드는 방법은 다음과 같다: {{{ gcc -shared -Wl,-soname,your_soname \ -o libarary_name file_list library_list }}} 예를 들어 파일1.c, 파일2.c를 libmy.so.1이라는 동적 라이브러리로 만든다면 다음과 같다: {{{ gcc -fPIC -g -c -Wall 파일1.c gcc -fPIC -g -c -Wall 파일2.c gcc -shared -Wl,-soname,libmy.so.1 \ -o libmy.so.1.0.1 파일1.o 파일2.o -lc }}} == 부트로더 == == 커널 == === 모듈 프로그래밍 === ==== Makefile 만들기 ==== 커널 2.6에서는 외부 모듈의 빌드를 커널 소스 내의 빌드 시스템을 이용한다. 예를 들어 foobar.ko 라는 커널 모듈을 만든다고 하면 다음과 같이 Makefile 파일을 만든다. {{{ ifneq ($(KERNELRELEASE),) #call from kernel build system foobar-objs := foo_1.o foo_2.o obj-m := foobar.o else KERNELDIR ?= /YOUR/KERNEL/SOURCE/PATH PWD := $(shell pwd) modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules endif clean: rm -rf *.o *~ core .depend .*.cmd .*.o.d *.ko *.mod.c .tmp_versions }}} /!\ Makefile에서 실행 명령 행 첫 공백은 '''TAB'''으로 띄워야 한다. 공백 문자가 아니다 === 프로세스 === 관련 함수는 에 정의되어 있다. pid를 갖는 프로세스(태스크)의 디스크립터를 가져오고자 할 때는 '''find_task_by_pid()'''함수를 사용한다. 사용 예는 다음과 같다: {{{#!vim C struct task_struct *p; read_lock(&tasklist_lock); p = find_task_by_pid(pid); read_unlock(&tasklist_lock); }}} === 비동기 === ==== 시그널 ==== 커널에서 호출한 프로세스로 시그널을 전달할 수 있다. 시그널 호출 시 전달되는 데이터는 siginfo 구조체로 정의되어 있다. 다음은 include/asm-generic/siginfo.h에 정의되어 있는 내용 중 일부이다. {{{#!vim C typedef struct siginfo { int si_signo; int si_errno; int si_code; union { ... } _sifields; } siginfo_t; #define si_pid _sifields._kill._pid ... #define si_int _sifields._rt._sigval.sival_int ... }}} 사용할 수 있는 함수들은 다음과 같다: ||recalc_sigpendign|| || ||dequeue_signal|| || ||flush_signals|| || ||force_sig|| || ||kill_pg|| || ||kill_proc||호출한 프로세스(pid)에 signal을 보낸다|| ||ptrace_notify|| || ||send_sig|| || ||send_sig_info||siginfo의 내용을 보내려면 이 함수를 이용한다|| ||sigprocmask|| || ||block_all_signals|| || ||unblock_all_signals|| || 시그널 함수 중 kill_proc()함수는 다음과 같이 이용할 수 있다. {{{#!vim C kill_proc(cpid, SIGINT, 1); }}} 프로세스(cpid)에 SIGUSR2 시그널을 보낸다. 보내는 것에 실패한다면 SIGINT를 보내게 된다. '''send_sig_info()'''를 이용하는 경우는 다음과 같다: {{{#!vim C struct siginfo info; info.si_signo = SIGUSR2; info.si_errno = 0; info.si_code = SI_QUEUE; info.si_int = flags; if (send_sig_info(SIGUSR2, &info, p)) send_sig_info(SIGINT, (struct siginfo *)1, p); }}} 여기서 p는 struct task_struct형의 포인터이다. === 메모리 === ==== IO 메모리 ==== IO 메모리에 관련된 인터페이스는 에 정의되어 있다. IO를 위한 메모리를 할당은 다음과 같이 한다. {{{#!vim C char *name = "MyIO"; if (!request_mem_region(phys, size, name)) return -EBUSY; virt = ioremap(phys, size); }}} * phys : 물리 메모리 시작 번지 * size : IO 메모리로 할당할 메모리의 크기 할당 받은 IO 메모리를 해제하는 경우는 다음과 같다. {{{#!vim C release_mem_region(phys, size); iounmap((void *)virt); }}} * virt : 해제할 가상 메모리 시작 번지 == 응용 프로그램 == === Makefile (skelecton) === 다음은 Makefile을 작성할 때 참조할 만한 간단한 예제이다: {{{ #CROSS := arm-linux- CC := $(CROSS)gcc LIBS := -lpthread INCDIR := -I./ CFLAGS = -Wall -O2 $(INCDIR) BIN := dj_run2 OBJS := dj_event2.o dj_run2.o SRCS := $(OBJS:.o=.c) all: $(BIN) $(BIN): $(OBJS) $(CC) $(CFLAGS) $(LIBS) -o $@ $^ %.o:%.c $(CC) $(CFLAGS) -c -o $@ $< clean: rm -f *.o $(BIN) tags: find ./ -name "*.[ch]" | ctags -L- dep: gccmakedep -- $(CFLAGS) -- $(SRCS) }}} === 프로세스 === === 시그널 === 간단하게 다음의 함수를 이용하여 시그널을 다룰 수 있다. {{{#!vim C #include void (*signal(int sig, void (*func)(int)))(int); }}} /!\ signal 함수는 시그널 핸들러가 수행된 후 기본 상태로 복구되어, 등록한 시그널 핸들러를 반복적으로 처리하고자 할 때는 적합하지 않다. 안정적인 시그널 인터페이스는 다음을 사용한다: {{{#!vim C #include int sigaction(int sig, const struct sigaction *act, struct sigaction *oact); }}} sigaction 구조체는 최소한 다음의 멤버를 갖는다: {{{#!vim C void (*) (int) sa_handler sigset_t sa_mask int sa_flags }}} 여기서 sa_handler는 함수가 올 수도 있고 SIG_DFL 또는 SIG_IGN이 올 수도 있다. == 참고 == * [wiki:임베디드리눅스레퍼런스 임베디드 리눅스 레퍼런스]