NetBSD 커널 모듈 샘플을 사용해보십시오

15570 단어 AdventCalendarNetBSD
NetBSD Advent Calendar 2018 3일째 기사입니다.
오늘은 NetBSD 커널 모듈에 대해 이야기하려고합니다.

커널 모듈



커널 모듈에 대해서는 module(7) 의 설명을 보는 것이 빠르다. 커널 모듈은 시스템 관리자가 가동중인 시스템에 동적으로 추가 및 삭제할 수 있는 기능으로 개발자가 재부팅을 필요로 하지 않는 형태로 커널 기능을 추가할 수 있습니다.
DESCRIPTION
     Kernel modules allow the system administrator to dynamically add and
     remove functionality from a running system.  This also helps software
     developers add new parts of the kernel without constantly rebooting to
     test their changes.

커널 모듈 샘플



NetBSD 소스 트리 sys/modules/examples 아래에는 커널 모듈 샘플이 있습니다.
이번에는 이 샘플을 바탕으로 커널 모듈을 사용해 봅시다.
# cd /usr/src/sys/modules/examples
# ls -F
CVS/           Makefile.inc   hello/         luareadhappy/  properties/
Makefile       README         luahello/      ping/          readhappy/

커널 모듈을 빌드하는 환경 만들기



커널 모듈의 샘플 코드는 빌드 툴 체인이 생성한 경로에 gcc가 있다고 가정한 Makefile입니다.
따라서 커널 모듈을 빌드하려면 build.sh에서 셀프 호스트 용 툴 체인을 구축하는 것이 빠른 방법입니다.
(전제적으로 /usr/src에 커널 소스가 배포되었다고 가정합니다.)

다음 단계를 통해 x86_64용 툴체인을 빌드합니다.
$ sudo bash
# cd /usr/src
# ./build.sh -m amd64 -a x86_64 -T /usr/src/tooldir.NetBSD-8.0-amd64 -r -o -U tools

모듈 샘플을 만져보세요



즉시 커널 모듈의 샘플을 만져보십시오.
샘플에는 helloreadhappy 라는 작은 2 개의 모듈이 준비되어 있으므로, 각각을 보자.

hello 모듈



먼저 hello 모듈입니다. 코멘트를 제외하면 30행 정도의 소스 코드입니다.
# cd hello
# ls -F
CVS/      Makefile  hello.c
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: hello.c,v 1.1 2015/05/13 07:07:36 pgoyette Exp $");

#include <sys/param.h>
#include <sys/module.h>

/*
 * Last parameter of MODULE macro is a list of names (as string; names are
 * separated by commas) of dependencies.  If module has no dependencies,
 * then NULL should be passed.
 */

MODULE(MODULE_CLASS_MISC, hello, NULL);

static int
hello_modcmd(modcmd_t cmd, void *arg __unused)
{
        switch (cmd) {
        case MODULE_CMD_INIT:
                printf("Example module loaded.\n");
                break;

        case MODULE_CMD_FINI:
                printf("Example module unloaded.\n");
                break;

        case MODULE_CMD_STAT:
                printf("Example module status queried.\n");
                break;

        default:
                return ENOTTY;
        }

        return 0;
}
make 명령으로 직접 빌드할 수 있습니다.
# make
# ls -F
CVS/             amd64@           hello.kmod       hello.o          machine@
Makefile         hello.c          hello.kmod.map   i386@            x86@

커널 모듈 로드



커널 모듈은 modload 명령으로 로드됩니다.
# cd /usr/src/sys/modules/examples/hello/
#
# # まだhelloモジュールはロードされていない
# modstat | grep hello | wc -l
       0
#
# # modloadコマンドでロード。
# # 「./」で相対パス指定にしないとロードエラーになるので注意。
# modload ./hello.kmod
#
# # helloモジュールがロードされていることを確認する。
# modstat | egrep 'NAME|hello'
NAME                    CLASS    SOURCE   FLAG  REFS    SIZE REQUIRES
hello                   misc     filesys  -        0      77 -

소스 코드와 대조하는 형태로, 커널 모듈의 로드 언로드가 어떻게 행동하는지를 살펴봅니다.
static int
hello_modcmd(modcmd_t cmd, void *arg __unused)
{
        switch (cmd) {
        case MODULE_CMD_INIT:
                printf("Example module loaded.\n");
                break;

모듈을로드하면 ....
# modunload hello

해당 처리는 hello_modcmd() 함수의 MODULE_CMD_INIT를 처리합니다.
static int
hello_modcmd(modcmd_t cmd, void *arg __unused)
{
        switch (cmd) {
...中略...
        case MODULE_CMD_INIT:
                printf("Example module loaded.\n");
                break;
modunload의 경우 같은 함수의 MODULE_CMD_FINI가 처리됩니다.
        case MODULE_CMD_FINI:
                printf("Example module unloaded.\n");
                break;

readhappy 모듈



그런 다음 readhappy 모듈을 살펴 보겠습니다. 이쪽도 심플한 코드 같네요.
# cd /usr/src/sys/modules/examples/readhappy
# ls -F
CVS/         Makefile     readhappy.c

여기도 make 명령으로 빌드하고 modload 명령으로로드합니다.
# modload ./readhappy.kmod
# modstat | egrep '^NAME|happy'
NAME                    CLASS    SOURCE   FLAG  REFS    SIZE REQUIRES
happy                   misc     filesys  -        0     377 -
readhappy.c 의 소스 코드 코멘트를 보면 이하의 기술이 있으므로, 그에 따라 mknod 명령을 실행합니다.
 * Create a device /dev/happy from which you can read sequential
 * happy numbers.
 *
 * To use this device you need to do:
 *     mknod /dev/happy c 210 0
# mknod /dev/happy c 210 0
# ls -l /dev/happy
crw-r--r--  1 root  wheel  210, 0 Dec  3 18:58 /dev/happy
# file /dev/happy
/dev/happy: character special (210/0)

소스 코드를 보면 readhappy.cstruct cdevsw.d_readread(2)에 해당하는 함수가 연결됩니다. 그런 다음 연결 된 happy_read() 함수에 적절한 printf()를 삽입하여 동작을 살펴 보겠습니다.
static struct cdevsw happy_cdevsw = {
        ...中略...
        .d_read = happy_read,
...中略...

happy_read(dev_t self __unused, struct uio *uio, int flags __unused)
{
        ...中略...
        /* Send it to User-Space */
        int e;
        printf("-=> (^_^)/ %s", line);  // 確認用に追加した行。
        if ((e = uiomove(line, len, uio)))
                return e;

사용자 랜드 측에서 간단한 샘플 코드를 준비하고 /dev/happy에서 데이터를 읽어보십시오.
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
        char buf[BUFSIZ];
        int fd, i;

        fd = open("/dev/happy", O_RDONLY);
        if (fd == -1) {
                fprintf(stderr, "open() failed.\n");
                exit(-1);
        }

        for (i = 0; i < argc; i++) {
                read(fd, buf, BUFSIZ);
                printf("%s", buf);
        }
        close(fd);

        return 0;
}

데이터를 read() 할 때마다 printf() 에서 추가한 문장이 커널 메시지로서 출력되고 있는 것과 커널내의 값 (데이터)이 read() 로 유저 랜드측에서도 취득할 수 있는 것을 알 수 있습니다.


요약



NetBSD 커널 모듈에 대해 제공된 샘플을 바탕으로 빌드 및 로드 방법을 소개했습니다.
특히 readhappy.c 는 단순하면서도, 유저 랜드에의 데이터의 건네주는 방법 등, 삼키기를 누른 샘플이 되어 있습니다.

커널 모듈의 샘플에는, 그 밖에도 Lua로 기술된 커널 모듈도 포함되어 있으므로, 이쪽도 나중에 소개할 수 있으면 좋겠습니다.

좋은 웹페이지 즐겨찾기