리눅스 커널 모듈 프로그래밍 가이드
참조:http://www.tldp.org/LDP/lkmpg/2.6/html/lkmpg.html
커널 모듈 가이드
아래 테스트는 우분투 14.04에서 테스트 되었습니다.
rmmod:커널에서 모듈을 제거한다.
lsmod:커널에서 적재된 모듈 목록을 보여준다.
depmod: 모듈간 의존성 정보를 생성한다.
modprobe:모듈을 커널에 적재하거나 제거한다.
modinfo rdma_ucm
#cat /etc/modprobe.conf
#modinfo ib_core
커널에서 제공하는 심볼 테이블
:커널 내부의 함수나 변수 중 외부에서 참조할 수 있는 함수의 심볼과 주소를 담은 테이블
#cat /proc/kallsyms
kernel ver 3.16
hello.c
/*
* hello.c - The simplest kernel module.
*/
#
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
현재 모듈이 다른 모듈의 함수를 사용했을 경우다.
쓰레드 생성 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);
===========================================
#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) 통신->
참조: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) 통신->
댓글
댓글 쓰기