FSTWikiDiff for 1.64 EmbeddedLinuxGuide
Login:
Password:
Join
E D R S I H P RSS
FrontPage|FindPage|TitleIndex|RecentChanges

Difference between r1.64 and the current

@@ -6,9 +6,184 @@
== 개요 ==
== 저작권 ==
본 문서에 대한 저작권은 김도집에 있으며, 자유롭게 배포가 가능하다. 단, 출처는 명시해야 한다.
== 용어 개발 도구 == 
=== scratchbox === 
http://www.scratchbox.org

scratchbox는 다른 toolchains과는 다소 다른 환경을 제공한다.  
 
이는 호스트와는 독립적인 cross 개발 환경을 제공한다. 
== == 설치 on the debian ==== 
sources.list에 다음을 추가한다. 
{{{ 
# for scratchbox 
deb http://scratchbox.org/debian ./ 
}}} 
==== scratchbox에서 toolchain 만들기 ==== 
scratchbox에서 toolchains을 직접 빌드 하기 위해서는 sb-toolchains를 사용한다. 
 
다음은 sb-toolchains-1.0.3을 기준으로 설명한다. 
 
meta/toolchain/arm-gcc3.3-glibc.conf에 toolchains에 대한 configuration이 정의되어 있다. 
 
'''툴체인 빌드:''' 
make CONFIG=meta/toolchain/xxx.conf 
 
'''툴체인 clean:''' 
make clean-toolchain CONFIG=meta/toolchain/xxx.conf 
 
==== scratchbox에서 kegel의 crosstool로 만들기 ==== 
 
1. darcs get --set-scripts-executable http://scratchbox.org/repos/1.2/sb-toolchains 
1. wget http://kegel.com/crosstool/crosstool-0.42.tar.gz 
1. tar xzf crosstool-0.42.tar.gz 
1. cd sb-toolchains 
1. ./import-crosstool.sh ../crosstool-0.42/ 
1. make clean-toolchain CONFIG=meta/toolchain/<myconfig> 
1. make CONFIG=meta/toolchain/<myconfig> all-sums 
1. make CONFIG=meta/toolchain/<myconfig> 
 
/!\ ''make clean-toolchain CONFIG=meta/toolchain/<myconfig>'' 는 선택으로, 이전에 빌드했던 적이 있다면 실행해 주는 것이 좋다. [[BR]] 
/!\ ''LIBC_APP_DIR'' <= glibc-2.3.4 또는 이후 버전에서만 <myconfig>에서 정의해야 한다. 
 
xscale용 툴체인을 빌드 하는 경우 -mcpu=iwmmxt를 지정하는데, ARM Emulator인 QEMU에서 이를 인식하지 못하는 문제가 있다. 
=== uclibc를 이용한 toolchain 만들기 === 
[http://www.uclibc.org]를 참고한다. 
 
uClibc와 관련된 사이트는 다음과 같다. 
* NPTL Support for uClibc [http://www.realitydiluted.com/nptl-uclibc/] 
=== kegel의 toolchains 만들기 === 
/!\ crosstool 스크립트는 계속 업데이트 되고 있으므로 http://www.kegel.com/crosstool 을 확인하기 바란다. 
 
kegel의 스크립트를 이용하여 툴체인을 만드는 방법은 다음과 같다: 
1. crosstool을 다운 받는다. wget [http://kegel.com/crosstool/crosstool-0.38.tar.gz] 
1. 압축을 푼다. {{{tar zxvf crosstool-0.38.tar.gz}}} 
1. {{{demo-arm-softfloat.sh}}}을 수정한다. gcc-3.3.3 줄의 주석처리를 없앤다. 
1. {{{arm-softfloat.dat}}}을 수정한다. {{{Set TARGET=arm-linux}}} 
1. 빌드 디렉토리를 만든다. {{{sudo mkdir /opt; sudo chown $USER /opt}}} 
1. {{{unset LD_LIBRARY_PATH}}}를 실행한다. 
1. tool을 실행한다. {{{sh demo-arm-softfloat.sh}}} 
1. 빌드 될 동안 커피 한잔 마시면서 기다리면 된다. 
 
만약, 빌드 할 디렉토리를 변경하고자 한다면 {{{demo-arm-softfloat.sh}}}를 수정하고 빌드 디렉토리를 만들 때 수정된 디렉토리로 만들면 된다. 
 
혹, 다음과 같은 에러가 난다면 
{{{ 
atic hello.c -o arm-linux-hello-static 
hello.c: In function `main': 
hello.c:4: error: `PATH_MAX' undeclared (first use in this function) 
hello.c:4: error: (Each undeclared identifier is reported only once 
hello.c:4: error: for each function it appears in.) 
}}} 
testhello.sh에서 ''#include <limits.h>''의 아래에 ''#include <linux/litmits.h>''를 추가한다. 
=== xscale toolchains 빌드 분석 === 
xscale용 toolchains은 ftp://ftp.arm.linux.org.uk에서 구할 수 있다. 
 
xsclae용 toolchains은 앞서 소개한 kegel의 스크립트를 이용하여 만들어진다. 
 
빌드시 사용되는 스크립트: 
* arm-iwmmxt.dat 
* gcc-3.4.3-glibc-2.3.2.dat 
* all.sh 
dat 파일은 all.sh에서 사용하는 환경변수를 지정하게 된다. 
 
arm-iwmmxt.dat의 내용은 다음과 같다. 
{{{#!vim sh 
KERNELCONFIG=`pwd`/arm.config 
TARGET=arm-linux 
TARGET_CFLAGS="-O" 
GCC_EXTRA_CONFIG="--with-cpu=iwmmxt --enable-cxx-flags=-mcpu=iwmmxt" 
GLIBC_EXTRA_CONFIG="--without-fp" 
GLIBC_EXTRA_CC_ARGS="-finline-limit=10000" 
}}} 
gcc-3.4.3-glibc-2.3.2.dat 파일을 보면 toolchains에서 사용하는 소스를 확인할 수 있는데, 그들 소스는 다음과 같다. 
* driscoll-binutils-2.14.90 
* gcc-3.4.3 
* glibc-2.3.2 
* linux-2.6.9 
* glibc-linuxthreads-2.3.2 
 
패치 파일은 다음과 같다. 
* driscoll-binutils-2.14.90 
* gcc-3.4.3 
* gcc-3.4.3-arm-softfloat.patch 
* glibc-2.3.2 
* glibc-2.3.2-memxxx-strxxx.patch 
* glibc-2.3.2.patch 
* glibc-stat.patch 
* glibc-vfp.patch 
* glibc-linuxthreads-2.3.2 
* glibc-linuxthreads-2.3.2.patch 
* linux-2.6.9 
 
이들 패치는 getandpatch.sh 스크립트를 통해 패치가 이뤄진다. 패치는 -p1 옵션을 사용한다. 
=== binutiles === 
{{{ 
arm-linux-addr2line arm-linux-ld arm-linux-ranlib arm-linux-strip 
arm-linux-ar arm-linux-nm arm-linux-readelf  
arm-linux-as arm-linux-objcopy arm-linux-size  
arm-linux-c++filt arm-linux-objdump arm-linux-strings 
}}} 
 
{{{ 
Invocation line includes a switch not recognized by the base assembler. 
See if it's a processor-specific option. 
 
This routine is somewhat complicated by the need for backwards 
compatibility (since older releases of gcc can't be changed). 
The new options try to make the interface as compatible as 
possible with GCC. 
 
New options (supported) are: 
 
-mcpu=<cpu name> Assemble for selected processor 
-march=<architecture name> Assemble for selected architecture 
-mfpu=<fpu architecture> Assemble for selected FPU. 
-EB/-mbig-endian Big-endian 
-EL/-mlittle-endian Little-endian 
-k Generate PIC code 
-mthumb Start in Thumb mode 
-mthumb-interwork Code supports ARM/Thumb interworking 
 
For now we will also provide support for: 
 
-mapcs-32 32-bit Program counter 
-mapcs-26 26-bit Program counter 
-macps-float Floats passed in FP registers 
-mapcs-reentrant Reentrant code 
-matpcs 
(sometime these will probably be replaced with -mapcs=<list of options> 
and -matpcs=<list of options>) 
 
The remaining options are only supported for back-wards compatibility. 
Cpu variants, the arm part is optional: 
-m[arm]1 Currently not supported. 
-m[arm]2, -m[arm]250 Arm 2 and Arm 250 processor 
-m[arm]3 Arm 3 processor 
-m[arm]6[xx], Arm 6 processors 
-m[arm]7[xx][t][[d]m] Arm 7 processors 
-m[arm]8[10] Arm 8 processors 
-m[arm]9[20][tdmi] Arm 9 processors 
-mstrongarm[110[0]] StrongARM processors 
-mxscale XScale processors 
-m[arm]v[2345[t[e]]] Arm architectures 
-mall All (except the ARM1) 
FP variants: 
-mfpa10, -mfpa11 FPA10 and 11 co-processor instructions 
-mfpe-old (No float load/store multiples) 
-mvfpxd VFP Single precision 
-mvfp All VFP 
-mno-fpu Disable all floating point instructions 
 
The following CPU names are recognized: 
arm1, arm2, arm250, arm3, arm6, arm600, arm610, arm620, 
arm7, arm7m, arm7d, arm7dm, arm7di, arm7dmi, arm70, arm700, 
arm700i, arm710 arm710t, arm720, arm720t, arm740t, arm710c, 
arm7100, arm7500, arm7500fe, arm7tdmi, arm8, arm810, arm9, 
arm920, arm920t, arm940t, arm946, arm966, arm9tdmi, arm9e, 
arm10t arm10e, arm1020t, arm1020e, arm10200e, 
strongarm, strongarm110, strongarm1100, strongarm1110, xscale. 
}}}
=== 라이브러리 ===
==== zlib ====
zlib은 널리 사용되는 압축 라이브러리이다.
@@ -19,7 +194,9 @@

zlib는 다음의 사이트에서 구할 수 있다:
[http://www.zlib.net]
=====  
 
'''빌드 하기 =====:'''
다음은 zlibc 1.2.3의 빌드 과정이다.


@@ -41,12 +218,22 @@
}}}

1. make install 를 실행하여 빌드된 것을 설치한다.
 
==== libjpeg ==== 
{{{#!vim sh 
./configure --prefix=/usr --enabled-shared 
}}} 
==== libpng ==== 
{{{#!vim sh 
./configure --prefix=/usr --enable-shared 
}}}
=== 사용자 정의 라이브러리 ===
사용자가 직접 라이브러리를 만들어 사용할 수 있다. 이럴 때는 라이브러리를 만들어 사용하는 것이 좋다:


* 소스 코드를 공개하고 싶지 않을 때
* 동일한 코드를 여러 프로그램에서 반복적으로 사용할 때
* 업데이트를 용이하게 하고자 할 때


라이브러리는 두 가지 타입이 있다. 정적 라이브러리와 동적 라이브러리이다. 정적 라이브러리를 이용하는 경우엔 이 라이브러리를 사용하는 실행 파일에 라이브러리가 포함이 된다. 반면에, 동적 라이브러리를 이용하는 경우엔 실행 파일에 라이브러리가 포함되지 않고 동적으로 라이브러리를 참조하여 사용하게 된다. 즉, 여러 프로그램에서 동일한 라이브러리를 반복적으로 사용하는 경우엔 동적 라이브러리로 생성하여 사용하는 것이 좋다.
@@ -62,7 +249,9 @@
예를 들어 파일1.o, 파일2.o를 libmy.a라는 정적 라이브러리로 만든다면 다음과 같이 만든다:
{{{
ar rcs libmy.a 파일1.o 파일2.o
}}} 
 
==== 동적 라이브러리 만들기 ====
동적 라이브러리를 만드는 방법은 다음과 같다:
{{{
gcc -shared -Wl,-soname,your_soname \
@@ -76,13 +265,152 @@
gcc -shared -Wl,-soname,libmy.so.1 \
-o libmy.so.1.0.1 파일1.o 파일2.o -lc
}}}
==== 라이브러리 사용하기 ==== 
사용자가 만든 라이브러리는 사용자 임의의 디렉토리에 위치한다. 따라서 표준 라이브러리 경로에서 해당 라이브러리를 찾지 못해, 라이브러리를 이용하여 컴파일 하고자 할 때 라이브러리를 찾을 수 없다는 에러를 보게 될 것이다. 
 
 
컴파일러를 이용하여 컴파일 할 때는 -L과 -l를 지정하여 라이브러리를 경로를 지정해 줄 수 있다: 
||-L{경로}||라이브러리를 찾을 경로를 추가해 준다|| 
||-l{lib를뺀라이브러리이름}||라이브러리 이름|| 
 
 
예를 들어 /home/foo/bar/라는 디렉토리 아래에 libmy.a가 있다면 다음과 같이 컴파일 옵션을 줄 수 있다. 
{{{ 
gcc -Wall -O2 -o hello hello.c -L/home/foo/bar -lmy 
}}} 
 
동적 라이브러리의 경우엔 mylib.so.1.0.1로 만들었다면 다음과 같이 심볼릭 링크를 만들어주어야 한다. 
{{{ 
ln -s mylib.so.1.0.1 mylib.so.1 
ln -s mylib.so.1 mylib.so 
}}} 
libmy.so를 찾을 수 없다면 경로를 명시해도 라이브러리를 찾지 못할 것이다. 
 
 
동적 라이브러리를 이용하여 컴파일 경우엔 실행 파일을 실행 시에 라이브러리를 필요로 한다. LD_LIBRARY_PATH라는 환경 변수에 라이브러리 경로를 추가하거나 다음과 같이 임시로 지정할 수도 있다: 
{{{ 
LD_LIBRARY_PATH={라이브러리경로}:${LD_LIBRARY_PATH} 실행파일 
}}}=== Autotools === 
* http://wiki.kldp.org/wiki.php/DocbookSgml/Autotools-KLDP 
=== Make/Makefile === 
빌드시에 일괄 처리를 위한 목적으로 make 를 사용한다. 반복적인 작업을 Makefile에 명시된 절차에 따라 처리한다. 
 
make를 대체하는 도구로는 SCons라는 것도 있다. 개인적인 소견으로는 make보다는 SCons가 더 사용하기 편리하다. 
==== Makefile 일괄 처리 ==== 
{{{#!vim make 
DIRS = foo bar 
 
all: 
for i in $(DIRS); do make -C $$i; done 
 
clean: 
for i in $(DIRS); do make -C $$i clean; done 
}}} 
=== SCons === 
Software construction tool이다. 자세한 것은 [http://www.scons.org]를 참고하라. 
 
[wiki:/scons 좀 더 자세히 둘러보기] 
 
=== vim === 
리눅스에서 가장 널리 사용되는 편집기 중 하나이다. 키보드 상에 손을 올려 놓기만 하면 모든 것을 처리할 수 있는 아주 막강한 도구이다. 
 
처음 익히는데, 다소 어려움이 있을지 모르지만 익히고 나면 다른 편집기를 쓴다는 것이 매우 불편하다는 것을 알게 된다.  
=== ctags === 
vim에서는 tags를 지원한다. ctags를 통해 만들어진 tag 파일을 참조하여 소스 내에서 쉽게 탐색을 할 수 있다. 
 
tags 파일의 생성은 다음가 같이 한다. 
{{{ 
find ./ -name "*.[chS]" | ctags -L- 
find ./ -name "*.cc" | ctags -a -L- 
}}} 
 
vim에서 사용하는 tag 관련 명령으로는 :ts, :ta, :set tags=tags,../tags 등이 있다. 
||'''명령'''||'''설명'''|| 
||:ts||찾고자 하는 index가 여럿일 때 그 목록을 보여준다|| 
||:ta index명||tag파일에서 index명을 찾는다|| 
||:set tags=tags,../tags,../../tags||tag 파일의 위치를 지정한다|| 
 
그외 vim을 실행할 때 '''vim -t index명''' 을 통해 직접 해당 위치로 찾아 들어갈 수 있다. 
 
=== 디버깅 === 
==== gdb ==== 
{{{ 
Target Host 
+--------------+ +-------------+ 
| gdbserver +------------+ gdb | 
+--------------+ +-------------+ 
}}} 
'''On the Target machine''' 
{{{target> gdbserver comm program [ args ... ]}}} 
{{{target> gdbserver /dev/ttyS1 emacs foo.txt}}}[[BR]] 
{{{target> gdbserver :2345 emacs foo.txt}}}[[BR]] 
경우에 따라서는 현재 실행 중인 프로그램을 gdbserver와 연계 시킬 수 있다.[[BR]] 
{{{target> gdbserver comm --attache program [ argss ... ]}}} 
'''On the GDB host machine''' 
{{{(gdb) target remote /dev/ttyS1}}}[[BR]] 
{{{(gdb) target remote the-target:2345}}} 
'''Dynamic Link''' 
{{{(gdb) set solib-absolute-prefix /dev/null}}}[[BR]] 
{{{(gdb) set solib-search-path /.../my_libs:/.../more_libs}}} 
 
===== Cross-GDB compilation ===== 
1. GDB 빌드 (생략) 
1. GDB 서버 빌드 
1. make realclean 
1. ./configure --host=arm-linux-elf 
--target=arm-linux-elf 
1. cd gdb 
1. mv gdbserver gdbserver.orig 
1. tar xvzf gdbserver-arm.src.tgz 
1. cd gdbserver 
1. make CC=arm-linux-gcc 
 
===== gdb 사용 ===== 
'''메모리 dump''' 
{{{ 
x/20b 0xc0001000 
x/20h 0xc0001000 
x/20w 0xc0001000 
}}} 
==== 사용자 모드 디버깅 ==== 
{{{arm_linux_objdump -xDSl apps > apps.dump}}} 
 
==== 커너 모드 디버깅 ==== 
커널 모드 디버깅에 대한 것은 [KernelApi7]를 참고하자. 
===== kgdb ===== 
===== kdb ===== 
=== Doxygen === 
[http://www.pie.pe.kr/cgi-bin/moin.cgi/Doxygen] 
 
=== 테스트 === 
* Linux Test Project [http://ltp.sourceforge.net/] 
* Open POSIX Test Suite [http://posixtest.sourceforge.net/]
== 부트로더 ==
데스크탑 PC에서 리눅스를 사용해 본 사용자라면 lilo나 grub과 같은 부트로더를 들어봤을 것이다. 이는 하드 디스크의 MBR 또는 부트 영역에서 있어 리눅스 커널을 로딩하는 역할을 한다. 
 
임베디드에서는 이와 같은 부트로더가 있지만, 몇가지 다른 점이 있다. 
* 임베디드에서는 바이오스가 없다. 
* 임베디드에서는 하드 디스크가 아닌 플래시 메모리 등이 사용되는 경우가 많다. 
* 하드 디스크가 없어 물리적인 디스크가 존재하지 않는다. 
 
이외에도 많은 차이가 있겠지만, 위 내용들에 대해서만 간단히 살펴보자. 
 
일반적으로 임베디드 시스템이라고 한다면 CPU와 NOR 또는 NAND 플래시 메모리 그리고 SDRAM과 같은 메모리를 갖고 있다. 이 외에 UART 등의 시리얼, LCD, NETWORK 등 매우 다양한 주변 장치들이 붙을 수 있다. 이러한 것은 어느 용도의 시스템인지에 따라, 그 변화 폭이 매우 크다. 시중에 많은 임베디드 시스템 및 개발에 대한 책들이 있지만, 실제 프로젝트에서 실용적으로 적용하고자 할 때 유용한 정보가 적은 것은 앞서 말한 것들이 그중 한 요인일 것이다. 
 
임베디드 시스템에서 부트로더의 역할 중 하나는 CPU의 초기화 및 시스템(또는 보드)의 초기화를 하게 된다. 물론, 초기 전원이 인가되었을 때 최초 부트로더가 실행될 수 있는 최소한의 기본적인 환경은 하드웨어적으로 초기화된다. 이를 통해 부트로더의 초기 스타트업 코드가 실행되고 여기서 시스템의 추가적인 초기화 작업이 이뤄지는 것이다. 
 
부트로더가 실행이 되면 일반적으로 사용자와 대화를 할 수 있는 콘솔을 열게 된다. 이때 많이 사용하는 것이 UART를 이용한 시리얼 통신이다. 이외에도 NETWORK나 USB 등을 이용해서 콘솔을 열 수 있는 경우도 있지만, 이는 2차적인 통신 방법으로 그리 많이 사용되지는 않는다. 
 
콘솔이 열리면 이를 통해 부트로더를 제어하고 OS 이미지를 올리거나 모니터링 하거나 OS 이미지를 로딩하고 부팅하는 것을 하게 된다. OS로 부팅이 이뤄지면 부트로더는 그 소임을 다 한 것이다. 
 
임베디드 리눅스 있어서 널리 사용되는 것으로는 다음과 같다. 
* [http://sourceforge.net/projects/u-boot u-boot]
== 커널 ==

=== 모듈 프로그래밍 ===
==== Makefile 만들기 ====
커널 2.6에서는 외부 모듈의 빌드를 커널 소스 내의 빌드 시스템을 이용한다. 예를 들어 foobar.ko 라는 커널 모듈을 만든다고 하면 다음과 같이 Makefile 파일을 만든다. 
{{{#!vim make
ifneq ($(KERNELRELEASE),)
#call from kernel build system

@@ -103,11 +431,46 @@
clean:
rm -rf *.o *~ core .depend .*.cmd .*.o.d *.ko *.mod.c .tmp_versions
}}}
/!\ Makefile에서 실행 명령 행 첫 공백은 '''TAB'''으로 띄워야 한다. 공백 문자가 아니다

/!\ Makefile만약, 컴파일 하는 시스템에서 실행 명령 행 첫 공백은 '''TAB'''사용하는 커널 버전으로 띄워야 컴파일을 하고자 다면 다음과 같이 Makefile을 작성해도 된다. 공백 문자가 아니 
{{{#!vim make 
obj-m := foobar.o 
 
KDIR := /lib/modules/$(shell uname -r)/build 
PWD := $(shell pwd) 
 
default: 
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules 
 
clean: 
rm -rf *.ko *.mod.* .*.cmd *.o 
}}} 
==== 간한단 모듈 예 ==== 
음은 hello module을 출력하는 간단한 모듈 예이다. 
{{{#!vim c 
#include <linux/kernel.h> 
#include <linux/module.h> 
#include <linux/init.h> 
 
static int __init hello_init(void) 
printk("hello module\n"); 
return 0; 
 
static void __exit hello_exit(void) 
 
module_init(hello_init); 
module_exit(hello_exit); 
 
MODULE_LICENSE("GPL"); 
}}}
=== 프로세스 ===
관련 함수는 <linux/sched.h>에 정의되어 있다. pid를 갖는 프로세스(태스크)의 디스크립터를 가져오고자 할 때는 '''find_task_by_pid()'''함수를 사용한다. 사용 예는 다음과 같다: 
{{{#!vim c
struct task_struct *p;

read_lock(&tasklist_lock);
@@ -121,7 +484,7 @@


다음은 include/asm-generic/siginfo.h에 정의되어 있는 내용 중 일부이다.
{{{#!vim Cc
typedef struct siginfo {
int si_signo;
int si_errno;
@@ -152,16 +515,17 @@
||sigprocmask|| ||
||block_all_signals|| ||
||unblock_all_signals|| ||

시그널 함수 중 kill_proc()함수는 다음과 같이 이용할 수 있다.
{{{#!vim Cc
kill_proc(cpid, SIGINT, 1);
}}}
프로세스(cpid)에 SIGUSR2 시그널을 보낸다. 보내는 것에 실패한다면 SIGINT를 보내게 된다.


'''send_sig_info()'''를 이용하는 경우는 다음과 같다:
{{{#!vim Cc
struct siginfo info;

info.si_signo = SIGUSR2;
@@ -178,7 +542,7 @@
=== 메모리 ===
==== IO 메모리 ====
IO 메모리에 관련된 인터페이스는 <linux/ioport.h>에 정의되어 있다. IO를 위한 메모리를 할당은 다음과 같이 한다.
{{{#!vim Cc
char *name = "MyIO";

if (!request_mem_region(phys, size, name))
@@ -191,15 +555,34 @@


할당 받은 IO 메모리를 해제하는 경우는 다음과 같다.
{{{#!vim Cc
release_mem_region(phys, size);
iounmap((void *)virt);
}}}
* virt : 해제할 가상 메모리 시작 번지
== 램디스크 == 
임베디드 시스템에서는 일반적으로 하드 디스크와 같은 물리적인 디스크 디바이스가 없는 경우가 많다. 이런 경우 램디스크라는 메모리 상에 디스크와 같은 물리적인 디스크 상에 존재하는 파일 시스템을 만들어주게 된다.  
 
램디스크의 파일 시스템으로 많이 이용하는 것으로는 다음과 같다. 
* ext2/ext3 
* cramfs 
* squashfs 
* jffs2 
* yaffs1/2 
 
ext2/ext3의 경우엔 dd 명령과 루프백 디바이스를 이용한 마운트를 통해 램디스크를 만들게 된다. 그외 cramfs, jffs2, yaffs1/2 등은 이들 램디스크를 만들어 주는 전용 유틸을 제공한다.  
 
일반적으로 처음부터 램디스크를 만드는 것은 다소 어려울 수 있다. 그래서 남들이 미리 만든 램디스크를 이용하여 적절히 수정하여 사용하는 경우가 많다. 
 
램디스크를 만들 때 많이 사용하는 것이 busybox이다. busybox는 리눅스에서 사용하는 필수 유틸 및 유용한 유틸들을 임베디드 시스템에 적합하도록 경량화시켜 하나로 묶은, 일종의 스위스 군용 칼 같은 것이다. 작지만 모든 기능을 갖추고 있는 아주 강력하면서도 매우 작은 도구상자인 셈이다. 
 
busybox의 소스 및 자료는 다음 사이트에서 구할 수 있다. 
* [http://busybox.net] 
== 응용 프로그램 ==
=== Makefile (skelecton) ===
다음은 Makefile을 작성할 때 참조할 만한 간단한 예제이다:
{{{#!vim make
#CROSS := arm-linux-
CC := $(CROSS)gcc
LIBS := -lpthread
@@ -230,10 +613,23 @@

}}}

다음은 여러 디렉토리의 Makefile이 있을 때 상위 디렉토리에서 일괄적으로 make할 때 사용하는 경우이다: 
{{{#!vim make 
DIRS= drv src lib test 
 
all: 
for i in $(DIRS) ; do make -C $$i || exit $? ; done 
 
clean: 
for i in $(DIRS) ; do make -C $$i clean ; done 
 
dep: 
for i in $(DIRS) ; do make -C $$i dep ; done 
}}}
=== 프로세스 ===
=== 시그널 ===
간단하게 다음의 함수를 이용하여 시그널을 다룰 수 있다.
{{{#!vim Cc
#include <signal.h>

void (*signal(int sig, void (*func)(int)))(int);
@@ -243,23 +639,24 @@


안정적인 시그널 인터페이스는 다음을 사용한다:
{{{#!vim Cc
#include <signal.h>

int sigaction(int sig, const struct sigaction *act, struct sigaction *oact);
}}}

sigaction 구조체는 최소한 다음의 멤버를 갖는다:
{{{#!vim Cc
void (*) (int) sa_handler
sigset_t sa_mask
int sa_flags
}}}

여기서 sa_handler는 함수가 올 수도 있고 SIG_DFL 또는 SIG_IGN이 올 수도 있다.

== 참고 ==
* [wiki:임베디드리눅스레퍼런스 임베디드 리눅스 레퍼런스]
* [http://ftp.linux.org.uk/pub/linux/arm/source/patches/ 유틸들에 대한 ARM용 패치들] 




임베디드 리눅스 실전 가이드 #

-- 김도집 2005-08-24 11:41:25

Contents

1 임베디드 리눅스 실전 가이드
1.1 개요
1.2 저작권
1.3 개발 도구
1.3.1 scratchbox
1.3.1.1 설치 on the debian
1.3.1.2 scratchbox에서 toolchain 만들기
1.3.1.3 scratchbox에서 kegel의 crosstool로 만들기
1.3.2 uclibc를 이용한 toolchain 만들기
1.3.3 kegel의 toolchains 만들기
1.3.4 xscale toolchains 빌드 분석
1.3.5 binutiles
1.3.6 라이브러리
1.3.6.1 zlib
1.3.6.2 libjpeg
1.3.6.3 libpng
1.3.7 사용자 정의 라이브러리
1.3.7.1 정적 라이브러리 만들기
1.3.7.2 동적 라이브러리 만들기
1.3.7.3 라이브러리 사용하기
1.3.8 Autotools
1.3.9 Make/Makefile
1.3.9.1 Makefile 일괄 처리
1.3.10 ?SCons
1.3.11 vim
1.3.12 ctags
1.3.13 디버깅
1.3.13.1 gdb
1.3.13.1.1 Cross-GDB compilation
1.3.13.1.2 gdb 사용
1.3.13.2 사용자 모드 디버깅
1.3.13.3 커너 모드 디버깅
1.3.13.3.1 kgdb
1.3.13.3.2 kdb
1.3.14 Doxygen
1.3.15 테스트
1.4 부트로더
1.5 커널
1.5.1 모듈 프로그래밍
1.5.1.1 Makefile 만들기
1.5.1.2 간한단 모듈 예
1.5.2 프로세스
1.5.3 비동기
1.5.3.1 시그널
1.5.4 메모리
1.5.4.1 IO 메모리
1.6 램디스크
1.7 응용 프로그램
1.7.1 Makefile (skelecton)
1.7.2 프로세스
1.7.3 시그널
1.8 참고


1.1 개요 #

1.2 저작권 #

본 문서에 대한 저작권은 김도집에 있으며, 자유롭게 배포가 가능하다. 단, 출처는 명시해야 한다.

1.3 개발 도구 #

1.3.1 scratchbox #

http://www.scratchbox.org

scratchbox는 다른 toolchains과는 다소 다른 환경을 제공한다.

이는 호스트와는 독립적인 cross 개발 환경을 제공한다.

1.3.1.1 설치 on the debian #

sources.list에 다음을 추가한다.
# for scratchbox
deb http://scratchbox.org/debian ./

1.3.1.2 scratchbox에서 toolchain 만들기 #

scratchbox에서 toolchains을 직접 빌드 하기 위해서는 sb-toolchains를 사용한다.

다음은 sb-toolchains-1.0.3을 기준으로 설명한다.

meta/toolchain/arm-gcc3.3-glibc.conf에 toolchains에 대한 configuration이 정의되어 있다.

툴체인 빌드:
make CONFIG=meta/toolchain/xxx.conf

툴체인 clean:
make clean-toolchain CONFIG=meta/toolchain/xxx.conf

1.3.1.3 scratchbox에서 kegel의 crosstool로 만들기 #


  1. darcs get --set-scripts-executable http://scratchbox.org/repos/1.2/sb-toolchains
  2. wget http://kegel.com/crosstool/crosstool-0.42.tar.gz
  3. tar xzf crosstool-0.42.tar.gz
  4. cd sb-toolchains
  5. ./import-crosstool.sh ../crosstool-0.42/
  6. make clean-toolchain CONFIG=meta/toolchain/<myconfig>
  7. make CONFIG=meta/toolchain/<myconfig> all-sums
  8. make CONFIG=meta/toolchain/<myconfig>

/!\ make clean-toolchain CONFIG=meta/toolchain/<myconfig> 는 선택으로, 이전에 빌드했던 적이 있다면 실행해 주는 것이 좋다.
/!\ LIBC_APP_DIR <= glibc-2.3.4 또는 이후 버전에서만 <myconfig>에서 정의해야 한다.

xscale용 툴체인을 빌드 하는 경우 -mcpu=iwmmxt를 지정하는데, ARM Emulator인 QEMU에서 이를 인식하지 못하는 문제가 있다.

1.3.2 uclibc를 이용한 toolchain 만들기 #

[http]http://www.uclibc.org를 참고한다.

uClibc와 관련된 사이트는 다음과 같다.

1.3.3 kegel의 toolchains 만들기 #

/!\ crosstool 스크립트는 계속 업데이트 되고 있으므로 http://www.kegel.com/crosstool 을 확인하기 바란다.

kegel의 스크립트를 이용하여 툴체인을 만드는 방법은 다음과 같다:
  1. crosstool을 다운 받는다. wget [http]http://kegel.com/crosstool/crosstool-0.38.tar.gz
  2. 압축을 푼다. tar zxvf crosstool-0.38.tar.gz
  3. demo-arm-softfloat.sh을 수정한다. gcc-3.3.3 줄의 주석처리를 없앤다.
  4. arm-softfloat.dat을 수정한다. Set TARGET=arm-linux
  5. 빌드 디렉토리를 만든다. sudo mkdir /opt; sudo chown $USER /opt
  6. unset LD_LIBRARY_PATH를 실행한다.
  7. tool을 실행한다. sh demo-arm-softfloat.sh
  8. 빌드 될 동안 커피 한잔 마시면서 기다리면 된다.

만약, 빌드 할 디렉토리를 변경하고자 한다면 demo-arm-softfloat.sh를 수정하고 빌드 디렉토리를 만들 때 수정된 디렉토리로 만들면 된다.

혹, 다음과 같은 에러가 난다면
atic hello.c -o arm-linux-hello-static
hello.c: In function `main':
hello.c:4: error: `PATH_MAX' undeclared (first use in this function)
hello.c:4: error: (Each undeclared identifier is reported only once
hello.c:4: error: for each function it appears in.)
testhello.sh에서 #include <limits.h>의 아래에 #include <linux/litmits.h>를 추가한다.

1.3.4 xscale toolchains 빌드 분석 #

xscale용 toolchains은 ftp://ftp.arm.linux.org.uk에서 구할 수 있다.

xsclae용 toolchains은 앞서 소개한 kegel의 스크립트를 이용하여 만들어진다.

빌드시 사용되는 스크립트:
  • arm-iwmmxt.dat
  • gcc-3.4.3-glibc-2.3.2.dat
  • all.sh
dat 파일은 all.sh에서 사용하는 환경변수를 지정하게 된다.

arm-iwmmxt.dat의 내용은 다음과 같다.
KERNELCONFIG=`pwd`/arm.config
TARGET=arm-linux
TARGET_CFLAGS="-O"
GCC_EXTRA_CONFIG="--with-cpu=iwmmxt --enable-cxx-flags=-mcpu=iwmmxt"
GLIBC_EXTRA_CONFIG="--without-fp"
GLIBC_EXTRA_CC_ARGS="-finline-limit=10000"
gcc-3.4.3-glibc-2.3.2.dat 파일을 보면 toolchains에서 사용하는 소스를 확인할 수 있는데, 그들 소스는 다음과 같다.
  • driscoll-binutils-2.14.90
  • gcc-3.4.3
  • glibc-2.3.2
  • linux-2.6.9
  • glibc-linuxthreads-2.3.2

패치 파일은 다음과 같다.
  • driscoll-binutils-2.14.90
  • gcc-3.4.3
    • gcc-3.4.3-arm-softfloat.patch
  • glibc-2.3.2
    • glibc-2.3.2-memxxx-strxxx.patch
    • glibc-2.3.2.patch
    • glibc-stat.patch
    • glibc-vfp.patch
  • glibc-linuxthreads-2.3.2
    • glibc-linuxthreads-2.3.2.patch
  • linux-2.6.9

이들 패치는 getandpatch.sh 스크립트를 통해 패치가 이뤄진다. 패치는 -p1 옵션을 사용한다.

1.3.5 binutiles #

arm-linux-addr2line  arm-linux-ld       arm-linux-ranlib   arm-linux-strip
arm-linux-ar         arm-linux-nm       arm-linux-readelf  
arm-linux-as         arm-linux-objcopy  arm-linux-size     
arm-linux-c++filt    arm-linux-objdump  arm-linux-strings

      Invocation line includes a switch not recognized by the base assembler.
      See if it's a processor-specific option.
 
      This routine is somewhat complicated by the need for backwards
      compatibility (since older releases of gcc can't be changed).
      The new options try to make the interface as compatible as
      possible with GCC.
 
      New options (supported) are:
 
              -mcpu=<cpu name>           Assemble for selected processor
              -march=<architecture name> Assemble for selected architecture
              -mfpu=<fpu architecture>   Assemble for selected FPU.
              -EB/-mbig-endian           Big-endian
              -EL/-mlittle-endian        Little-endian
              -k                         Generate PIC code
              -mthumb                    Start in Thumb mode
              -mthumb-interwork          Code supports ARM/Thumb interworking
 
      For now we will also provide support for:
 
              -mapcs-32                  32-bit Program counter
              -mapcs-26                  26-bit Program counter
              -macps-float               Floats passed in FP registers
              -mapcs-reentrant           Reentrant code
              -matpcs
      (sometime these will probably be replaced with -mapcs=<list of options>
      and -matpcs=<list of options>)
 
      The remaining options are only supported for back-wards compatibility.
      Cpu variants, the arm part is optional:
              -m[arm]1                Currently not supported.
              -m[arm]2, -m[arm]250    Arm 2 and Arm 250 processor
              -m[arm]3                Arm 3 processor
              -m[arm]6[xx],           Arm 6 processors
              -m[arm]7[xx][t][[d]m]   Arm 7 processors
              -m[arm]8[10]            Arm 8 processors
              -m[arm]9[20][tdmi]      Arm 9 processors
              -mstrongarm[110[0]]     StrongARM processors
              -mxscale                XScale processors
              -m[arm]v[2345[t[e]]]    Arm architectures
              -mall                   All (except the ARM1)
      FP variants:
              -mfpa10, -mfpa11        FPA10 and 11 co-processor instructions
              -mfpe-old               (No float load/store multiples)
              -mvfpxd                 VFP Single precision
              -mvfp                   All VFP
              -mno-fpu                Disable all floating point instructions
 
      The following CPU names are recognized:
              arm1, arm2, arm250, arm3, arm6, arm600, arm610, arm620,
              arm7, arm7m, arm7d, arm7dm, arm7di, arm7dmi, arm70, arm700,
              arm700i, arm710 arm710t, arm720, arm720t, arm740t, arm710c,
              arm7100, arm7500, arm7500fe, arm7tdmi, arm8, arm810, arm9,
              arm920, arm920t, arm940t, arm946, arm966, arm9tdmi, arm9e,
              arm10t arm10e, arm1020t, arm1020e, arm10200e,
              strongarm, strongarm110, strongarm1100, strongarm1110, xscale.

1.3.6 라이브러리 #

1.3.6.1 zlib #

zlib은 널리 사용되는 압축 라이브러리이다.


zlib 1.2.2 버전 이하에서는 보안 문제가 있으므로 반드시 최신 버전을 사용하기 권한다.


zlib는 다음의 사이트에서 구할 수 있다: [http]http://www.zlib.net


빌드하기:
다음은 zlibc 1.2.3의 빌드 과정이다.


  1. ./configure 또는 ./configure --shared 를 실행한다.

    --shared 옵션은 zlib 라이브러리를 동적으로 만들라는 의미이다.
  2. ./configure --shared --prefix=/usr/local/arm/arm-uclibc-3.3.5
    


  3. 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
    


  4. make install 를 실행하여 빌드된 것을 설치한다.

1.3.6.2 libjpeg #

./configure --prefix=/usr --enabled-shared

1.3.6.3 libpng #

./configure --prefix=/usr --enable-shared

1.3.7 사용자 정의 라이브러리 #

사용자가 직접 라이브러리를 만들어 사용할 수 있다. 이럴 때는 라이브러리를 만들어 사용하는 것이 좋다:


  • 소스 코드를 공개하고 싶지 않을 때
  • 동일한 코드를 여러 프로그램에서 반복적으로 사용할 때
  • 업데이트를 용이하게 하고자 할 때


    라이브러리는 두 가지 타입이 있다. 정적 라이브러리와 동적 라이브러리이다. 정적 라이브러리를 이용하는 경우엔 이 라이브러리를 사용하는 실행 파일에 라이브러리가 포함이 된다. 반면에, 동적 라이브러리를 이용하는 경우엔 실행 파일에 라이브러리가 포함되지 않고 동적으로 라이브러리를 참조하여 사용하게 된다. 즉, 여러 프로그램에서 동일한 라이브러리를 반복적으로 사용하는 경우엔 동적 라이브러리로 생성하여 사용하는 것이 좋다.


    그외에도 라이선스 문제도 있는데, 정적 라이브러리의 경우 실행 파일에 라이브러리 자체가 포함되므로 GPL 라이선스의 영향을 받지만, 동적 라이브러리의 경우엔 이러한 문제를 피해갈 수도 있다.
  • 1.3.7.1 정적 라이브러리 만들기 #

    정적 라이브러리는 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
    


    1.3.7.2 동적 라이브러리 만들기 #

    동적 라이브러리를 만드는 방법은 다음과 같다:
    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
    

    1.3.7.3 라이브러리 사용하기 #

    사용자가 만든 라이브러리는 사용자 임의의 디렉토리에 위치한다. 따라서 표준 라이브러리 경로에서 해당 라이브러리를 찾지 못해, 라이브러리를 이용하여 컴파일 하고자 할 때 라이브러리를 찾을 수 없다는 에러를 보게 될 것이다.


    컴파일러를 이용하여 컴파일 할 때는 -L과 -l를 지정하여 라이브러리를 경로를 지정해 줄 수 있다:
    -L{경로}라이브러리를 찾을 경로를 추가해 준다
    -l{lib를뺀라이브러리이름}라이브러리 이름


    예를 들어 /home/foo/bar/라는 디렉토리 아래에 libmy.a가 있다면 다음과 같이 컴파일 옵션을 줄 수 있다.
    gcc -Wall -O2 -o hello hello.c -L/home/foo/bar -lmy
    


    동적 라이브러리의 경우엔 mylib.so.1.0.1로 만들었다면 다음과 같이 심볼릭 링크를 만들어주어야 한다.
    ln -s mylib.so.1.0.1 mylib.so.1
    ln -s mylib.so.1 mylib.so
    
    libmy.so를 찾을 수 없다면 경로를 명시해도 라이브러리를 찾지 못할 것이다.


    동적 라이브러리를 이용하여 컴파일 경우엔 실행 파일을 실행 시에 라이브러리를 필요로 한다. LD_LIBRARY_PATH라는 환경 변수에 라이브러리 경로를 추가하거나 다음과 같이 임시로 지정할 수도 있다:
    LD_LIBRARY_PATH={라이브러리경로}:${LD_LIBRARY_PATH} 실행파일
    

    1.3.8 Autotools #

  • http://wiki.kldp.org/wiki.php/DocbookSgml/Autotools-KLDP
  • 1.3.9 Make/Makefile #

    빌드시에 일괄 처리를 위한 목적으로 make 를 사용한다. 반복적인 작업을 Makefile에 명시된 절차에 따라 처리한다.

    make를 대체하는 도구로는 ?SCons라는 것도 있다. 개인적인 소견으로는 make보다는 ?SCons가 더 사용하기 편리하다.

    1.3.9.1 Makefile 일괄 처리 #

    DIRS = foo bar
    
    all:
         for i in $(DIRS); do make -C $$i; done
    
    clean:
         for i in $(DIRS); do make -C $$i clean; done
    

    1.3.10 ?SCons #

    Software construction tool이다. 자세한 것은 [http]http://www.scons.org를 참고하라.

    좀 더 자세히 둘러보기

    1.3.11 vim #

    리눅스에서 가장 널리 사용되는 편집기 중 하나이다. 키보드 상에 손을 올려 놓기만 하면 모든 것을 처리할 수 있는 아주 막강한 도구이다.

    처음 익히는데, 다소 어려움이 있을지 모르지만 익히고 나면 다른 편집기를 쓴다는 것이 매우 불편하다는 것을 알게 된다.

    1.3.12 ctags #

    vim에서는 tags를 지원한다. ctags를 통해 만들어진 tag 파일을 참조하여 소스 내에서 쉽게 탐색을 할 수 있다.

    tags 파일의 생성은 다음가 같이 한다.
    find ./ -name "*.[chS]" | ctags -L-
    find ./ -name "*.cc" | ctags -a -L-
    

    vim에서 사용하는 tag 관련 명령으로는 :ts, :ta, :set tags=tags,../tags 등이 있다.
    명령설명
    :ts찾고자 하는 index가 여럿일 때 그 목록을 보여준다
    :ta index명tag파일에서 index명을 찾는다
    :set tags=tags,../tags,../../tagstag 파일의 위치를 지정한다

    그외 vim을 실행할 때 vim -t index명 을 통해 직접 해당 위치로 찾아 들어갈 수 있다.

    1.3.13 디버깅 #

    1.3.13.1 gdb #

       Target                       Host
    +--------------+            +-------------+
    | gdbserver    +------------+   gdb       |
    +--------------+            +-------------+
    
    On the Target machine
    target> gdbserver comm program [ args ... ]
    target> gdbserver /dev/ttyS1 emacs foo.txt
    target> gdbserver :2345 emacs foo.txt
    경우에 따라서는 현재 실행 중인 프로그램을 gdbserver와 연계 시킬 수 있다.
    target> gdbserver comm --attache program [ argss ... ]
    On the GDB host machine
    (gdb) target remote /dev/ttyS1
    (gdb) target remote the-target:2345
    Dynamic Link
    (gdb) set solib-absolute-prefix /dev/null
    (gdb) set solib-search-path /.../my_libs:/.../more_libs

    1.3.13.1.1 Cross-GDB compilation #
    1. GDB 빌드 (생략)
    2. GDB 서버 빌드
      1. make realclean
      2. ./configure --host=arm-linux-elf
        --target=arm-linux-elf
      3. cd gdb
      4. mv gdbserver gdbserver.orig
      5. tar xvzf gdbserver-arm.src.tgz
      6. cd gdbserver
      7. make CC=arm-linux-gcc

    1.3.13.1.2 gdb 사용 #
    메모리 dump
    x/20b 0xc0001000
    x/20h 0xc0001000
    x/20w 0xc0001000
    

    1.3.13.2 사용자 모드 디버깅 #

    arm_linux_objdump -xDSl apps > apps.dump

    1.3.13.3 커너 모드 디버깅 #

    커널 모드 디버깅에 대한 것은 KernelApi7를 참고하자.
    1.3.13.3.1 kgdb #
    1.3.13.3.2 kdb #

    1.3.14 Doxygen #

    [http]http://www.pie.pe.kr/cgi-bin/moin.cgi/Doxygen

    1.3.15 테스트 #

    1.4 부트로더 #

    데스크탑 PC에서 리눅스를 사용해 본 사용자라면 lilo나 grub과 같은 부트로더를 들어봤을 것이다. 이는 하드 디스크의 MBR 또는 부트 영역에서 있어 리눅스 커널을 로딩하는 역할을 한다.

    임베디드에서는 이와 같은 부트로더가 있지만, 몇가지 다른 점이 있다.
    • 임베디드에서는 바이오스가 없다.
    • 임베디드에서는 하드 디스크가 아닌 플래시 메모리 등이 사용되는 경우가 많다.
    • 하드 디스크가 없어 물리적인 디스크가 존재하지 않는다.

    이외에도 많은 차이가 있겠지만, 위 내용들에 대해서만 간단히 살펴보자.

    일반적으로 임베디드 시스템이라고 한다면 CPU와 NOR 또는 NAND 플래시 메모리 그리고 SDRAM과 같은 메모리를 갖고 있다. 이 외에 UART 등의 시리얼, LCD, NETWORK 등 매우 다양한 주변 장치들이 붙을 수 있다. 이러한 것은 어느 용도의 시스템인지에 따라, 그 변화 폭이 매우 크다. 시중에 많은 임베디드 시스템 및 개발에 대한 책들이 있지만, 실제 프로젝트에서 실용적으로 적용하고자 할 때 유용한 정보가 적은 것은 앞서 말한 것들이 그중 한 요인일 것이다.

    임베디드 시스템에서 부트로더의 역할 중 하나는 CPU의 초기화 및 시스템(또는 보드)의 초기화를 하게 된다. 물론, 초기 전원이 인가되었을 때 최초 부트로더가 실행될 수 있는 최소한의 기본적인 환경은 하드웨어적으로 초기화된다. 이를 통해 부트로더의 초기 스타트업 코드가 실행되고 여기서 시스템의 추가적인 초기화 작업이 이뤄지는 것이다.

    부트로더가 실행이 되면 일반적으로 사용자와 대화를 할 수 있는 콘솔을 열게 된다. 이때 많이 사용하는 것이 UART를 이용한 시리얼 통신이다. 이외에도 NETWORK나 USB 등을 이용해서 콘솔을 열 수 있는 경우도 있지만, 이는 2차적인 통신 방법으로 그리 많이 사용되지는 않는다.

    콘솔이 열리면 이를 통해 부트로더를 제어하고 OS 이미지를 올리거나 모니터링 하거나 OS 이미지를 로딩하고 부팅하는 것을 하게 된다. OS로 부팅이 이뤄지면 부트로더는 그 소임을 다 한 것이다.

    임베디드 리눅스 있어서 널리 사용되는 것으로는 다음과 같다.

    1.5 커널 #


    1.5.1 모듈 프로그래밍 #

    1.5.1.1 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으로 띄워야 한다. 공백 문자가 아니다

    만약, 컴파일 하는 시스템에서 사용하는 커널 버전으로 컴파일을 하고자 한다면 다음과 같이 Makefile을 작성해도 된다.
    obj-m := foobar.o
    
    KDIR := /lib/modules/$(shell uname -r)/build
    PWD := $(shell pwd)
    
    default:
            $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
    
    clean:
            rm -rf *.ko *.mod.* .*.cmd *.o
    

    1.5.1.2 간한단 모듈 예 #

    다음은 hello module을 출력하는 간단한 모듈 예이다.
    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/init.h>
    
    static int __init hello_init(void)
    {
      printk("hello module\n");
      return 0;
    }
    
    static void __exit hello_exit(void)
    {
    }
    
    module_init(hello_init);
    module_exit(hello_exit);
    
    MODULE_LICENSE("GPL");
    

    1.5.2 프로세스 #

    관련 함수는 <linux/sched.h>에 정의되어 있다. pid를 갖는 프로세스(태스크)의 디스크립터를 가져오고자 할 때는 find_task_by_pid()함수를 사용한다. 사용 예는 다음과 같다:
    struct task_struct *p;
    
    read_lock(&tasklist_lock);
    p = find_task_by_pid(pid);
    read_unlock(&tasklist_lock);
    

    1.5.3 비동기 #

    1.5.3.1 시그널 #

    커널에서 호출한 프로세스로 시그널을 전달할 수 있다. 시그널 호출 시 전달되는 데이터는 siginfo 구조체로 정의되어 있다.


    다음은 include/asm-generic/siginfo.h에 정의되어 있는 내용 중 일부이다.
    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_infosiginfo의 내용을 보내려면 이 함수를 이용한다
    sigprocmask
    block_all_signals
    unblock_all_signals


    시그널 함수 중 kill_proc()함수는 다음과 같이 이용할 수 있다.
    kill_proc(cpid, SIGINT, 1);
    
    프로세스(cpid)에 ?SIGUSR2 시그널을 보낸다. 보내는 것에 실패한다면 SIGINT를 보내게 된다.


    send_sig_info()를 이용하는 경우는 다음과 같다:
    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형의 포인터이다.


    1.5.4 메모리 #

    1.5.4.1 IO 메모리 #

    IO 메모리에 관련된 인터페이스는 <linux/ioport.h>에 정의되어 있다. IO를 위한 메모리를 할당은 다음과 같이 한다.
    char *name = "MyIO";
    
    if (!request_mem_region(phys, size, name))
      return -EBUSY;
    
    virt = ioremap(phys, size);
    
  • phys : 물리 메모리 시작 번지
  • size : IO 메모리로 할당할 메모리의 크기


    할당 받은 IO 메모리를 해제하는 경우는 다음과 같다.
    release_mem_region(phys, size);
    iounmap((void *)virt);
    
  • virt : 해제할 가상 메모리 시작 번지
  • 1.6 램디스크 #

    임베디드 시스템에서는 일반적으로 하드 디스크와 같은 물리적인 디스크 디바이스가 없는 경우가 많다. 이런 경우 램디스크라는 메모리 상에 디스크와 같은 물리적인 디스크 상에 존재하는 파일 시스템을 만들어주게 된다.

    램디스크의 파일 시스템으로 많이 이용하는 것으로는 다음과 같다.
    • ext2/ext3
    • cramfs
    • squashfs
    • jffs2
    • yaffs1/2

    ext2/ext3의 경우엔 dd 명령과 루프백 디바이스를 이용한 마운트를 통해 램디스크를 만들게 된다. 그외 cramfs, jffs2, yaffs1/2 등은 이들 램디스크를 만들어 주는 전용 유틸을 제공한다.

    일반적으로 처음부터 램디스크를 만드는 것은 다소 어려울 수 있다. 그래서 남들이 미리 만든 램디스크를 이용하여 적절히 수정하여 사용하는 경우가 많다.

    램디스크를 만들 때 많이 사용하는 것이 busybox이다. busybox는 리눅스에서 사용하는 필수 유틸 및 유용한 유틸들을 임베디드 시스템에 적합하도록 경량화시켜 하나로 묶은, 일종의 스위스 군용 칼 같은 것이다. 작지만 모든 기능을 갖추고 있는 아주 강력하면서도 매우 작은 도구상자인 셈이다.

    busybox의 소스 및 자료는 다음 사이트에서 구할 수 있다.

    1.7 응용 프로그램 #

    1.7.1 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)
    
    


    다음은 여러 디렉토리의 Makefile이 있을 때 상위 디렉토리에서 일괄적으로 make할 때 사용하는 경우이다:
    DIRS= drv src lib test
    
    all:
            for i in $(DIRS) ; do make -C $$i || exit $? ; done
    
    clean:
            for i in $(DIRS) ; do make -C $$i clean ; done
    
    dep:
            for i in $(DIRS) ; do make -C $$i dep ; done
    

    1.7.2 프로세스 #

    1.7.3 시그널 #

    간단하게 다음의 함수를 이용하여 시그널을 다룰 수 있다.
    #include <signal.h>
    
    void (*signal(int sig, void (*func)(int)))(int);
    


    /!\ signal 함수는 시그널 핸들러가 수행된 후 기본 상태로 복구되어, 등록한 시그널 핸들러를 반복적으로 처리하고자 할 때는 적합하지 않다.


    안정적인 시그널 인터페이스는 다음을 사용한다:
    #include <signal.h>
    
    int sigaction(int sig, const struct sigaction *act, struct sigaction *oact);
    


    sigaction 구조체는 최소한 다음의 멤버를 갖는다:
    void (*) (int) sa_handler
    sigset_t sa_mask
    int sa_flags
    


    여기서 sa_handler는 함수가 올 수도 있고 SIG_DFL 또는 SIG_IGN이 올 수도 있다.

    1.8 참고 #


    last modified 2006-05-11 08:05:34
    ShowPage|FindPage|DeletePage|LikePages Valid XHTML 1.0! Valid CSS! powered by MoniWiki
    0.2684 sec