翼度科技»论坛 云主机 LINUX 查看内容

linux进阶:内核模块

12

主题

12

帖子

36

积分

新手上路

Rank: 1

积分
36
现代内核派系

宏内核


关键功能(基本功能,不可裁剪、扩展)和服务功能(如文件系统、设备驱动、网络服务等,可裁剪、扩展)均在内核空间提供。运行效率高。扩展性较差。system call(系统调用)能够先入内核态来使用内核提供的服务。
 
微内核

内核空间只提供关键功能,服务功能在用户空间提供。运行效率较低。安全性、扩展性较高。

在Linux内核源码中有超过50%的代码都与设备驱动相关。Linux为宏内核架构(windows、鸿蒙等为微内核架构),如果开启所有的功能,内核就会变得十分臃肿。内核模块实现了某个功能的一段内核代码,在内核运行过程,可以加载这部分代码到内核,从而动态地增加了内核的功能。基于这种特性,进行设备驱动开发时,以内核模块的形式编写设备驱动,只需要编译相关的驱动代码即可,无需对整个内核进行编译
在设备驱动开发过程中,用户可以随意将正在测试的驱动程序添加到内核或从内核中移除,每次修改内核模块的代码不需要重新启动内核。
在开发板上,用户也不需要将内核模块程序,或设备驱动程序的ELF文件存放在开发板,免去占用不必要的存储空间。当需要加载内核模块时,可以挂在NFS服务器,将存放在其它设备的内核模块加载到开发板上
 
内核模块

为解决linux内核可扩展性和可维护性相对较差的缺陷。我们编写的内核模块,经过编译,最终形成.ko为后缀的ELF文件。ko文件是elf格式,是一种普通的可重定位目标文件。这类文件包含了代码和数据,可以被用来链接成可执行文件或共享目标文件,静态链接库也可以归为这一类。
 

内核模块头文件
  1. #include <linux/init.h>      /*包含module_init()和module_exit()函数的声明*/
  2. #include <linux/module.h>    /*包含内核模块信息声明的相关函数*/
  3. #include <linux/kernel.h>    /*包含内核提供的各种函数,如printk*/
复制代码
 

内核模块加载与卸载

加载内核模块:insmod
卸载内核模块:rmmod
 

内核模块出入口

module_init():加载模块时该函数自动执行,进行初始化操作
module_exit():卸载模块时该函数自动执行,进行清理操作
 
内核模块信息声明
MODULE_LICENSE():表示模块代码接受的软件许可协议,Linux内核遵循GPL V2开源协议,内核模块与linux内核保持一致即可。
MODULE_AUTHOR():描述模块的作者信息。
MODULE_DESCRIPTION():对模块的简单介绍。
MODULE_ALIAS():给模块设置一个别名。
 

打印函数printk

printf:glibc库实现的打印函数,工作于用户空间。
printk:内核模块无法使用glibc库函数,内核自身实现的一个类printf函数,但是需要指定打印等级。
  1. #include <linux/kernel.h>
  2. /*printk打印等级,数字越小,等级越高*/
  3. #define KERN_EMERG    "<0>"    /*通常是系统崩溃前的信息*/
  4. #define KERN_ALERT    "<1>"    /*需要立即处理的消息*/
  5. #define KERN_CRIT     "<2>"    /*严重情况*/
  6. #define KERN_ERR      "<3>"    /*错误情况*/
  7. #define KERN_WARNING  "<4>"    /*有问题的情况*/
  8. #define KERN_NOTICE   "<5>"    /*注意信息*/
  9. #define KERN_INFO     "<6>"    /*普通消息*/
  10. #define KERN_DEBUG    "<7>"    /*调试信息*/
复制代码
cat /proc/sys/kernel/printk查看当前系统printk打印等级
终端输出:
当前控制台日志级别(小于该等级才能打印在当前控制台)
默认消息日志级别
最小的控制台级别(当前控制台日志级别的最小值
默认控制台日志级别(没指定等级时默认级别)

dmesg:打印内核所有打印信息。
lsmod:查看当前系统加载内核情况。
 
实验环境(野火为例)

开发板烧录好Debian镜像。启动开发板,搭建好nfs客户端,挂载共享文件夹。获取Debian镜像的内核源码并编译。(选择4.19.71版本内核,内核模块的功能需要依赖内核提供的各种底层接口。)
注:cat /etc/issue查看镜像日期。新版内核是4.19.35版本(22年之后),看驱动文档的“驱动章节实验环境搭建”。
 
下载linux内核源码(基于野火linux开发板,ebf-buster-linux存放着内核相关)
  1. git clone https://github.com/Embedfire/ebf-buster-linux.git
  2. git clone https://gitee.com/Embedfire/ebf-buster-linux.git
复制代码

 
安装必要环境工具库(make工具、gcc交叉编译链、gcc编译工具、bison语法分析器、flex词法分析器、libssl-dev OpenSSL通用库、lzop LZO压缩库的压缩软件)
  1. sudo apt install make gcc-arm-linux-gnueabihf gcc bison flex libssl-dev dpkg-dev lzop
复制代码
 
前往ebf-buster-linux目录,执行脚本,一键编译内核 
  1. sudo ./make_deb.sh
复制代码
 
获取编译出来的内核相关文件(make_deb.sh脚本指定存放路径)
/home/couvrir/桌面/ebf-buster-linux/build_image/build
make_deb.sh如下:
  1. deb_distro=bionic
  2. DISTRO=stable
  3. build_opts="-j 6"
  4. build_opts="${build_opts} O=build_image/build"            //O执行编译文件的存放路径
  5. build_opts="${build_opts} ARCH=arm"
  6. build_opts="${build_opts} KBUILD_DEBARCH=${DEBARCH}"
  7. build_opts="${build_opts} LOCALVERSION=-imx-rl"
  8. build_opts="${build_opts} KDEB_CHANGELOG_DIST=${deb_distro}"
  9. build_opts="${build_opts} KDEB_PKGVERSION=l${DISTRO}"
  10. build_opts="${build_opts} CROSS_COMPILE=arm-linux-gnueabihf-"
  11. build_opts="${build_opts} KDEB_SOURCENAME=linux-upstream"
  12. make ${build_opts} npi_v7+defconfig
  13. make ${build_opts}
  14. make ${build_opts} bindeb-pkg
复制代码
 
实验一

helloworld.c文件
  1. #include <linux/init.h>      
  2. #include <linux/module.h>   
  3. #include <linux/kernel.h>  
  4. static int __init hello_init(void)    //__init指将这段函数指定保存在__init段内存
  5. {
  6.     printk(KERN_EMERG "[ KERN_EMERG ] Hello World Module Init\n");  //指定等级
  7.     printk("[ default ] Hello World Module Init\n");           //默认等级
  8.     return 0;
  9. }
  10. static int __exit hello_exit(void)    //__exit指将这段函数指定保存在__exit段内存
  11. {
  12.     printk("[ default ] goodbye\n");                    //默认等级
  13. }
  14. module_init(hello_init);    //hello_init作为模块函数入口
  15. module_exit(hello_exit);    //hello_exit作为模块函数出口
  16. MODULE_LICENSE("GPL2");                     //表示模块代码接受的软件许可协议
  17. MODULE_AUTHOR("couvrir");                   //描述模块的作者信息
  18. MODULE_DESCRIPTION("hello world module");   //对模块的简单介绍
  19. MODULE_ALIAS("test module");                //给模块设置一个别名
复制代码
 Makefile文件
  1. KERNEL_DIR=/home/couvrir/桌面/ebf-buster-linux/build_image/build    //将内核源码编译生成的内核文件存放在该路径
  2. ARCH=arm                                //交叉编译的目标架构
  3. CROSS_COMPILE=arm-linux-gnueabihf-        //交叉编译的编译工具链
  4. export ARCH CROSS_COMPILE                //将架构和编译链导出到子Makefile,使其在当前shell环境中生效
  5. obj-m:=helloworld.o
  6. all:                                    //默认目标,用于编译所有模块 
  7.         $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules    //执行内核源码目录的Makefile文件,并将当前目录作为模块的源码目录
  8. //通过执行Makefile文件,可以将helloworld.c文件编译成一个内核模块
  9. .PHONY:clean copy
  10. clean:
  11.         $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean        //执行内核源码目录的Makefile文件的clean目标,清理编译生成的文件
  12. copy:
  13.         sudo cp *.ko /home/couvrir/workdir                //将ko文件存到共享文件夹
复制代码
obj-m:=.o指定要编译的模块文件
-C指定子Makefile的路径,M=描述内核关于内核模块的源码路径。CURDIR为当前目录路径。
make modules:执行linux顶层Makefile的伪目标,它实现内核模块的源码读取并编译为.ko文件。
执行make、make copy。
然后开发板上sudo insmod xxx.ko。
dmesg:打印内核所有打印信息。
lsmod:查看当前系统加载内核情况。
 

来源:https://www.cnblogs.com/couvrir/p/17613829.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具