• 一个简单的驱动

  模块的使用能使linux内核便于裁剪,根据不同的应用需求得到一个最小的内核,同时调试内核驱动也更为方便,比如如果调试i2c驱动,如果不采用模块的方式,那么每次修改i2c驱动就得编译整个内核,对于编译调试极其耗时,使用模块,一个简单的insmod就将模块加载进了内核,如果觉得不合适,需要调试,只需要rmmod就可以将模块卸载。

  一个简单的驱动模块:

 #include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL"); static int __init test_init(void)
{
printk(KERN_ALERT"test modules init\n");
return ;
} static void __exit test_exit(void)
{
printk(KERN_ALERT"test modules exit\n");
} module_init(test_init);
module_exit(test_exit);

  这个驱动什么都没干,就打印了几句话,但是具备了驱动程序最基本的几个要素:

  1.  驱动初始化函数 XXX_init(void),使用宏module_init(test_init);告诉内核这是模块的初始化函数入口。
  2.  驱动退出函数XXX_exot(void),使用宏module_exit(test_exit);告诉内核这是模块的退出函数入口。
  3. 驱动模块遵循的协议 MODULE_LICENSE("Dual BSD/GPL")

  注意到初始化函数和退出函数分别被__init,__exit修限定,其实不用它们来限定2个函数也是可以的,那么用不用这2个东西限定有什么区别呢?首先看它们到底是2个什么东西:

 #define __init        __section(.init.text) __cold notrace
#define __exit __section(.exit.text) __exitused __cold notrace

  是2个宏,其中__section也是个宏

 # define __section(S) __attribute__ ((__section__(#S)))

  __attribute__是针对gcc的特有关键字,__attribute__ 可以设置函数属性(Function Attribute )、变量属性(Variable Attribute )和类型属性(Type Attribute ),我见的比较多的是在设置数据结构的对齐属性上:

 struct S {

 short b[];

 } __attribute__ ((aligned ()));

  在这里设置为.init.text是指把这个函数放到特殊的.init.text段,看过汇编代码就可能有映象.text段,也就是代码段,函数都是放这个段的。.init.text段的特性是等模块加载完了,这个段将被释放,因为就好比我们写一个单片机程序一样,一般都会有个init函数,它完了就是个死循环,驱动实际在运行过程中XXX_init函数就没有什么作用了,因此就可以把XXX_init函数加载到内存的那段释放掉,腾出空间来给别人用。

  __exit就是在卸载模块完成后,将它标识的函数所占用的内存释放。

  除了__init,__exit,还有__initdata,__exitdata。作用基本类似,我原本猜想带data的应该是修饰仅在初始化函数和退出函数里面用到的全局变量,比如一个数组之类的东西,但是我试了用__initdata修饰函数好像编译并不报错,暂时不深究。

  MODULE_LICENSE("Dual BSD/GPL"),少了它不影响编译,但是加载模块的时候会警告提示这个模块污染了内核:

  [22321.826339] test: module license 'unspecified' taints kernel.
  [22321.826349] Disabling lock debugging due to kernel taint
  [22321.826759] test modules init

  • 编译并加载,卸载这个驱动

  编译模块的makefile

 ifneq ($(KERNELRELEASE), )
obj-m := test.o book.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
defualt:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions modules.order Module.symvers .PHONY:clean

  加载模块:  

 sudo insmod test.ko

  为了图方便,使用一个简单的shell脚本监视dmesg的输出,脚本如下:

 #!/bin/bash

 set -o nounset                              # Treat unset variables as an error

 while true
do
dmesg -c
sleep
done

  卸载模块:

sudo rmmod test

  卸载模块不需要后面的.ko。

  • 指定和导出模块的参数

  参数在驱动里面必须是一个全局变量,一般加static限定。

  声明参数,使用宏:

module_param(<参数名>, <参数类型>, <参数读写权限>);

  参数类型支持:byte、short、ushort、int、uint、long、ulong、charp(字符指针)、bool 或 invbool(布尔的反)

  例如:

 static int num = ;

 module_param(num, int, S_IRUGO);

 static int __init test_init(void)
{
printk(KERN_ALERT"num:%d\n",num);
return ;
}

  如果在insmod的时候,指定num的值:

insmod test.ko num=

  那么输出:num:10

  需要注意的是,参数在声明时要给一个默认值,因为如果insmod指定参数值,那么它就使用这个默认值。

  一个参数(在驱动里面的一个全局变量)怎么会有读写权限?加载这个模块后,进入/sys/module目录,会看到一个test的目录,这个目录记录了这个test模块的一些信息,与未加模块参数的时候相比,它多出了一个parameters的目录,进如这个目录,发现会有个与模块参数同名的num文件,cat它里面的内容就是它的默认值5,再查看这个num文件的权限:

  -r--r--r-- 1 root root 4096 5月   3 21:30 num
  和我在代码中指定的一样,原来权限就是指这个文件的权限,参数与之对应的一个文件,也就是说参数的默认值是可以被更改的,只要权限拿到了。

  使用 cat /proc/kallsyms | grep "\[test\]"查看与test.ko这个模块相关的符号:

    t test_exit    [test]
   r __param_num [test]
   r __param_str_num [test]
   d num [test]
   d __this_module [test]
   t cleanup_module [test]

  可以看到2个我们预料中的符号,num和test_exit,如果我们不使用__init限定test_init,那么可以看到test_init也在里面,也就证明了__init的作用是在调用初始化函数后释放了与之对应的内存:

  t test_init    [test]
t test_exit [test]
r __param_num [test]
r __param_str_num [test]
d num [test]
d __this_module [test]
t cleanup_module [test]
t init_module [test]

  使用module_param是指外面可以传一个值进来,如果要这个值对外可见,我们明显不能采用去掉static限定的办法,而是使用EXPORT_SYMBOL(num);这个宏是指将模块内的全局变量num对外可见,它不仅仅可以用于变量也可以对函数起作用。

模块A:

 #include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL"); static int num = ;
module_param(num, int, S_IRUGO); static int __init test_init(void)
{
printk(KERN_ALERT"test modules init\n");
return ;
} static void test_saymyname(void)
{
printk(KERN_ALERT"you call me\n");
}
static void __exit test_exit(void)
{
printk(KERN_ALERT"test modules exit\n");
} module_init(test_init);
module_exit(test_exit);
EXPORT_SYMBOL(test_saymyname);

模块B调用模块A的 test_saymyname

 #include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL"); extern void test_saymyname(void); static int __init book_init(void)
{
test_saymyname();
return ;
} static void __exit book_exit(void)
{
printk(KERN_ALERT"book modules exit\n");
} module_init(book_init);
module_exit(book_exit);

分别insmod test.ko和insmod book.ko,再卸载模块时,如果先卸载test.ko会发现提示test.ko里面有函数被book.ko调用,无法卸载,只有先卸载book.ko才行。

linux内核模块的更多相关文章

  1. 5.linux内核模块基础,内核模块学习

    linux内核模块基础 一.定义 Linux 内核的整体结构非常庞大,其包含的组件也非常多,如何使用这些组件呢: 方法 1:把所有的组件都编译进内核文件,即:zImage 或 bzImage,但这样会 ...

  2. Linux内核模块简介

    一. 摘要 这篇文章主要介绍了Linux内核模块的相关概念,以及简单的模块开发过程.主要从模块开发中的常用指令.内核模块程序的结构.模块使用计数以及模块的编译等角度对内核模块进行介绍.在Linux系统 ...

  3. Smart210学习记录-------linux内核模块

    Linux 驱动工程师需要牢固地掌握 Linux 内核的编译方法以为嵌入式系统构建可运行的Linux 操作系统映像.在编译 LDD6410 的内核时,需要配置内核,可以使用下面命令中的 一个: #ma ...

  4. linux内核模块相关命令:lsmod,depmod,modprobe,modinfo,insmod,rmmod 使用说明

    加载内核驱动的通常流程: 1.先将.ko文件拷贝到/lib/module/`uname -r`(内核版本号)/kernel/driver/...目录下, 根据具体用途的区别分为net.ide.scsi ...

  5. linux内核模块编程实例

    linux内核模块编程实例 学号:201400814125 班级:计科141 姓名:刘建伟 1.确定本机虚拟机中的Ubuntu下Linux的版本 通过使用命令uname -a/uname -r/una ...

  6. Linux内核模块编程——Hello World模块

    Linux内核模块编程 编程环境 Ubuntu 16.04 LTS 什么是模块 内核模块的全称是动态可加载内核模块(Loadable Kernel Modul,KLM),可以动态载入内核,让它成为内核 ...

  7. linux内核模块的安全

    linux可以动态的加载内核模块,在很多场合可能需要确保加载内核的安全性.如果被攻击者加载恶意内核模块,将会使得内核变得极其危险. 当然,稳妥的做法就是给内核模块进行签名,内核只加载能正确验证的签名. ...

  8. Linux内核模块编写详解

    内核编程常常看起来像是黑魔法,而在亚瑟 C 克拉克的眼中,它八成就是了.Linux内核和它的用户空间是大不相同的:抛开漫不经心,你必须小心翼翼,因为你编程中的一个bug就会影响到整个系统,本文给大家介 ...

  9. 3、Linux内核模块学习

    一.内核模块的学习   内核的整体框架是非常的大,包含的组件也是非常多,如何将需要的组件包含在内核中呢?选择一,就是将所有的组件全部编译进内核,虽然需要的组件都可以使用,但是内核过分庞大,势必带来效率 ...

  10. Linux内核模块编程与内核模块LICENSE -《具体解释(第3版)》预读

    Linux内核模块简单介绍 Linux内核的总体结构已经很庞大,而其包括的组件或许多.我们如何把须要的部分都包括在内核中呢?一种方法是把全部须要的功能都编译到Linux内核.这会导致两个问题.一是生成 ...

随机推荐

  1. vmware安装mac

    1.笔记本安装mac10.6 2.用VMware8,需要在mac.vmx中添加以下语句 guestOS = "darwin10"ich7m.present="TRUE&q ...

  2. Python基础:模块

    一.概述 二.导入语句 1.基本语法 2.推荐风格 三.模块 1.模块名 2.模块属性 3.可导出的公有属性 4.直接执行 四.包 1.包名 2.包属性 3.可导出的公有属性 4.其他 五.导入原理 ...

  3. C# 修改电脑DNS和IP方法

    /// <summary> /// 将IP,DNS设置为自动获取 /// </summary> private void setDHCP() { string _doscmd ...

  4. ahjesus fstab修改错误了如何修复

    fstab修改错误了如何修复   当你不小心把磁盘表输入错误以后,系统总是让你按ctrl+D重新启动或者输入密 码进入shell,你输入密码登陆后,   编辑文件是只读的,执行下面的命令后就可以编辑了 ...

  5. 阿里云主机上安装jdk

    今天继续安装jdk到阿里云服务上,大家要看一下阿里云是32位还是64位的,如果是32位下载32位的包,如果是64位的下载64位的包 我的就是64位的,开始我还不知道是怎么区分32/64位的,原来X64 ...

  6. Ogre2.1 灯光与阴影

    Ogre2.1大量光源渲染 Ogre2.1不是采用现在大部分引擎所用的延迟渲染,而是采用一种前向渲染的改进技术,理论基本来自于Forward+,见如下. http://www.klayge.org/? ...

  7. dbcp/c3p0连接池设置mysql会话变量

    我们有几个计算风控值的定时任务,几乎每隔5秒会更新所有账户的当前总资产并以此通知风控,每隔一小时就产生一两个G的binlog,几十台服务器折腾..数据库是公用的,代码是通过工具自动生成的,直接修改流程 ...

  8. [Architecture Pattern] Factory Builder

    [Architecture Pattern] Factory Builder 目的 同时提供延迟注入对象.挂载注入项目这两个功能 情景 在开发系统时,如果需要在运行时间才生成并注入对象,可以套用Fac ...

  9. 回车键和button按钮都绑定同一个事件,如何避免按回车的时候button重复点击

    保存一个全局变量,用来记录Button的焦点状态 <button onclick="login();" onfocus="window.buttonIsFocuse ...

  10. Mybatis学习记录(六)----Mybatis的高级映射

    1.一对多查询 1.1 需求 查询订单及订单明细的信息. 1.2 sql语句 确定主查询表:订单表 确定关联查询表:订单明细表 在一对一查询基础上添加订单明细表关联即可. SELECT orders. ...