기본 콘텐츠로 건너뛰기

kernel Hello World

리눅스 커널 모듈 프로그래밍 가이드
참조:http://www.tldp.org/LDP/lkmpg/2.6/html/lkmpg.html

커널 모듈 가이드

아래 테스트는 우분투 14.04에서 테스트 되었습니다.

모듈 유틸리티

insmod:모듈을 커널에 적재한다.
rmmod:커널에서 모듈을 제거한다.
lsmod:커널에서 적재된 모듈 목록을 보여준다.
depmod: 모듈간 의존성 정보를 생성한다.
modprobe:모듈을 커널에 적재하거나 제거한다.
modinfo rdma_ucm


모듈 정보 보기:

#lsmod
#cat /etc/modprobe.conf
#modinfo ib_core

커널에서 제공하는 심볼 테이블
:커널 내부의 함수나 변수 중 외부에서 참조할 수 있는 함수의 심볼과 주소를 담은 테이블
#cat /proc/kallsyms

커널 모듈 제작해 보기


kernel ver 3.16

hello.c

/*
 *  hello.c - The simplest kernel module.
 */
#
#include <linux/module.h>
#include <linux/kernel.h>

int init_module(void)
{
    printk("<1>Hello World 1.\n");
    return 0;
}

void cleanup_module(void)
{
    printk(KERN_ALERT "Goodbye world 1.\n");
}
   


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


#make

make -C /lib/modules/3.16.0-30-generic/build M=/opt/kernel/base modules
make[1]: Entering directory `/usr/src/linux-headers-3.16.0-30-generic'
  CC [M]  /opt/kernel/base/hello.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /opt/kernel/base/hello.mod.o
  LD [M]  /opt/kernel/base/hello.ko
make[1]: Leaving directory `/usr/src/linux-headers-3.16.0-30-generic'

#modinfo hello.ko
filename:       /opt/kernel/base/hello.ko
srcversion:     140276773A3090F6F33891F
depends:
vermagic:       3.16.0-30-generic SMP mod_unload modversions

#insmod hello.ko

아무내용이 나오지 않는다.

#dmesg
[27693.264826] Hello world 1.
맨 마지막에 볼수 있다.

insmod: ERROR: could not insert module rdma_krping.ko: Invalid parameters
현재 모듈이 다른 모듈의 함수를 사용했을 경우다. 
이때는 사용하려는 해당 모듈이 컴파일 되기전에 함수에 대한 심볼 테이블이 필요하다.
만약 foo.ko 가 bar.ko 의 함수를 사용한다면 우선 bar.ko 를 컴파일 한다. 
컴파일하면 해당 폴더에 Module.symvers 란 파일이 생성된다. 
이 파일을 foo.ko 가 컴파일될 폴더에 복사한후 foo.ko를 컴파일하면 에러 없이 컴파일되고 에러 없이 insmod 될 것이다.
혹은 makefile 에 KBUILD_EXTRA_SYMBOLS += {Module.symvers} 를 추가한다.


*. kthread

  대다수의 커널 쓰레드 들은 kthread 를 사용하여 구현되어 있습니다. (커널 소스 트리 검색)

  쓰레드 생성 api
    kthread_create();
    kthread_run();

  쓰레드 정지 api
    kthread_stop();
    kthread_should_stop();

  그외 api
    kthread_bind();
    kthread_data();
    kthreadadd();
    kthread_worker();
    init_kthread_worker();
    init_kthread_work();
    kthread_worker_fn();
    queue_kthread_work();
    flush_kthread_work();
    flush_kthread_worker();

  많은 API 가 있지만 주로 사용하는 API 는 아래와 같습니다. 
    kthread_create();        : 쓰레드 생성
    kthread_run();            : 쓰레드 생성 + 잘생성되었는지 검사
    kthread_stop();           : 쓰레드 종료하라고 명령 내림
    kthread_should_stop(); : 쓰레드 종료 명령이 내려졌는지 검사함. 

    kthread_bind()            : 쓰레드가 사용할 cpu 지정.



참조:http://www.programering.com/a/MDN4IjMwATk.html

간단한 kernel thread source

#include 
#include 
#ifndef SLEEP_MILLI_SEC
#define SLEEP_MILLI_SEC(nMilliSec)\
do { \
long timeout = (nMilliSec) * HZ / 1000; \
while(timeout > 0) \
{ \
timeout = schedule_timeout(timeout); \
} \
}while(0);
#endif

static struct task_struct * MyThread = NULL;

static int MyPrintk(void *data)
{
    char *mydata = kmalloc(strlen(data)+1,GFP_KERNEL);
    memset(mydata,'\0',strlen(data)+1);
    strncpy(mydata,data,strlen(data));
    while(!kthread_should_stop())
    {
        SLEEP_MILLI_SEC(1000);
        printk("%s\n",mydata);
    }
    kfree(mydata);
    return 0;
}

static int __init init_kthread(void)
{
    MyThread = kthread_run(MyPrintk,"hello world","mythread");
    return 0;
}

static void __exit exit_kthread(void)
{
    if(MyThread)
    {
        printk("stop MyThread\n");
        kthread_stop(MyThread);
    }
}
module_init(init_kthread);
module_exit(exit_kthread);



===========================================

커널 스레드로 구현한 udp 서버 프로그램



#include
#include
#include
#include

#include
#include

#include
#include
#include

#include

#define DEFAULT_PORT 2325
#define CONNECT_PORT 23
#define MODULE_NAME "m_server"
#define INADDR_SEND INADDR_LOOPBACK

struct kthread_t
{
        struct task_struct *thread;
        struct socket *sock;
        struct sockaddr_in addr;
        int running;
};

struct kthread_t *kthread = NULL;

int ksocket_receive(struct socket *sock, struct sockaddr_in *addr, unsigned char *buf, int len);
int ksocket_send(struct socket *sock, struct sockaddr_in *addr, unsigned char *buf, int len);

static void ksocket_start(void)
{
        int size, err;
        int bufsize = 10;
        unsigned char buf[bufsize+1];

        /* kernel thread initialization */
        lock_kernel();
        current->flags |= PF_NOFREEZE;

        /* daemonize (take care with signals, after daemonize() they are disabled) */
        daemonize(MODULE_NAME);
        allow_signal(SIGKILL);
        unlock_kernel();
        kthread->running = 0;

        /* create a socket */
        if ( ( (err = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &kthread->sock)) < 0) )
        {
                printk(KERN_INFO MODULE_NAME": Could not create a datagram socket, error = %d\n", -ENXIO);
                goto out;
        }

        memset(&kthread->addr, 0, sizeof(struct sockaddr));
        kthread->addr.sin_family      = AF_INET;

        kthread->addr.sin_addr.s_addr      = htonl(INADDR_ANY);

        kthread->addr.sin_port      = htons(DEFAULT_PORT);

        if ( ( (err = kthread->sock->ops->bind(kthread->sock, (struct sockaddr *)&kthread->addr, sizeof(struct sockaddr) ) ) < 0))
        {
                printk(KERN_INFO MODULE_NAME": Could not bind or connect to socket, error = %d\n", -err);
                goto close_and_out;
        }

        printk(KERN_INFO MODULE_NAME": listening on port %d\n", DEFAULT_PORT);

        /* main loop */
        for (;;)
        {
                memset(&buf, 0, bufsize+1);
                size = ksocket_receive(kthread->sock, &kthread->addr, buf, bufsize);

                if (signal_pending(current))
                        break;

                if (size < 0)
                        printk(KERN_INFO MODULE_NAME": error getting datagram, sock_recvmsg error = %d\n", size);
                else
                {
                        printk(KERN_INFO MODULE_NAME": received %d bytes\n", size);
                        /* data processing */
                        printk("\n data: %s\n", buf);

                        /* sending */
                        memset(&buf, 0, bufsize+1);
                        strcat(buf, "testing...");
                        ksocket_send(kthread->sock, &kthread->addr, buf, strlen(buf));
                }
        }

close_and_out:
        sock_release(kthread->sock);
        kthread->sock = NULL;

out:
        kthread->thread = NULL;
        kthread->running = 0;
}

int ksocket_send(struct socket *sock, struct sockaddr_in *addr, unsigned char *buf, int len)
{
        struct msghdr msg;
        struct iovec iov;
        mm_segment_t oldfs;
        int size = 0;

        if (sock->sk==NULL)
           return 0;

        iov.iov_base = buf;
        iov.iov_len = len;

        msg.msg_flags = 0;
        msg.msg_name = addr;
        msg.msg_namelen  = sizeof(struct sockaddr_in);
        msg.msg_control = NULL;
        msg.msg_controllen = 0;
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
        msg.msg_control = NULL;

        oldfs = get_fs();
        set_fs(KERNEL_DS);
        size = sock_sendmsg(sock,&msg,len);
        set_fs(oldfs);
        return size;
}

int ksocket_receive(struct socket* sock, struct sockaddr_in* addr, unsigned char* buf, int len)
{
        struct msghdr msg;
        struct iovec iov;
        mm_segment_t oldfs;
        int size = 0;

        if (sock->sk==NULL) return 0;

        iov.iov_base = buf;
        iov.iov_len = len;

        msg.msg_flags = 0;
        msg.msg_name = addr;
        msg.msg_namelen  = sizeof(struct sockaddr_in);
        msg.msg_control = NULL;
        msg.msg_controllen = 0;
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
        msg.msg_control = NULL;

        oldfs = get_fs();
        set_fs(KERNEL_DS);
        size = sock_recvmsg(sock,&msg,len,msg.msg_flags);
        set_fs(oldfs);

        return size;
}

int __init ksocket_init(void)
{
        kthread = kmalloc(sizeof(struct kthread_t), GFP_KERNEL);
        memset(kthread, 0, sizeof(struct kthread_t));

        kthread->thread = kthread_run((void *)ksocket_start, NULL, MODULE_NAME);
        if (IS_ERR(kthread->thread))
        {
                printk(KERN_INFO MODULE_NAME": unable to start kernel thread\n");
                kfree(kthread);
                kthread = NULL;
                return -ENOMEM;
        }

        return 0;
}

void __exit ksocket_exit(void)
{

#if 0
        if (kthread->thread==NULL)
                printk(KERN_INFO MODULE_NAME": no kernel thread to kill\n");
        else 
        {
                lock_kernel();
                err = kill_proc(kthread->thread->pid, SIGKILL, 1);
                unlock_kernel();

                if (err < 0)
                        printk(KERN_INFO MODULE_NAME": unknown error %d while trying to terminate kernel thread\n",-err);
                else 
                {
                        while (kthread->running == 1)
                                msleep(10);
                        printk(KERN_INFO MODULE_NAME": succesfully killed kernel thread!\n");
                }
        }
#endif

        if (kthread->running)
                kthread_stop(kthread->thread);
        if (kthread->sock != NULL)
        {
                sock_release(kthread->sock);
                kthread->sock = NULL;
        }

        kfree(kthread);
        kthread = NULL;

        printk(KERN_INFO MODULE_NAME": module unloaded\n");
}

module_init(ksocket_init);
module_exit(ksocket_exit);

MODULE_DESCRIPTION("UDP socket example");
MODULE_AUTHOR("Toni Garcia-Navarro ");

MODULE_LICENSE("GPL");


Proc 프로그래밍 (kernel <-> user) 통신

댓글

이 블로그의 인기 게시물

[Java] Http File Download 이어받기

Http 서버로부터 다운로드 받는 파일을 이어받기 위해서는 Http Header에 아래 두가지 정보를 추가해 주면 된다. URLConnection conn = url.openConnection(); conn.setRequestProperty("Accept-Ranges", "bytes"); conn.setRequestProperty("Range", "bytes=" + mOffset + "-"); 그러면 서버에서는 해당 Offset으로부터 File을 다운로드 시켜준다. 클라이언트가 요청헤더에 Range 필드를 포함 시켜서 보내면, 서버는 그 정보를 가지고 어디서 부터 파일을 보낼지 판단을 합니다. 하지만 클라이언트가(브라우저) Range 필드를 포함 시켜야 할지를 판단하는 기준은 최초 다운로드 요청시 서버의 응답헤더에 따라 다음 요청헤더에 Range 헤더를 생성할지 않할지 판단하게 됩니다. 그걸 당락짓는 응답 헤더 필드는 다음과 같습니다. Accept-Ranges , ETag, Last-Modified 반드시 위 필드를 응답 헤더에 같이 보내줘야 클라이언트는 다음 요청시 Range헤더를 포함시켜 보내게 됩니다. 참고로 말씀 드리면 위 필드를 포함 시켜서 보내더라도 value는 반드시 " " 로 묶어서 보내야 합니다. 안그러면 브라우저는 죽어도 Range 필드를 생성시키지 않습니다. HTTP 1.1 스펙은 따옴표를 강제적으로 해줘라 이런 내용 없습니다. 자바기준 40byte의 파일이라치면 클리이언트 요청을 두번으로 나누었다치면 이케 connection.setRequestProperty("Range", "bytes=0-20"); connection.setRequestProperty("Range", "bytes=20-40"); 단 co...

java 특정 디렉토리에 있는 파일 목록을 읽어내기, 정렬해서 가져오기

폴더 리스트 가져오기 String path="C:\"; File dirFile=new File(path); File []fileList=dirFile.listFiles(); for(File tempFile : fileList) {   if(tempFile.isFile()) {     String tempPath=tempFile.getParent();     String tempFileName=tempFile.getName();     System.out.println("Path="+tempPath);     System.out.println("FileName="+tempFileName);     /*** Do something withd tempPath and temp FileName ^^; ***/   } } 정렬해서 가져오기 import java.io.FileFilter; import java.io.IOException; import java.util.Arrays; import java.util.Date; import org.apache.commons.io.comparator.LastModifiedFileComparator; import org.apache.commons.io.filefilter.FileFileFilter; public class LastModifiedFileComparatorTest { public static void main(String[] args) throws IOException { File directory = new File("."); // get just files, not directories File[] files = directory.listFiles((FileFilter) FileFileFilter.FILE); System.out.println("Defaul...