驱动以模块编译进linux内核,Linux 驱动开发之内核模块开发

66b52468c121889b900d4956032f1009.png

8种机械键盘轴体对比

本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?

Linux 驱动开发之内核模块开发

一、内核模块的概念

1、什么是模块? ​ 内核模块是一些可以让操作系统内核在需要时载入和执行的代码,同时在不需要的时候可以卸载。这是一个好的功能,扩展了操作系统的内核功能,却不需要重新启动系统,是一种动态加载的技术。

特点:动态加载,随时载入,随时卸载,扩展功能

2、内核模块的加载作用 ​ 内核模块只是向linux内核预先注册自己,以便于将来的请求使用;由目标代码组成,没有形成完整的可执行程序。只是告诉内核,它有了新增的功能,而并不马上使用(执行),只是等待应用程序的调用;而应用程序在加载后就开始执行。

3、内核模块所用函数 ​ 内核模块代码编写没有外部的函数库可以用,只能使用内核导出的函数。而应用程序习惯于使用外部的库函数,在编译时将程序与库函数链接在一起。例如对比printf( ) and printk( )。

​ 所以驱动所用头文件均来自内核源代码,应用程序所用头文件来自库函数。

4、内核模块代码运行空间 ​ 内核代码运行在内核空间,而应用程序在用户空间。应用程序的运行会形成新的进程,而内核模块一般不会。每当应用程序执行系统调用时,linux执行模式从用户空间切换到内核空间。

二、linux内核模块的框架

​众所周知,Linux是一个开源的项目,为了使Linux在发展中能不成为一个封闭的项目,就要求任何使用Linux内核源码的个人或者组织在免费获得源码并做任意修改和再发布的同时,必须将修改后的源码发布。这就是所谓的GPL协议。

​所以在每个驱动模块的代码里面必须声明:MODULE_LICENSE(“GPL”); ​其实MODULE_LICENSE是一个宏,代表着相应的许可证协议,没有这行代码内核中的某些函数是不能使用的,类似的还有MODULE_AUTHOR 用于描述作者的信息、MODULE_DESCRIPTION用于模块的详细信息说明。

module_init(drv_init);//模块加载函数

module_exit(drv_exit);//模块卸载函数

​这里利用 module_init 和 module_exit 这两个函数接口实现模块加载和卸载到内核。参数是我们自己定义的函数名,利用模块初始化函数和清除函数就可以用别名来定义了,但是指定任意的函数名又带来一个问题,就是可能会和内核中的已经有的函数重名,因为模块的代码最终也是属于内核代码的一部分。C语言没有类似C++的命名空间的概念,为了避免因为重名而带来重复定义的问题,函数可以加 static 关键字修饰。经过修饰的函数的链接属性为内部,从而解决了问题。这就是几乎所有的模块驱动程序都需要在函数前加static关键字修饰的原因。

​在函数名前面加 __init 是把标记的函数放到ELF文件的特定代码段,在模块加载这些段时将会单独分配内存,这些函数调用成功后,模块的加载程序会释放这部分内存空间、 __exit用于修饰卸载函数,和__init的作用类似。这样便可以更加节约内存,调用完成后就直接释放掉。 代码示例:my_drv.c

#include

#include

#include

static int __init drv_init(void)

{

printk("%sn", __FUNCTION__);

return 0;

}

static void __exit drv_exit(void)

{

printk("%sn", __FUNCTION__);

}

module_init(hello_drv_init);

module_exit(hello_drv_exit);

MODULE_LICENSE("GPL");

应用程序和内核模块对比总结如下 :应用程序模块入口函数main加载时候调用drv_init

函数的调用/lib所有函数可以直接调用

运行空间用户空间内核空间

资源的释放系统自动释放kill -9 pid 手动释放手动释放

三、Linux 内核模块的编译和加载

​在编写完驱动程序后,接下来需要编写与之配对的Makefile文件。驱动编译分为静态编译和动态编译;静态编译即为将驱动直接编译进内核,动态编译即为将驱动编译成模块。 而动态编译又分为两种: a – 内部编译 ​ 在内核源码目录内编译(因为会修改内核的源码,可能造成不稳定的因素) b – 外部编译 ​ 在内核源码的目录外编译(一般采取的编译方法) Makefile示例:my_drv.c

ROOTFS_DIR = /home/linux/NFS/rootfs //测试平台上的NFS服务路径

ifeq ($(KERNELRELEASE), )//在linux内核源代码中的顶层makefile中有定义

KERNEL_DIR =/home/linux/Exynos4412/linux-4.17 //测试平台上的内核源码路径(4.17版本内核)

CUR_DIR = $(shell pwd)//取得当前工作路径

all :

make -C $(KERNEL_DIR) M=$(CUR_DIR) modules

clean :

make -C $(KERNEL_DIR) M=$(CUR_DIR) clean

install:

cp -raf *.ko $(ROOTFS_DIR)/drv_module/

else

obj-m += my_drv.o

endif

make 的的执行步骤 ​第一次进来的时候,宏“KERNELRELEASE”未定义,因此进入 else;

记录内核路径,记录当前路径;由于make 后面没有目标,所以make会在Makefile中的第一个不是以.开头的目标作为默认的目标执行。默认执行all这个规则

make -C $(KERNEL_DIR) M=$(CUR_DIR) modules

​ -C 进入到内核的目录执行Makefile ,在执行的时候KERNELRELEASE就会被赋值,M=$(CUR_DIR)表示返回当前目录,再次执行makefile,modules 编译成模块的意思,再次执行该makefile,KERNELRELEASE就有值了,就会执行obj-m += my_drv.o, obj-m:表示把my_drv.o 和其他的目标文件链接成my_drv.ko模块文件,编译的时候还要先把my_drv.c编译成hello.o文件。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
模块一般用来支持那些不经常使用的功能。例如,通常情况下你仅使用拨号网络,因此网络功能并不是任何时候都需要的,那么就应该使用可装入的模块来提供这个功能。仅在你行拨号联接的时候,该模块才被装入。而在你断掉连接的时候它会被自动卸下。这样会使内核使用内存的量最小,减小系统的负荷。 当然,那些象硬盘访问这样时时刻刻都需要的功能,则必须作在内核里。如果你搭一台网络工作站或 web服务器,那么网络功能是时刻都需要的,你就应该考虑把网络功能编译内核里。另外一个方法是在启动的时候就装入网络模块。这种方法的优点是你不需要重新编译内核。而缺点是网络功能不能特别高效。 按照以上的原则,我们首先列出一张清单,看看 kernel 中哪些选项是非有不可的,也就是说,这些东西是必须被编译内核中的。将那些非必需的模块剔除到内核以外。 第一个是root所在的硬盘配置。 哪果您的硬盘是IDE接口,就把 ide 的选项标记下来。如果是SCSI接口,请把您的接口参数及 SCSI id 记标下来。 第二个是选择使用哪一个文件系统。 Linux的默认文件系统是是 ext2 ,那么就一定要把它标记下来。如果机器中还其它的操作系统,如win98或windows NT,您还会可能选择FAT32或NTFS的支持,不过后面你可以通过手工加载的方式来加入新的模块支持。 第三个是选择Linux所支持的可执行文件格式。这里有两种格式可供选择: elf:这是当前Linux普遍支持的可执行文件格式,必须编译内核中 。 a.out: 这是旧版的Linux的可执行文件各函数库的格式,如果你确认肯定用不到这种格式的可执行文件,那么就可以不把它编译内核当中。 以上这些内容,是必须要编译内核中的。其它的内容凡是所有选项中m提示的,都选择m,这样可以通过手工的方式添加该模块。 ** Loadable module support*Enable loadable module support (CONFIG_MODULES) [Y/n/?]Set version information on all symbols for modules (CONFIG_MODVERSIONS) [N/y/?]Kernel daemon support (e.g. autoload of modules) (CONFIG_KERNELD) [Y/n/?] 分别回答 Y,N,Y 。其中 CONFIG_KERNELD 的 default 值是 N, 所以要注意选择Y。 make config 完后,仍旧是 make dep; make clean。 接下来要 make zlilo 或 make zImage。 然后 make modules ; make modules_install 。完成之后,就编译出一个没有调入多余模块的一个“干净的”内核映像文件了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值