加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > Linux > 正文

linux 驱动开发-模块的构建

发布时间:2020-12-14 01:21:19 所属栏目:Linux 来源:网络整理
导读:1.模块的含义 linux 是採用模块化的方式构建的,同意内核在执行时动态地向当中插入或从中删除代码。这些代码(包扩函数,数据,模块入口函数,模块出口函数)被一并组合 在一个单独的二进制镜像,就是所谓的可装载内核模块。 模块能够是主要的内核镜像尽可能

1.模块的含义

linux 是採用模块化的方式构建的,同意内核在执行时动态地向当中插入或从中删除代码。这些代码(包扩函数,数据,模块入口函数,模块出口函数)被一并组合

在一个单独的二进制镜像,就是所谓的可装载内核模块。

模块能够是主要的内核镜像尽可能小,同一时候能够方便地对新功能进行调试。还能够实现热插拔(兴许会学习怎样实现设备的热插拔功能,临时无需深究),和内核的核心子系统不一样,模块文件须要有入口点和出口点。

模块与应用程序的差别:

a.模块和库函数相似,一个模块通常包括若干函数和数据,每一个函数提供特定的服务。决定完毕哪些功能则是应用程序的事情,模块仅仅是用来为应用程序提供服务。也就是策略与机制的关系。模块提供机制,应用程序依据模块提供的机制实现策略。

b.模块的注冊函数仅仅是把模块注冊到内核中。模块中的函数怎样被调用是应用程序的事情。

c.应用程序退出是能够无论资源的释放和其它清理工作(通常我们还是会处理),但模块的退出函数必须细致撤销初始化函数所作的一切。

d.模块被链接到内核,它仅仅能调用哪些被内核导出的函数(以后会讲到怎样导出内核函数),使用的头文件是内核源码中include/文件夹下的头文件,如:

#include<linux/init.h>?? ======>include/linux/init.h

e.模块不会和不论什么库链接

f.模块编程必须谨慎考虑并发问题,通常必须是可重入的。

2.模块的编译,装载和卸载。

请參考linux内核稳定。有很具体的描写叙述。

Documentation/kbuild/modules.txt

由于文档是英文。我简单描写叙述一下。

a.首先执行makefile编译模块,简单分析make的编译參数

make -C? 将文件夹改变到内核源码所在文件夹,当中有内核的顶层makefile文件。

M=‘pwd’? 要求顶层makefile在构造modules目标之前返回到模块的源码文件夹。

modules 仅仅想obj-m变量中设定的模块。就是告诉编译系统,此make 是要编译一个内核模块。

内核转载和卸载的命令

insmod
?46 通过insmod将模块的代码和数据安装到系统正在执行的内核中。一旦成功安装。则模块就能够訪问内核的公共符号(包括函数和变量)。

insmod和ld相似。但insmod不
??? 会改动模块的磁盘文件,仅仅会改动内存中的副本。


rmmod
?通过rmmod卸载模块。注意,仅仅有root权限才干够安装和卸载模块。

假设内核觉得模块仍然处于使用状态,有可能无法卸载。在编译内核时须要将Loadable module s??? upport-->Module unloading选中,否则内核不支持模块卸载。


lsmod

lsmod程序列出了当前装载到内核中的所以模块,还列出了其它信息,如其它模块是否在使用某个特点模块。lsmod通过读取/proc/modules获得这些信息。

上面的基本知识大家有个大概的印象就可以,以下演示样例分析一下,一些程序学习的起源都是Hello world, 那么我们也从Hello world開始。创建一个简单的Hello world 模块。


首先构建模块编译的makefile文件

#Makefile
obj-m = hello.o

KERN = /share/arm/linux-3.2                               #基于源码树的模块编译。源码树必须要先编译完毕后,才干进行模块编译
# KERN = /lib/modules/`uname -r`/build/                   ##假设你在x86平台的linux系统,直接能够编译基于x86平台的模块

all:
        make -C $(KERN) M=`pwd` modules

clean:
        make -C $(KERN) M=`pwd` modules clean
        rm -rf modules.order
看看首个模块代码helloWorld.c

#include <linux/init.h> #include <linux/module.h> //模块必须的两个头文件 MODULE_LICENSE("GPL"); //GPL协议要求 MODULE_AUTHOR("MJ"); //option 描写叙述作者信息 MODULE_DESCRIPTION("just for test"); //option 描写叙述模块的 static __init int hello_init(void) { printk(KERN_EMERG,"hello kernel !n"); //printk 相似应用程序的printf。 使用方法稍有不同,可百度一下。

} static __exit void hello_exit(void) { printk(KERN_EMERG,"bye,cruel world!n"); } module_init(hello_init); module_exit(hello_exit);
通常printk不能直接输出到终端,须要看终端的配置及你printk的打印机别。 可是一定会输出到log文件,你能够用dmesg 命令查看log message。

printk()
a.介绍
printk是内核中很好用的一种信息输出方法。和printf很象。不同的是printk能够附加不同的日志级别,从而能够依据消息的严重程度分类。

如:
printk(KERN_DEBUG "Here I am: %s:%in",__FILE__,__LINE__);

b.日志级别
printk()定义在/kernel/printk.c中。消息级别和原型声明都在<linux/kernel.h>中。

缺省级别在printk.c中指定。
支持的日志级别:
KERN_EMERG: 紧急情况 ?
KERN_ALERT: 须要马上被注意到的错误
KERN_CRIT: 临界情况
KERN_ERR: 错误
KERN_WARNING: 警告
KERN_NOTICE: 注意
KERN_INFO: 非正式的消息
KERN_DEBUG: 调试信息(冗余信息)


c.printk向用户空间的输出
两个守护进程syslogd和klogd用于处理日志信息。小于控制台级别的信息会被输出到console。而大于控制台级别的信息不输出,仅仅是写入/var/log/messages和/proc/kmsg中。

能够通过dmesg查看。



改动控制台级别:
$>echo 8 > /proc/sys/kernel/printk

$>klogd -c 8
/*须要先关闭klogd。用ps -C klogd 查出这个进程的pid,然后kill)

printk函数将消息写到一个长度为__LOG_BUF_LEN字节的循环缓冲区中,然后唤醒不论什么睡眠在syslog系统调用或正在读取/proc/kmsg的进程。循环缓冲区满了以后,会绕>回開始处填写新的内容。

printk()能够在中断中调用。

当printk()的调用频率过高时。还能够通过printk_ratelimit()限制输出。



(7)模块的初始化和清除
<linux/init.h>中包括了和模块的初始化和清除相关的信息。

初始化
a.module_init
用于注冊模块的初始化函数
module_init(hello_init);

b.__init
用于修饰模块的初始化函数。表明该函数仅仅在模块的初始化期间使用。初始化完毕后,这个函数所占的内存会被释放。


static int __init hello_init(void)
{
? ...
}

c.__initdata
用于修饰模块的初始化数据结构。


int __initdata array[100];


清除
a.module_exit
用于注冊模块的清除函数
module_exit(hello_exit);

b.__exit
static void __exit hello_exit(void)
{
? ...
清除函数没有返回值,被__exit修饰的模块仅仅能用于模块卸载(编译器会把该函数放在特殊的ELF段中)。其它时候调用清除函数都是错的。


本文对linux 模块进行主要的介绍。让大家对linux模块有个主要的认识。


下一篇文章将对模块的其它特性进行解说

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读