(리눅스 시스템 프로그래밍) LKM + C 예제코드
by 줌코딩
들어가기 앞서
이번 학기 OS를 공부하면서 처음으로 프로그래밍 과제가 나왔다.
학기 시작하고 2주간 어색한 OS 개념들을 어렴풋이 이해해왔는데 이번 기회를 통해서 좀 더 실질적으로 이해할 수 있기를 바란다.
첫 목표
Create a Linux kernel module (LKM) that works as an agent in kernel space for your commands
- Log the names of files that a user has accessed
- Prevent a kill to a specified process
- Hide the dogdoor module from the module list
LKM이란?
LKM이 없다면?
- 리눅스 커널은 드라이브를 다 수용해야하기 때문에 엄청 커야한다.
- 하드웨어가 추가되거나 드라이버가 업데이트 될 때마다 커널을 리빌드 해야한다.
LKM의 단점은?
- 파일을 드라이버에서 각각 관리해야한다.
LKM을 위한 환경설정하기
$ sudo apt-get update
$ apt-cache search linux-headers-$(uname -r)
linux-headers-4.15.0-041500-generic - Linux kernel headers for version 4.15.0 on 64 bit x86 SMP
$ sudo apt-get install (헤더명)
$ cd /usr/src/(헤더명)
$ ls
이때 주의할 점은 자신한테 맞는 header를 설치해야 한다.
apt-cache search linux-headers-$(uname -r) 명령어를 통해 나온 header를 써야한다
**jinhyeok@CRA230**:**~**$ apt-cache search linux-headers-$(uname -r)
linux-headers-4.15.0-041500-generic - Linux kernel headers for version 4.15.0 on 64 bit x86 SMP
Module Code시 주의할 점
- 순서대로 동작하지 않는다. (Event Driven과 같다)
- allocate된거는 반드시 release되어야 한다.
- printf() 함수가 없다. (printk()를 이용해라)
- interrupt가 있을 수 있기 때문에 이에 대한 대비도 해야한다.
- Kernel Module에 우선적으로 CPU cycle이 할당되므로 조심해서 코딩해야한다.
- Floating Point 모드가 존재하지 않는다.
LKM 예제 코드 The Hello World
/**
* @file hello.c
* @author Jinhyeok Jeong
* @date 24 March 2019
* @version 0.1
* @brief An introductory "Hello World!" loadable kernel module (LKM) that can display a message
* in the /var/log/kern.log file when the module is loaded and removed. The module can accept an
* argument when it is loaded -- the name, which appears in the kernel log files.
* @see http://www.derekmolloy.ie/ for a full description and follow-up descriptions.
*/
#include <linux/init.h> // Macros used to mark up functions e.g., __init __exit
#include <linux/module.h> // Core header for loading LKMs into the kernel
#include <linux/kernel.h> // Contains types, macros, functions for the kernel
MODULE_LICENSE("GPL"); ///< The license type -- this affects runtime behavior
MODULE_AUTHOR("Derek Molloy"); ///< The author -- visible when you use modinfo
MODULE_DESCRIPTION("A simple Linux driver for the BBB."); ///< The description -- see modinfo
MODULE_VERSION("0.1"); ///< The version of the module
static char *name = "world"; ///< An example LKM argument -- default value is "world"
module_param(name, charp, S_IRUGO); ///< Param desc. charp = char ptr, S_IRUGO can be read/not changed
MODULE_PARM_DESC(name, "The name to display in /var/log/kern.log"); ///< parameter description
/** @brief The LKM initialization function
* The static keyword restricts the visibility of the function to within this C file. The __init
* macro means that for a built-in driver (not a LKM) the function is only used at initialization
* time and that it can be discarded and its memory freed up after that point.
* @return returns 0 if successful
*/
static int __init helloBBB_init(void){
printk(KERN_INFO "EBB: Hello %s from the BBB LKM!\n", name);
return 0;
}
/** @brief The LKM cleanup function
* Similar to the initialization function, it is static. The __exit macro notifies that if this
* code is used for a built-in driver (not a LKM) that this function is not required.
*/
static void __exit helloBBB_exit(void){
printk(KERN_INFO "EBB: Goodbye %s from the BBB LKM!\n", name);
}
/** @brief A module must use the module_init() module_exit() macros from linux/init.h, which
* identify the initialization function at insertion time and the cleanup function (as
* listed above)
*/
module_init(helloBBB_init);
module_exit(helloBBB_exit);
- 16번째줄 - MODULE_LICENSE(“GPL”)은 모듈의 라이센스 정보를 제공하는 것이다
- 21번째줄 - hello를 static char*으로 저장했다.
- 커널모드에서는 글로벌 변수 사용하면 안됌(커널에서 다 돌꺼기 때문에) 반드시 static으로 제한해야한다.
- 22번째줄 - module_param(name, type, permissions);
- 파라미터 3개, 이름, 파라미터 타입, 접근 권한 이렇게 세개를 받는다.
- charp : char pointer
- S_IRUGO : user/group/others에게 읽기 권한 제공
- 31, 40번째줄 - 함수는 아무이름으로 해도된다
- 대신 48, 49번째 줄과 같이 module_init()과 module_exit()으로 꼭 보내져야 함
- 31번째줄 - printk는 printf와 유사하다
- 다른 점은 log level 적어보내야 한다.
예제코드 빌드하기
-
커널 모듈을 빌드하기 위해서는 반드시 makefile이 필요하다.
obj-m += hello.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
- 1번째줄 - 이 makefile의 목표를 정의한다.
- 나머지는 보통 makefile과 유사하다
- -C는 kernel directory로 switch시켜주는 용도
- M=$(PWD)는 프로젝트 파일이 실제 존재하는 위치를 make에 알려주는 용도
- $(shell uname -r)은 현재 kernel build 버전을 전해주는 용도이다.
빌드 결과 모습
jinhyeok@CRA230:~/Linux_Kernel_Programming$ make
make -C /lib/modules/4.15.0-041500-generic/build M=/home/jinhyeok/Linux_Kernel_Programming modules
make[1]: 디렉터리 '/usr/src/linux-headers-4.15.0-041500-generic' 들어감
CC [M] /home/jinhyeok/Linux_Kernel_Programming/hello.o
Building modules, stage 2.
MODPOST 1 modules
CC /home/jinhyeok/Linux_Kernel_Programming/hello.mod.o
LD [M] /home/jinhyeok/Linux_Kernel_Programming/hello.ko
make[1]: 디렉터리 '/usr/src/linux-headers-4.15.0-041500-generic' 나감
jinhyeok@CRA230:~/Linux_Kernel_Programming$ ls
Makefile hello.c hello.mod.c hello.o
Module.symvers hello.ko hello.mod.o modules.order
hello.ko 파일이 바로 loadable kernel module이다!!
LKM 테스트하기
- insmode와 rmmod로 LKM을 로드하고 언로드할 수 있다.
- 그리고 로그의 결과물은 kern.log에 있어서 tail이라는 명령어를 통해 확인 가능하다.
//로드하기
sudo insmode hello.ko
//언로드하기
sudo rmmod hello.ko
더블 탭으로 실시간으로 로그가 찍히는 것을 확인할 수 있다.
jinhyeok@CRA230:/var/log$ cd /var/log
jinhyeok@CRA230:/var/log$ sudo tail -f kern.log
Mar 24 17:08:08 CRA230 kernel: [141413.098009] EBB: Hello world from the BBB LKM!
Mar 24 17:13:07 CRA230 kernel: [141711.746656] EBB: Goodbye world from the BBB LKM!
LKM을 파라미터를 줘서 테스트하기
jinhyeok@CRA230:~/Linux_Kernel_Programming$ sudo insmod hello.ko name=jinhyeok
jinhyeok@CRA230:~/Linux_Kernel_Programming$ sudo rmmod hello.ko
뿐만 아니라 proc를 이용해서도 확인이 가능하다.
Proc Interface(procfs/proc) 이란?
- Virtual file system where files act as agents for a kernel data structure or kernel module to interact with user-level programs
- 내부 커널 정보 및 디바이스 정보를 파일형태로 유저에게 제공
- LKM이 로드된 정보가 /proc에도 표시된다.
jinhyeok@CRA230:/proc$ cd /proc
jinhyeok@CRA230:/proc$ cat modules|grep hello
hello 16384 0 - Live 0x (null) (OE)
파라미터는 /sys/module에서 확인이 가능하다
jinhyeok@CRA230:~$ cd /sys/module/hello/parameters/
jinhyeok@CRA230:/sys/module/hello/parameters$ cat name
jinhyeok
- hello라는 디렉토리 밑에 있는 parameter에 name의 정보가 들어있는 것을 볼 수 있다.
정리
- LKM은 썼다 안썼다가 가능한 kernel의 보조장치 같은 역할인 듯 하다.
- 본격적으로 나도 한번 모듈을 짜보자.
이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
Subscribe via RSS