Difference between r1.10 and the current
@@ -5,11 +5,185 @@
== 개요 ==
== 저작권 ==
본 문서에 대한 저작권은 김도집에 있으며, 자유롭게 배포가 가능하다. 단, 출처는 명시해야 한다.
== 개발 도구 ==
=== 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은 널리 사용되는 압축 라이브러리이다.
@@ -20,31 +194,469 @@
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 를 실행하여 빌드된 것을 설치한다.
==== libjpeg ====
{{{#!vim sh
./configure --prefix=/usr --enabled-shared
}}}
==== libpng ====
{{{#!vim sh
./configure --prefix=/usr --enable-shared
}}}
=== 사용자 정의 라이브러리 ===
사용자가 직접 라이브러리를 만들어 사용할 수 있다. 이럴 때는 라이브러리를 만들어 사용하는 것이 좋다:
* 소스 코드를 공개하고 싶지 않을 때
* 동일한 코드를 여러 프로그램에서 반복적으로 사용할 때
* 업데이트를 용이하게 하고자 할 때
라이브러리는 두 가지 타입이 있다. 정적 라이브러리와 동적 라이브러리이다. 정적 라이브러리를 이용하는 경우엔 이 라이브러리를 사용하는 실행 파일에 라이브러리가 포함이 된다. 반면에, 동적 라이브러리를 이용하는 경우엔 실행 파일에 라이브러리가 포함되지 않고 동적으로 라이브러리를 참조하여 사용하게 된다. 즉, 여러 프로그램에서 동일한 라이브러리를 반복적으로 사용하는 경우엔 동적 라이브러리로 생성하여 사용하는 것이 좋다.
그외에도 라이선스 문제도 있는데, 정적 라이브러리의 경우 실행 파일에 라이브러리 자체가 포함되므로 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
}}}
==== 라이브러리 사용하기 ====
사용자가 만든 라이브러리는 사용자 임의의 디렉토리에 위치한다. 따라서 표준 라이브러리 경로에서 해당 라이브러리를 찾지 못해, 라이브러리를 이용하여 컴파일 하고자 할 때 라이브러리를 찾을 수 없다는 에러를 보게 될 것이다.
컴파일러를 이용하여 컴파일 할 때는 -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
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을 작성해도 된다.
{{{#!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);
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 메모리에 관련된 인터페이스는 <linux/ioport.h>에 정의되어 있다. 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 : 해제할 가상 메모리 시작 번지
== 램디스크 ==
임베디드 시스템에서는 일반적으로 하드 디스크와 같은 물리적인 디스크 디바이스가 없는 경우가 많다. 이런 경우 램디스크라는 메모리 상에 디스크와 같은 물리적인 디스크 상에 존재하는 파일 시스템을 만들어주게 된다.
램디스크의 파일 시스템으로 많이 이용하는 것으로는 다음과 같다.
* 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
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할 때 사용하는 경우이다:
{{{#!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 c
#include <signal.h>
void (*signal(int sig, void (*func)(int)))(int);
}}}
/!\ signal 함수는 시그널 핸들러가 수행된 후 기본 상태로 복구되어, 등록한 시그널 핸들러를 반복적으로 처리하고자 할 때는 적합하지 않다.
안정적인 시그널 인터페이스는 다음을 사용한다:
{{{#!vim c
#include <signal.h>
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:임베디드리눅스레퍼런스 임베디드 리눅스 레퍼런스]
* [http://ftp.linux.org.uk/pub/linux/arm/source/patches/ 유틸들에 대한 ARM용 패치들]
임베디드 리눅스 실전 가이드 #
--
김도집 2005-08-24 11:41:25
본 문서에 대한 저작권은 김도집에 있으며, 자유롭게 배포가 가능하다. 단, 출처는 명시해야 한다.
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로 만들기 #
- darcs get --set-scripts-executable http://scratchbox.org/repos/1.2/sb-toolchains
- wget http://kegel.com/crosstool/crosstool-0.42.tar.gz
- tar xzf crosstool-0.42.tar.gz
- cd sb-toolchains
- ./import-crosstool.sh ../crosstool-0.42/
- make clean-toolchain CONFIG=meta/toolchain/<myconfig>
- make CONFIG=meta/toolchain/<myconfig> all-sums
- 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 만들기 #
1.3.3 kegel의 toolchains 만들기 #
kegel의 스크립트를 이용하여 툴체인을 만드는 방법은 다음과 같다:
- crosstool을 다운 받는다. wget http://kegel.com/crosstool/crosstool-0.38.tar.gz
- 압축을 푼다. tar zxvf crosstool-0.38.tar.gz
- demo-arm-softfloat.sh을 수정한다. gcc-3.3.3 줄의 주석처리를 없앤다.
- arm-softfloat.dat을 수정한다. Set TARGET=arm-linux
- 빌드 디렉토리를 만든다. sudo mkdir /opt; sudo chown $USER /opt
- unset LD_LIBRARY_PATH를 실행한다.
- tool을 실행한다. sh demo-arm-softfloat.sh
- 빌드 될 동안 커피 한잔 마시면서 기다리면 된다.
만약, 빌드 할 디렉토리를 변경하고자 한다면
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 빌드 분석 #
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 옵션을 사용한다.
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 1.2.2 버전 이하에서는 보안 문제가 있으므로 반드시 최신 버전을 사용하기 권한다.
zlib는 다음의 사이트에서 구할 수 있다:
http://www.zlib.net
빌드하기:
다음은 zlibc 1.2.3의 빌드 과정이다.
- ./configure 또는 ./configure --shared 를 실행한다.
--shared 옵션은 zlib 라이브러리를 동적으로 만들라는 의미이다.
./configure --shared --prefix=/usr/local/arm/arm-uclibc-3.3.5
- 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
- make install 를 실행하여 빌드된 것을 설치한다.
./configure --prefix=/usr --enabled-shared
./configure --prefix=/usr --enable-shared
1.3.7 사용자 정의 라이브러리 #
사용자가 직접 라이브러리를 만들어 사용할 수 있다. 이럴 때는 라이브러리를 만들어 사용하는 것이 좋다:
소스 코드를 공개하고 싶지 않을 때
동일한 코드를 여러 프로그램에서 반복적으로 사용할 때
업데이트를 용이하게 하고자 할 때
라이브러리는 두 가지 타입이 있다. 정적 라이브러리와 동적 라이브러리이다. 정적 라이브러리를 이용하는 경우엔 이 라이브러리를 사용하는 실행 파일에 라이브러리가 포함이 된다. 반면에, 동적 라이브러리를 이용하는 경우엔 실행 파일에 라이브러리가 포함되지 않고 동적으로 라이브러리를 참조하여 사용하게 된다. 즉, 여러 프로그램에서 동일한 라이브러리를 반복적으로 사용하는 경우엔 동적 라이브러리로 생성하여 사용하는 것이 좋다.
그외에도 라이선스 문제도 있는데, 정적 라이브러리의 경우 실행 파일에 라이브러리 자체가 포함되므로 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
사용자가 만든 라이브러리는 사용자 임의의 디렉토리에 위치한다. 따라서 표준 라이브러리 경로에서 해당 라이브러리를 찾지 못해, 라이브러리를 이용하여 컴파일 하고자 할 때 라이브러리를 찾을 수 없다는 에러를 보게 될 것이다.
컴파일러를 이용하여 컴파일 할 때는 -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.9 Make/Makefile #
빌드시에 일괄 처리를 위한 목적으로 make 를 사용한다. 반복적인 작업을 Makefile에 명시된 절차에 따라 처리한다.
make를 대체하는 도구로는
?SCons라는 것도 있다. 개인적인 소견으로는 make보다는
?SCons가 더 사용하기 편리하다.
DIRS = foo bar
all:
for i in $(DIRS); do make -C $$i; done
clean:
for i in $(DIRS); do make -C $$i clean; done
리눅스에서 가장 널리 사용되는 편집기 중 하나이다. 키보드 상에 손을 올려 놓기만 하면 모든 것을 처리할 수 있는 아주 막강한 도구이다.
처음 익히는데, 다소 어려움이 있을지 모르지만 익히고 나면 다른 편집기를 쓴다는 것이 매우 불편하다는 것을 알게 된다.
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명 을 통해 직접 해당 위치로 찾아 들어갈 수 있다.
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 #
- GDB 빌드 (생략)
- GDB 서버 빌드
- make realclean
- ./configure --host=arm-linux-elf
--target=arm-linux-elf
- cd gdb
- mv gdbserver gdbserver.orig
- tar xvzf gdbserver-arm.src.tgz
- cd gdbserver
- make CC=arm-linux-gcc
메모리 dump
x/20b 0xc0001000
x/20h 0xc0001000
x/20w 0xc0001000
arm_linux_objdump -xDSl apps > apps.dump
데스크탑 PC에서 리눅스를 사용해 본 사용자라면 lilo나 grub과 같은 부트로더를 들어봤을 것이다. 이는 하드 디스크의 MBR 또는 부트 영역에서 있어 리눅스 커널을 로딩하는 역할을 한다.
임베디드에서는 이와 같은 부트로더가 있지만, 몇가지 다른 점이 있다.
- 임베디드에서는 바이오스가 없다.
- 임베디드에서는 하드 디스크가 아닌 플래시 메모리 등이 사용되는 경우가 많다.
- 하드 디스크가 없어 물리적인 디스크가 존재하지 않는다.
이외에도 많은 차이가 있겠지만, 위 내용들에 대해서만 간단히 살펴보자.
일반적으로 임베디드 시스템이라고 한다면 CPU와 NOR 또는 NAND 플래시 메모리 그리고 SDRAM과 같은 메모리를 갖고 있다. 이 외에 UART 등의 시리얼, LCD, NETWORK 등 매우 다양한 주변 장치들이 붙을 수 있다. 이러한 것은 어느 용도의 시스템인지에 따라, 그 변화 폭이 매우 크다. 시중에 많은 임베디드 시스템 및 개발에 대한 책들이 있지만, 실제 프로젝트에서 실용적으로 적용하고자 할 때 유용한 정보가 적은 것은 앞서 말한 것들이 그중 한 요인일 것이다.
임베디드 시스템에서 부트로더의 역할 중 하나는 CPU의 초기화 및 시스템(또는 보드)의 초기화를 하게 된다. 물론, 초기 전원이 인가되었을 때 최초 부트로더가 실행될 수 있는 최소한의 기본적인 환경은 하드웨어적으로 초기화된다. 이를 통해 부트로더의 초기 스타트업 코드가 실행되고 여기서 시스템의 추가적인 초기화 작업이 이뤄지는 것이다.
부트로더가 실행이 되면 일반적으로 사용자와 대화를 할 수 있는 콘솔을 열게 된다. 이때 많이 사용하는 것이 UART를 이용한 시리얼 통신이다. 이외에도 NETWORK나 USB 등을 이용해서 콘솔을 열 수 있는 경우도 있지만, 이는 2차적인 통신 방법으로 그리 많이 사용되지는 않는다.
콘솔이 열리면 이를 통해 부트로더를 제어하고 OS 이미지를 올리거나 모니터링 하거나 OS 이미지를 로딩하고 부팅하는 것을 하게 된다. OS로 부팅이 이뤄지면 부트로더는 그 소임을 다 한 것이다.
임베디드 리눅스 있어서 널리 사용되는 것으로는 다음과 같다.
커널 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
다음은 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");
관련 함수는 <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);
커널에서 호출한 프로세스로 시그널을 전달할 수 있다. 시그널 호출 시 전달되는 데이터는 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_info | siginfo의 내용을 보내려면 이 함수를 이용한다 |
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형의 포인터이다.
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 : 해제할 가상 메모리 시작 번지
임베디드 시스템에서는 일반적으로 하드 디스크와 같은 물리적인 디스크 디바이스가 없는 경우가 많다. 이런 경우 램디스크라는 메모리 상에 디스크와 같은 물리적인 디스크 상에 존재하는 파일 시스템을 만들어주게 된다.
램디스크의 파일 시스템으로 많이 이용하는 것으로는 다음과 같다.
- ext2/ext3
- cramfs
- squashfs
- jffs2
- yaffs1/2
ext2/ext3의 경우엔 dd 명령과 루프백 디바이스를 이용한 마운트를 통해 램디스크를 만들게 된다. 그외 cramfs, jffs2, yaffs1/2 등은 이들 램디스크를 만들어 주는 전용 유틸을 제공한다.
일반적으로 처음부터 램디스크를 만드는 것은 다소 어려울 수 있다. 그래서 남들이 미리 만든 램디스크를 이용하여 적절히 수정하여 사용하는 경우가 많다.
램디스크를 만들 때 많이 사용하는 것이 busybox이다. busybox는 리눅스에서 사용하는 필수 유틸 및 유용한 유틸들을 임베디드 시스템에 적합하도록 경량화시켜 하나로 묶은, 일종의 스위스 군용 칼 같은 것이다. 작지만 모든 기능을 갖추고 있는 아주 강력하면서도 매우 작은 도구상자인 셈이다.
busybox의 소스 및 자료는 다음 사이트에서 구할 수 있다.
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
간단하게 다음의 함수를 이용하여 시그널을 다룰 수 있다.
#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이 올 수도 있다.