Linux Kernel을 공부하기 위해 Basic한 내용부터 차근차근 알아보자.

Installation
$ sudo apt-get install build-essential make
  • build-essential : C/C++ 등을 개발할 때 필요한 기본 Library, Header File을 가지고 있음.
  • make : 프로그램 그룹을 유지하는데 필요한 유틸리티.

What is Kernel?

커널은 운영 체제의 핵심 부분이다. 우리가 사용하는 OS는 커널 위에 여러가지 레이어를 올린 것이다. 구체적인 설명을 하면 끝도 없으므로 이하 생략하겠다.

What is Kernel Module?

Module은 프로그램의 한 조각이다. 커널은 Module이 한데 모아 이루어져 있는 것이고, 우리는 Module들을 생성하여 추가함으로 Kernel에 변화를 줄 수 있는 것이다. Kernel Module은 Kernel을 다시 빌드하거나 컴파일할 필요없이 기능성을 확장시킬 수 있다.

  • lsmod : 이미 실행중인 모듈들의 리스트를 보여준다.
baek@ubuntu:~/Kernel/sample$ lsmod
Module                  Size  Used by
rfcomm                 81920  4
intel_rapl_msr         20480  0
bnep                   24576  2
intel_rapl_common      24576  1 intel_rapl_msr
crct10dif_pclmul       16384  1
crc32_pclmul           16384  0
ghash_clmulni_intel    16384  0
vmw_balloon            24576  0
aesni_intel           372736  0
aes_x86_64             20480  1 aesni_intel
crypto_simd            16384  1 aesni_intel
cryptd                 24576  2 crypto_simd,ghash_clmulni_intel
glue_helper            16384  1 aesni_intel
intel_rapl_perf        20480  0
...

Basic Kernel Module

  • init_module() : Module이 Kernel에 삽입될 때 동작 해야하는 코드를 포함한다.
  • cleanup_module() : Module이 Kernel에 제거될 때 동작 해야하는 코드를 포함한다.
  • printk() : 출력 메시지의 레벨을 조절하여 출력한다. KERN_INFO는 레벨을 의미한다. 레벨들은 linux/kernel.h 에 정의되어 있다.

간단한 코드를 만들어보자.

#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_INFO */

int init_module(void) {
    printk(KERN_INFO "Welcome to Linux Kernel!\n");
    return 0;
}

void cleanup_module(void) {
    printk(KERN_INFO "Bye bye to Linux Kernel!\n");
}

Using Makefile

이 Module을 Build하기 위해선 Makefile을 이용하면 된다. 우리는 보통 c파일을 빌드할 때,

$ gcc -o main main.c

와 같이 빌드를 한다. 그런데 세 개의 소스파일을 Link하여 빌드하고 싶다면,

$ gcc -c -o main.o main.c
$ gcc -c -o foo.o foo.c
$ gcc -c -o bar.o bar.c
$ gcc -o app.out main.o foo.o bar.o

처럼 빌드를 해야 한다. 이런 명령들을 하나씩 치는 것은 매우 번거롭다. 그래서 우리는 보통 쉘 스크립트 파일을 만든다. Makefile 또한 그러한 역할 중 하난데 좋은 건 Incremental build 라는 것을 할 수 있다는 것이다. Incremental build란 반복적인 빌드 과정에서 변경된 소스코드에 의존성이 있는 대상들만 다시 빌드를 하는 것이다. 위의 코드로 예시를 들어보자. main.c만 변경하면, main.o만 다시 빌드된다는 것이다.

위의 코드를 Makefile로 작성해 보겠다.

app.out: main.o foo.o bar.o
    gcc -o app.out main.o foo.o bar.o

main.o: foo.h bar.h main.c
    gcc -c -o main.o main.c

foo.o: foo.h foo.c
    gcc -c -o foo.o foo.c

bar.o: bar.h bar.c
    gcc -c -o bar.o bar.c

이렇게 작성하고,

$ make

해주게 되면 app.out이 만들어진다. Makefile을 살펴보면, 반복적인 구조가 나타난다.

<Target>: <Dependencies>
    <Recipe>
  • Target: 빌드 대상 이름. 통상적으로 파일명을 써준다.
  • Dependencies: 빌드 대상이 의존하는 Target이나 파일 목록. 여기에 나열된 대상들을 먼저 만들어준다.
  • Recipe: 빌드 대상을 생성하는 명령이다. Indent 필수.

이제 우리가 하던 코드로 넘어와서 c파일이 있는 디렉토리에 Makefile을 작성하자.

obj-m += sample.o // add object file name in obj

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
baek@ubuntu:~/Kernel$ uname -r
5.3.0-42-generic # kernel version

make하면 다음과 같은 오류가 뜰 수 있다.

baek@ubuntu:~/Kernel$ make
Makefile:4: *** missing separator.  Stop.

이는 tab이 아닌 space가 코드 안에 있다는 뜻이므로 space를 없애주면 된다.

baek@ubuntu:~/Kernel$ make
make -C /lib/modules/5.3.0-42-generic/build M=/home/baek/Kernel modules
make[1]: Entering directory '/usr/src/linux-headers-5.3.0-42-generic'
  CC [M]  /home/baek/Kernel/sample.o
  Building modules, stage 2.
  MODPOST 1 modules
WARNING: modpost: missing MODULE_LICENSE() in /home/baek/Kernel/sample.o
see include/linux/module.h for more information
  CC      /home/baek/Kernel/sample.mod.o
  LD [M]  /home/baek/Kernel/sample.ko
make[1]: Leaving directory '/usr/src/linux-headers-5.3.0-42-generic'
baek@ubuntu:~/Kernel/sample$ ls
Makefile       Module.symvers  sample.ko   sample.mod.c  sample.o
modules.order  sample.c        sample.mod  sample.mod.o

굉장히 많은 파일이 생성되었다. 각각 어떤 파일인지 알아보자.

  • Module.symvers : 심볼 테이블이다. 함수의 위치 정보를 가지고 있다.
  • sample.ko : 커널 모듈이다.
  • sample.mod.c : 기본적으로 커널에 모듈을 삽입할 때 필요한 모듈의 정보들을 포함하고 있다.
  • modules.order : .ko 파일의 위치가 저장되어 있다.
  • sample.mod : object 파일(sample.o)의 위치가 저장되어 있다.

modinfo 라는 명령어를 이용하면 Kernel module의 정보를 확인할 수 있다.

baek@ubuntu:~/Kernel/sample$ modinfo sample.ko
filename:       /home/baek/Kernel/sample/sample.ko
srcversion:     F737E5AC51C880721D61129
depends:        
retpoline:      Y
name:           sample
vermagic:       5.3.0-42-generic SMP mod_unload

insmod 라는 명령어로 Kernel에 Module을 등록할 수 있다.

rmmod 라는 명령어로 Kernel에 등록된 Module을 제거할 수 있다.

baek@ubuntu:~/Kernel/sample$ sudo insmod sample.ko
baek@ubuntu:~/Kernel/sample$ lsmod | grep "sample"
sample                 16384  0

그런데 출력되어야할 Welcome to Linux Kernel! 이 보이지 않는다. 이는 user에게 직접 보여지는 부분이 아니므로 dmesg 명령어를 이용하여 메시지를 확인할 수 있다.

baek@ubuntu:~/Kernel/sample$ dmesg
...
[ 7943.255744] Welcome to Linux Kernel!
baek@ubuntu:~/Kernel/sample$ sudo rmmod sample
baek@ubuntu:~/Kernel/sample$ lsmod | grep "sample"
baek@ubuntu:~/Kernel/sample$ dmesg
...
[ 8221.560491] Bye bye to Linux Kernel!

이렇게 간단한 모듈을 생성하고 insert하는 법까지 살펴보았다.


참고문헌

[1] https://www.lazenca.net/display/TEC/01.Development+of+Kernel+Module

[2] https://www.tuwlab.com/ece/27193

'Linux Kernel' 카테고리의 다른 글

ioctl(Input/Output control)  (0) 2020.04.07
Device Driver - Character  (0) 2020.04.04

+ Recent posts