커널프로그래밍의환경구축
이 강좌는 커널 프로그래밍에 관심을 가지고 있는 입문자들을 위한 것이다. 하지만 아무리 입문자라고 해도 리눅스에 대해 전혀 모르고 있다면 이 강좌를 이해하기 쉽지 않을 것이다. 따라서 이 글을 보고 있는 독자라면 리눅스에서 Application 프로그래밍을 한 번 이상 해봤다는 가정 아래 강좌를 진행하고자 한다. 더구체적으로 말하자면 gcc와 vi 정도는 사용할 수 있으며
기본적인 리눅스 명령어들을 알고 있는 분들을 말한다. 커널 프로그래밍 첫 회에서는 커널 프로그래밍을하기 위한 환경구축및 커널 패치하기, 커널 프로그래밍의 특징 등에 대해 살펴보도록 한다.
글 _ 김민찬 KLDP 멤버, 전문 프로그래머
앞으로의 연재 순서
① 커널 프로그래밍의 환경 구축
② 모듈 구현하기
③ 커널의 동기화에 관하여
④ 커널의 시간관리 및 지연 함수에 대하여
⑤ 파일시스템과 proc file system 사용하기
⑥ 디버깅 기술에 관하여
커널 프로그래밍 개발환경 구축
약 5~6년 전만 해도 리눅스 커널을 적절하게 다시 빌드하는 것은 그리 만만한 작업이 아니었다. 하지만 요즘의 리눅스 배포판들은 굉장히 쉬운 방법으로 리눅스 커널을 빌드할 수 있게 했다. 심지어는 그것을 패키지 형태로 만들어 다른 컴퓨터에 복사함으로써 단순한 설치명령어를 실행하는 것만으로도 리눅스 커널을설치할 수 있도록 하고 있다.
여기서 필자는 커널 프로그래밍의 환경 구축을 위해 현재 리눅스 데스크탑용으로 가장 인기를 끌고 있는우분투(ubuntuedgy) 6.10 desktop 용을 사용할 것이다. 우분투는 iso의 이미지 형태로 다음의 URL의 페이지에서 다운로드할 수 있다 (http://releases.ubuntu.com/6.10/
나 ftp://ftp.kaist.ac.kr/ubuntu-cd/edgy/). 우분투를 설치하는 것은 윈도우를 설치하는 것만큼이나 쉽다. 심지어는 윈도우와 멀티 부팅 환경도 스스로 만들어 준다.
우리가 해야 할 것은 ISO image를 CD로 만들어서 설치하면 그만이다. 또는 필자와 같이 vmware와 같은툴을 활용하면 ISO파일을 그대로 이용해 윈도우에서 설치할 수도 있다. 우분투에 관한 내용들은 다음의 URL에 많은 정보들이 있다. ( http://www.ubuntu.or.kr/wiki.php/FrontPage)
우분투의 설치가 끝났다면 기본적으로 일반 사용자로 로그인이 되어 있을 것이다. 커널 컴파일을 위해서는 root 사용자의 권한이 필요하다. sudo 명령을 사용하여 커널을 빌드할 수도 있지만 쉽게 하기 위해 root로 로그인을 다시 하도록 한다. root로의 login은 다음의 명령을 통해 할 수 있다.
sudo i
우분투 6.10에는 기본적으로 /bin/sh가 /bin/dash의 심볼릭링크로 되어 있다. 이것으로 인하여 소스를 컴파일 하는데 간혹 문제가 생기는 것 같다. 다음의 명령을 통해 /bin/bash를 사용할 수 있게 바꾼다
rm f /bin/sh
ln s /bin/bash /bin/sh
다음으로 apt-get을 이용하여 현재 시스템의 모든 패키지들을 최신으로 update한다.
apt-get update
이번에는 자신이 현재 사용하고 있는 커널의 버전을 확인해 보자. 커널 버전은 다음의 명령을 통해 확인해 볼 수 있다.
uname -r
아마도 우분투 6.10이나 그 이하의 버전을 사용하고 있는 독자중 직접 커널을 업데이트하지 않은 독자라면 현재 2.6.17-10 버전 이하의 리눅스 커널을 사용하고 있을 것이다.
리눅스 커널 버전 관리
2.6.18 = major.minor.release
2.6.18이라하면 major번호가 2이고 minor가 6, 그리고 18번째라는 것이다. 즉, 2.6의 18번째 릴리즈된 버전이라는 뜻. 또한 리눅 의 minor 버전은 커널이 안정된 버전인지 개발 중 인 버전인지를 나타낸다. 안정된 버전은 짝수를 가지며 개발중인 버전은 홀수를 갖게 된다. 일반적으로 짝수와 홀수 버전은 커널 아카이브 트리에 공존한다.
예를 들어 2.4의 짝수 minor 버전을 가진 새로운 커널이 릴리즈되면 2.5의 홀수 버전을 가진 개발버전이2.6을 준비하며작업에 들어간다. 2.5에서 개발 중인 개발 버전들이 많은 기능 추가와 테스트를 통해 안정화되면 2.4의 릴리즈 버전들이 점차 올라가며 2.5의 기능들이 추가되고 버그들이 수정되어 나간다. 즉 2.4.1, 2.4.2, 2.4.3 이런 식으로 올라가게 되는 것 이다. 현재는 우리가 사용하고 있는 커널의 minor 번호는 6 이니 안정된 버전이라고 볼 수 있다.
하지만 2.6으로 오면서 변화가 생겼다. 2.6대의 커널이 릴리즈되면 2.7로 2.8을 준비해야 하는 커널 아카이브 트리가 생겨야 하지만 현재 그렇지가 않다. 2004년 Linux Kernel Developer Summit에서 더 이상 그런 개발방법으로 진행하지 않기로 결정했기 때문이다. 2.6커널은 충분히 안정화되어 있고 홀수 버전을 만들어야 할 만큼 큰 기능변화가 없을 것이 라는 판단에서이다. 지금부터 우리는 현재 커널을 2.6.18 버전으로 업그레이드 할 것이다. 먼저 시스템에 커널을 빌드하고, 빌드된 커널을 데비안 패키지로 만들기 위한 툴들을 설치해야 한다. 콘솔에서 다음 명령을 통해 그러한 툴들을 설치할 수 있다.
apt-get install kernel-package
libncurses5-dev fakeroot
wget bzip2
이번엔 우리가 빌드하게 될 2.6.18버젼의 커널 소스를 다운로드 하도록 하자. 우리는 wget을 사용할 것이다. 하지만 wget이 install되어 있지 않은 분들은 웹브라우저를 통해서도 아래 의 URL에서 다운로드할 수 있다.
cd /usr/src
wgethttp://www.kernel.org/pub/linux/kernel/v2.6/
linux 2.6.18.tar.bz2
이것으로 커널의 빌드를 위한 환경구축은 끝마쳤다.
커널 빌드
이번에는 다운로드 받은 새로운 커널을 빌드하기 위해 /usr/src 디렉토리에 압축을 해지한 후 linux란 이름으로 심볼
릭 링크를 만든 후 새로운 커널의 소스 디렉토리로 이동한다.
tar xjf linux-2.6.18.tar.bz2
ln -s linux-2.6.18 linux
cd /usr/src/linux
지금부터는 커널 컴파일에 앞서 자신의 시스템 환경에 맞게 커널을 설정하는 과정이다. 사실 커널 빌드를위해 설정하는 과정이 커널 빌드에 있어 제일 복잡하고 어려운 과정이다. 왜 냐하면 자신의 시스템의 모든 하드웨어와 그 하드웨어 연결관계를 모두 알고 있어야 하기 때문이다. 또한그 하드웨어와 관련된 커널 설정들도 일일이 설정해줘야 자신의 시스템에 맞 는 가장 알맞은 형태의 커널을 구성할 수 있다.
하지만 이러한 모든 지식을 갖기 위해서는 많이 시간을 투자해야 한다. 대부분의 배포판에는 위와 같은 설정을 가지고 있는 파일이 이미 있다. 물론 자신의 시스템에 최적화된 config 파일은 아니다. 단지 일반적으로 사용할 수 있는 config 파일일 뿐이다. 그러므로 불필요한 설정을 포함하고 있는 경우도 많이 있다. 하지만 강좌를 쉽게 만들기 위해 우리는 배포판에
이미 있는 config 파일을 사용하도록 할 것이다. 이 config파일을 자신의 환경에 최적화할 수 있게 불필요한 것은 다 빼서 날씬하게 만드는 일은 여러분들의 몫이다. (좀 오래되긴 했지 만 다음의 글을 보면 많은 도움이 될 것이다.
http://kldp.org/KoreanDoc/html/Kernel-KLDP/)
커널을 빌드할 때 사용되는 kbuild는 config파일을 통해 커널설정을 하게 된다. config 파일을 만드는 방법은 크게 3가지가 있다.
● make config : 텍스트를 기반으로 사용자가 각 질문에 대하여 대답하는 방식
● make menuconfig : 텍스트 기반 curses 라이브러리 사용하는 방식
● make xconfig : X윈도우 기반의 방식
우리는 위 방식 중 make menuconfig를 사용할 것이다. 먼저 아래의 명령을 통해 현재 디렉토리/usr/src/linux에 배포판의 config 파일을 .config라는 이름으로 복사하도록 하자.
(다른 배포판들도 이와 유사한 config 파일을 /boot 디렉토리나 커널 소스에 설치했다면 /usr/src/config등에 들어 있을 것이다.)
cp /boot/config-`uname r` ./.config
이번에는 복사된 .config 파일을 커널 설정에 반영할 차례이다. make menuconfig를 통해 .config 파일을읽어 들인다. 먼저 Load an Alternate Configuration File 항목으로 들어 간다.
이번에는 .config 파일을 입력하여 .config파일을 읽어 들인다.
마지막으로 설정을 저장하고 빠져나온다.
이제 커널을 빌드할 차례이다. 우분투에서는 컴파일을 할 때 다음과 같은 방법을 통해 우분투 패키지로 커널 이미지와 커널 헤더파일을 만들어 내는 것을 권장한다. 현재는 계속 /usr/src/linux 디렉토리다.
make-kpkg clean
make-kpkg --initrd --append-to-version=
-barrios kernel_image kernel_headers
make-kpkg clean 과정은 처음으로 커널 소스의 압축을 풀어 새롭게 빌드를 시작하는 경우에는 필요하지않은 과정이 다. 이 명령을 수행하게 되면 커널 소스 디렉토리에 있는 이전 에 빌드했던 오브젝트 파일들을 제거한다. 다음으로 append-to-version에 들어갈 내용은 항상 -(하이픈부호)로 시작해야하며 어떤 공백도 들어가선 안 된다. 이 이름은 커널을 구분짓기 위한 이름이니 자신이생각해서 알맞은 이름으로 넣으면 된다. 커널의 빌드과정이 끝나게 되면 /usr/src 디렉토리에는 다음과 같은 2개의 .deb 패키지 형태의 결과물이 나오게 된다.
● linux-image-2.6.18-barrios_2.6.18-barrios-
10.00.Custom_i386.deb
● linux-headers-2.6.18-barrios_2.6.18-barrios-
10.00.Custom_i386.deb
첫 번째 파일은 실제 커널 이미지를 포함하고 있는 파일로서 이 파일을 설치하면 커널이 시스템에 설치되는 것이다. 두 번째 파일은 나중에 모듈만 새로 추가할 때 필요한 커널 헤더 파일을 설치하는 것이다. 두 파일을 아래와 같은 명령으로 설치할 수 있다.
dpkg i linux-image-2.6.18-barrios_2.6.18-
barrios-10.00.Custom_i386.deb
dpkg i linux-headers-2.6.18-barrios_2.6.18-
barrios-10.00.Custom_i386.deb
자 이제 새로운 커널의 설치가 끝났다. 정말 간단하지 않은 가? 재부팅을 한 후 커널 버전을 확인해보자.
커널 설치 후의 변화들
우리는 위에서 만든 커널의 설치로 인해 우리의 시스템에는 어떤 변화가 있어났는지를 알아야 할 필요가 있다. 그래야만
우분투가 아닌 다른 시스템에서도 커널 업그레이드를 진행할 수 있을 것이다. 사실 위 패키지 형태로 커널을 설치할 때는 커
널 빌드에 필요한 다음과 같은 과정을 건너 뛸 수 있다. 일반적 으로 커널을 빌드할 때는 다음의 명령어 순으로 진행하기 마
련이다.
일반적인 커널 빌드 절차
make clean
make
make modules_install
cp arch/i386/boot/bzImage /boot/vmlinuz-2.6.18-barrios
cp System.map /boot/System.map-2.6.18-barrios
cd /boot
mkinitramfs 2.6.18-barrioso /boot/initrd.img-2.6.18-barrios
/boot 디렉토리를 살펴보면다음과 같은 파일들이 생긴 것을 알 수 있다.
● config-2.6.18-barrios : 새로운 커널의 config 파일
● initrd.img-2.6.18-barrios : 새로운 커널이 사용하게 될 initramfs 파일
● System.map-2.6.18-barrios : 새로운 커널의 커널 함수들의 주소가 저장되어 있는 파일(나중에 디버깅을 위해 사용됨)
● vmlinuz-2.6.18-barrios : 새로운 커널의 압축 이미지가 들어있는 바이너리
또한 /lib/modeuls 디렉토리에는 다음과 같은 디렉토리가 생겨 난 것을 알 수 있다. 일반적인 컴파일 방법으로 는 make modules_install을 실행하면 생성되는 디렉토리이다.
● 2.6.18-barrios : 새로운 커널의 모듈들이 들어있는 디렉토리
마지막으로 /boot/grub/menu.lst에는 다음과 같은 항목이 추가된 것을 알 수 있다.
…
title Ubuntu, kernel 2.6.18-barrios
root (hd0,0)
kernel /boot/vmlinuz-2.6.18-barrios
root=/dev/sda1 ro quiet splash
initrd /boot/initrd.img-2.6.18-barrios
quiet
savedefault
boot
title Ubuntu, kernel 2.6.18-barrios
(recovery mode)
root (hd0,0)
kernel /boot/vmlinuz-2.6.18-
barrios root=/dev/sda1 ro single
initrd /boot/initrd.img-2.6.18-barriosboot
…
이들은 새로운 커널로 부팅할 수 있도록 grub(부트로더)에게 알리는 역할을 한다.
커널 패치하기
다음으로 우리는 커널 패치(Patch)에 대해서 알아보도록 한다. 커널 개발자들은 커널의 새로운 기능이나버그들을 패치파일의 형태로 만들어서 전달한다. 그렇기 때문에 자신의 커널을 업그레이드(새로운 기능을 추가, 기존의 버그 해결)하기 위해서는 커널을 패치하는 방법에 대해 알고 있어야 한다. 패치를 하기위해서는 기본적으로 patch라는 명령을 사용한 다. patch 명령을 사용하는 방법은 간단하다. patch의 명령에 리다이렉션을 이용해 입력으로 패치 파일을주면 된다. 우리는 현재 2.6.18의 커널 버전에서 2.6.19-rc4로 우리의 커널을 업그레이드해볼 것이다. 커널 업그레이드를 위해 prepatch 파일을 사용할 것이다.
prepatch란?
prepatch란 리눅스 커널의 업데이트를 위한 알파 버전이라고 보면 된다. 즉 안정적인 버전이 릴리즈되기 전 여러개발자들에게 테스트를 부탁하는 커널 버전이다. 그래서 시간이 흘러 버그들이 많이 보고되고 수정되면 새로운 안정적인(Stable) 버전이 나오게 되는 것이다.
이 파일들은 리눅스 커널의 archive testing 디렉토리에 존재한다.(http://www.kernel.org/pub/linux/kernel/v2.6/testing/) 이prepatch 파일을 적용하는 데는 규칙이 있다. prepatch 파일의 이전 버전의 fullrelease된 커널에 적용해야 한다는 것이다. 즉 prepatch-2.6.19-rc3 라면 linux-kernel 2.6.18에 적용해야 한다는 것이다. 2.6.18.1이나 2.6.18.2와 같은 커널에 적용해서는 안 된다. rc는 Release Candidate의 약자로써Linux Tovalds에 의해 릴리즈 후보가 된 것이다.
다음의 명령을 통해 패치 파일을 다운로드할 수 있다.
cd /usr/src
wget http://www.kernel.org/pub/linux/kerne
/v2.6/testing/patch-2.6.19-rc6.b
다운로드가 끝났다면 이번에는 다시 우리의 커널 소스 디렉토리로 이동해 다음 명령을 통해 패치파일이이상 없이 적용가능한지 확인해 보고 이상이 없으면 실제로 패치파일을 적용한다.
cd /usr/src/linux
bzip2 dc /usr/src/patch-2.6.19-rc6.bz2 |
patch -p1 dry-run
bzip2 dc /usr/src/patch-2.6.19-rc6.bz2 |
patch -p1
두 번째 명령은 실제로 패치파일을 적용하는 것은 아니다. 단지 실제로 하는 것처럼 수행해보고 이상 유무를 보고할 뿐이다. 패치를 이상 없이 끝마쳤다면 위의 커널 빌드 과정을 반복하여 다시 커널을 설치해보자.
커널 프로그래밍의 특징
지금까지 우리는 커널 프로그래밍에 앞서 커널을 새로 빌드하는 방법, 커널에 패치를 적용하는 방법들을살펴보았다. 마지막으로 우리는 실제 커널 프로그래밍에 들어가기에 앞서 커널프로그래밍이 일반 응용어플리케이션 프로그래밍과 다른 점, 그리고 커널 프로그래밍을 막 시작하려는 독자들을 위해 몇몇웹사이트들을 소개하며 이번 호 강좌를 마무리 지려고 한다.
일반적으로 커널 프로그래밍은 어렵다고 생각한다. 왜 그렇게 생각하는 것일까? 당연히 커널 프로그래밍을 하기 위해서는 리눅스 커널에 관한 많은 지식들을 가지고 있어야만 한다. 적어도 자신이 지금 하려는 것이 커널의 어떤 서브 컴포넌트들과 어떤 인터페이스들을 통해 동작하는지 정확히 이해하고 있어야만 한다. 하지만 이러한 것들은 지금 막 커널 프로그래밍
에 입문하는 사람들에게는 별로 와 닿지 않을 것이다. 이러한 문제들은 프로젝트를 진행하며 점차 커널에관한 지식을 쌓아가며 또는 사전에 충분한 스터디를 통해 해결해야 할 문제들이다. 그러한 문제들은 결코 단 시간에 해결할 수 있는 문제가 아니라는 것이다. 그보다 먼저 부딪히는 문제들은 다음과 같다.
첫째, 라이브러리와 시스템콜을 사용할 수 없다. 처음 입문하는 커널 프로그래머들에게 가장 먼저 다가오는 괴로움은 응용프로그래밍에서와 같이 많은 라이브러리와 시스템콜(System Call) 등을 사용할 수 없다는 것이다. 예를 들어 커널에서 어떤 file을 만들고 싶다면 어떻게 할 것인가? 응용프로그램들처럼 open 시스템콜을 사용할 것인가? 물론 불가능하다.
둘째, 커널이 죽는다면 어떻게 디버깅을 할 것인가? 커널에는 fault가 나면 그것을 해결해 줄 만한 화려한도구가 있지 않다. 임베디드 환경은 더욱 그렇다. 그나마도 몇 개 없지만 x86 시스템에서 잘 동작하는 커널 디버깅 툴들이 대부분 arm이나 mips 등의 임베디드 환경에서는 동작하지 않는 경우가 많다. 그래서 대부분 커널 개발자들은 printk와 같은 원시적인 방법
에 의존하고 있는 것이 사실이다. 하지만 x86환경에서는 보다 좋은 툴들이 몇 가지 있다.
셋째, 커널은 한정된 스택 사이즈를 갖는다. 일반 응용 프로그램들은 동적으로 증가될 수 있는 스택을 갖는다(물론 한계는있다). 반면, 커널은 기본적으로 8K의 스택을 갖는다. 또한 동적으로 커질 수도 없다. 하지만 현재 x86에서는 8K도 너무 커서, 스택 사이즈를 4K로 줄이는 패치가 들어가 있다. 그러므로 커널 프로그래밍에서는 절대적으로 스택을 아껴서 사용해
야 한다. 게다가 커널 스택은 자신만이 사용하는 것이 아니다. 커널의 여러 Kernel Control Path에서 한 프로세스의 스택을 공유하여 사용할 수 있으므로, 특정 Path에서 너무 많은 스택을 사용하게 되면 나중에 사용하게 될 Path에서는 더 이상 사용할 수 있는 공간이 남아 있지 않게 된다. 이문제에 대해서는 나중에 소개할 기회가 있을 것이다. 기억해야 할 것은‘스택은
절대 4K 이상 넘어갈 수 없다. 또한 내가 사용하는 이 스택은 나 혼자만 쓰는 것이 아니다’라는 점이다. 넷째로, 커널에는 메모리 보호장치가 없다는 것이다. 응용 프로그램은 리눅스의 MMU(Memory Managet Unit)를 이용한 가상 페이지 할당방식으로 인해 절대 다른 프로세스의 주소를 침범할 수 없다. 또한 자신의 주소 공간에서도 금지된(illegal)주소 공간을 침범하게 되면 커널은 error를 잡아내고 그 프로세스에게 신호(Signal)를 전달한다.
이 신호에 대해서 대비하여 놓지 않은 일반 프로세스들은 Segment Fault를 일으키며 죽게 된다. 다른 프로세스들은 절대 영향을 받지 않으며 자신들만의 주소공간에서 안정적으로 실행되고 있을 것이다. 하지만 커널 프로그래밍은 상황이 전혀 다르다. 커널이 금지된(illegal) 메모리 주소에 접근하게 되면 결과는 Oops라는 형태로 나타나게 된다. Oops로 인하여 커널이 죽게 되면 시스템이 다운되는 것이다. 이것은 어떤 환경에서도 용납될 수 없는 문제이다. 마치 윈도의 블루스크린과 같다
필자 김민찬 씨는 운영체제에 많은 관심을 갖고 연구해 왔으며 현재는 kldp.org(리눅스 한글 문서화 프로젝트) 멤버로 활동하며, 리눅스 커널과 glibc와 관련된 개발 업무를 담당하고 있다.
출처 : 공개 SW 리포트 7호 페이지 52 ~ 57 발췌(2007년 6월) - 한국소프트웨어 진흥원 공개SW사업팀 발간
'L I N U X' 카테고리의 다른 글
Step by Step 커널 프로그래밍 강좌⑥ (0) | 2012.02.08 |
---|---|
Step by Step 커널 프로그래밍 강좌⑤ (0) | 2012.02.08 |
Step by Step 커널 프로그래밍 강좌④ (0) | 2012.02.08 |
Step by Step 커널 프로그래밍 강좌③ (0) | 2012.02.08 |
Step by Step 커널 프로그래밍 강좌② (0) | 2012.02.08 |