본문 바로가기

programming/Linux

hello 모듈 빌드하고 커널에 적재하기, hello 모듈 컴파일



hello 모듈 : "Ubuntu 16.04.5 LTS" , 4.15.0-33-generic에서 진행


함수 실행시간 측정 예제 : "Ubuntu 18.04.2 LTS", 5.1.5에서 진행(출처: https://jjudrgn.tistory.com/25 [jjudrgn's note])




커널에 모듈을 적재하고 메세지를 띄우기 위해서는 다음과정이 필요하다.


.ko파일 만들기

hello.c 와 Makefile을 만들고 make

모듈 로드

insmod hello.ko

메세지 확인하기

dmesg



먼저 hello.c파일을 만든다

hello.c


#include <linux/init.h>

#include <linux/module.h>

#include <linux/kernel.h>


static int __init init_hello(void){

        printk(KERN_ALERT "Hello, kernel!\n");

        return 0;

}


static void __exit exit_hello(void){

        printk(KERN_ALERT "Good-bye, kernel!\n");

}


module_init(init_hello);

module_exit(exit_hello);

MODULE_LICENSE("GPL");


커널이 모듈을 로드할때와 커널이 모듈을 제거할때 사용할 때 사용할 두개의 함수를 정의한다.  정의한 함수안에는 printk를 이용해서 log를 남긴다.

이떄 주의할점은 module_init라는 매크로와 module_exit에 들어갈 함수는 return을 주의해야 한다. 


필자는 첫 hello.c파일을 만들시 int나 void를 쓰지 않거나 const형으로 써서

return from incompatible pointer type [-Werror=incompatible-pointer-types]에러가 발생했다

/home/jy-os/lab/test2/hello.c:5:15: warning: return type defaults to ‘int’ [-Wreturn-type]

 static __init init_hello(void){

               ^

/home/jy-os/lab/test2/hello.c:10:15: warning: return type defaults to ‘int’ [-Wreturn-type]

 static __exit exit_hello(void){

               ^

In file included from /home/jy-os/lab/test2/hello.c:2:0:

/home/jy-os/lab/test2/hello.c: In function ‘__exittest’:

/home/jy-os/lab/test2/hello.c:15:13: error: return from incompatible pointer type [-Werror=incompatible-pointer-types]

 module_exit(exit_hello);

             ^

./include/linux/module.h:138:11: note: in definition of macro ‘module_exit’

  { return exitfn; }     \

           ^

/home/jy-os/lab/test2/hello.c: In function ‘exit_hello’:

/home/jy-os/lab/test2/hello.c:12:1: warning: control reaches end of non-void function [-Wreturn-type]

 }




위처럼 hello.c파일을 만든후 같은 폴더안에 Makefile을 만든다.


2.6버전 이후부터는 .ko파일을 만들기 위해 gcc말고 Makefile을 만들어 줘야한다. 

Makefile


obj-m = hello.o


KDIR := /lib/modules/$(shell uname -r)/build

PWD := $(shell pwd)


default:

$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules


clean:

rm -rf *.kr *.mod.* .*.cmd *.o



obj-m은 목적파일이다. 이떄 m은 모듈, y는 built-in 형식의 ko파일을 만든다.

KDIR은 Kernel Source Directory이다. 현재 커널소스의 디렉토리는 /lib/modules/$(shell uname -r)/build에 링크 되어있다.)


PWD는 현재 디렉토리 위치인데

PWD := $(shell pwd) 또는 PWD := `pwd`를 써도된다. ( 이떄 `는 '와 다르다.)


clean은 빌드결과물과 부산물을 삭제하는 Clean Rule으로 이다.

Makefile을 작성하는 공식 전체 메뉴얼 : 

http://www.gnu.org/software/make/manual/make.html


이렇게 Makefile을 만든후 

#make

를 실행하면 현재 폴더에 .ko파일이 생성된다.

(모듈 관련 명령어는 root 권한으로 실행해야한다.  Permission denied 에러가 발생할시 sudo를 앞에 붙이거나 root로 로그인하면 된다.)


이후 만든 모듈을 적재할때는 insmod명령어를 사용하면 된다.

#insmod hello.ko


(insmod error: inserting './hello.ko': -1 Invalid module format"같은 에fj가 뜬다면 https://stackoverflow.com/questions/34379013/insmod-error-inserting-hello-ko-1-invalid-module-format

위사이트를 참고하길 바란다)

insmod: ERROR: could not insert module vhost_net.ko: Unknown symbol in module
와 같은 에러는 심볼을 등록하지 않아서 생기는 문제이다.
dmseg를 통해서 자세한 내용을 확인할수 있다.
#dmesg | tail
[  188.232085] vhost_net: Unknown symbol tap_get_ptr_ring (err -2)
[  188.232166] vhost_net: Unknown symbol tap_get_socket (err -2)
위의 함수 tap_get_prt_ring, tap_get_socket 의 심볼이 등록되어 있지 않다는 의미이다..
필자 같은 경우는 기존의 vhost를 수정후 올리려는 작업을 하려고 했기때문에 virsh start vm1등과 같이 kvm 실행을 통해서 자동으로 vhost를 커널에 적재한후 rmmod vhost으로 내린다음 내가 수정한 모듈을 다시 올리니 에러가 다시 뜨지 않았다.




다른 모듈관련 명령어는 아래와 같이 있다.

모듈확인

#lsmod


모듈삭제

#rmmod hello


그후 dmesg를 사용하여 log를 확인할수 있다.

#dmesg


.

.

.

.

.

.

이런식으로도 가능하다..(4.19.0)

obj-m = hello.o


default:

make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules

clean:

rm -rf *.mod.*

rm -rf *.o

rm -rf *.ko

rm -rf *.order

rm -rf *.symvers




모듈 컴파일과 현재 커널의 버전이 다를경우 에러가 나면서 insmod가 안되니 확인하자.