1.Linux内核模块-helloworld
Linux设备驱动属于内核的一部分,Linux内核的一个模块可以以两种方式被编译和加载:
(1)直接编译进Linux内核,随同Linux启动时加载;
(2)编译成一个可加载和删除的模块,使用insmod加载(modprobe和insmod命令类似,但依赖于相关的配置文件),rmmod删除。这种方式控制了内核的大小,
而模块一旦被插入内核,它就和内核其他部分一样。
下面我们给出一个内核模块的例子:
#include <linux/module.h> //所有模块都需要的头文件 #include <linux/init.h> // init&exit相关宏 MODULE_LICENSE("GPL"); static int __init hello_init(void) { printk(KERN_ERR "#### hello world\n"); return 0; } static void __exit hello_exit(void) { printk(KERN_ERR "#### exit\n"); } module_init(hello_init); module_exit(hello_exit);
初始化与卸载函数必须在宏module_init和module_exit使用前定义,否则会出现编译错误。
程序中的MODULE_LICENSE("GPL")用于声明模块的许可证。
Makefile写法:
obj-m := hello.o hello-objs := helloworld.o KID := /lib/modules/`uname -r`/build PWD := $(shell pwd) all: make -C $(KID) M=${PWD} modules clean: rm -rf *.o .cmd *.ko *.mod.c .tmp_versions
先打开kmsg
snail@ubuntu:~$ sudo cat /dev/kmsg
然后加载hello.ko
sudo insmod ./hello.ko
下列命令完成相反过程:
sudo rmmod hello
如果要将其直接编译入Linux内核,则需要将源代码文件拷贝入Linux内核源代码的相应路径里,
并修改Makefile。
我们有必要补充一下Linux内核编程的一些基本知识
内存
在Linux内核模式下,我们不能使用用户态的malloc()和free()函数申请和释放内存。
进行内核编程时,最常用的内存申请和释放函数为在include/linux/kernel.h文件中声明的kmalloc()和kfree(),
其原型为:
void *kmalloc(unsigned int len, int priority);
void kfree(void *__ptr);
kmalloc的priority参数通常设置为GFP_KERNEL,如果在中断服务程序里申请内存则要用GFP_ATOMIC参数,
因为使用GFP_KERNEL参数可能会引起睡眠,不能用于非进程上下文中(在中断中是不允许睡眠的)。
由于内核态和用户态使用不同的内存定义,所以二者之间不能直接访问对方的内存。
而应该使用Linux中的用户和内核态内存交互函数(这些函数在include/asm/uaccess.h中被声明):
unsigned long copy_from_user(void *to, const void *from, unsigned long n);
unsigned long copy_to_user (void * to, void * from, unsigned long len);
copy_from_user、copy_to_user函数返回不能被复制的字节数,因此,如果完全复制成功,返回值为0。
include/asm/uaccess.h中定义的put_user和get_user用于内核空间和用户空间的单值交互(如char、int、long)。
输出
在内核编程中,我们不能使用用户态C库函数中的printf()函数输出信息,而只能使用printk()。
但是,内核中printk()函数的设计目的并不是为了和用户交流,它实际上是内核的一种日志机制,
用来记录下日志信息或者给出警告提示。
每个printk都会有个优先级,内核一共有8个优先级,它们都有对应的宏定义。如果未指定优先级,
内核会选择默认的优先级DEFAULT_MESSAGE_LOGLEVEL。
如果优先级数字比int console_loglevel变量小的话,消息就会打印到控制台上。
如果syslogd和klogd守护进程在运行的话,则不管是否向控制台输出,
消息都会被追加进/var/log/messages文件。klogd 只处理内核消息,
syslogd 处理其他系统消息,比如应用程序。