设为首页 收藏本站
查看: 338|回复: 0

[经验分享] Linux设备驱动编程-动态加载内核模块

[复制链接]

尚未签到

发表于 2016-2-26 11:28:56 | 显示全部楼层 |阅读模式
内核模块

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("Hello module init\n");

 return 0;

}

static void __exit hello_exit (void)

{

 printk("Hello module exit\n");

}

module_init(hello_init);

module_exit(hello_exit);

分析上述程序,发现一个Linux内核模块需包含模块初始化和模块卸载函数,前者在 insmod的时候运行,后者在rmmod的时候运行。初始化与卸载函数必须在宏module_init和module_exit使用前定义,否则会出现编译错误。程序中的MODULE_LICENSE("GPL")用于声明模块的许可证。

如果要把上述程序编译为一个运行时加载和删除的模块,可以编写makefile文件如下:

CC =/usr/local/arm/2.95.3/bin/arm-linux-gcc
KERNELDIR=/home/work/linux-2.4
CFLAGS =-D__KERNEL__ -DMODULE -I$(KERNELDIR)/include -O -Wall
test.o:test.c

    $(CC)$(CFLAGS) -c $<  

clean:
    rm -rf *.o

由此可见,Linux内核模块的编译需要给arm-linux-gcc指示-D__KERNEL__ -DMODULE 参数。-I选项跟着Linux内核源代码中Include目录的路径。

下列命令将可加载hello模块:

insmod ./hello.o

下列命令完成相反过程:

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)。

这里给出的仅仅是关于内核中内存管理的皮毛,关于Linux内存管理的更多细节知识,《内存与I/O操作》进行更加深入地介绍。

输出

在内核编程中,我们不能使用用户态C库函数中的printf()函数输出信息,而只能使用 printk()。但是,内核中printk()函数的设计目的并不是为了和用户交流,它实际上是内核的一种日志机制,用来记录下日志信息或者给出警告提示。每个printk都会有个优先级,内核一共有8个优先级,它们都有对应的宏定义。如果未指定优先级,内核会选择默认的优先级 DEFAULT_MESSAGE_LOGLEVEL。如果优先级数字比int console_loglevel变量小的话,消息就会打印到控制台上。如果syslogd和klogd守护进程在运行的话,则不管是否向控制台输出,消息都会被追加进/var/log/messages文件。klogd 只处理内核消息,syslogd 处理其他系统消息,比如应用程序。

模块参数

2.4内核下,include/linux/module.h中定义的宏 MODULE_PARM(var,type) 用于向模块传递命令行参数。var为接受参数值的变量名,type为采取如下格式的字符串[min[-max]]{b,h,i,l,s}。min及max 用于表示当参数为数组类型时,允许输入的数组元素的个数范围;b:byte;h:short;i:int;l:long;s:string。

在装载内核模块时,用户可以向模块传递一些参数:

insmod modname var=value

如果用户未指定参数,var将使用模块内定义的缺省值。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.iyunv.com/thread-183046-1-1.html 上篇帖子: 关于Linux下root密码的问题 下篇帖子: netbeans在linux环境下中文乱码的处理
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表