• 一个简单的驱动

  模块的使用能使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. Python基础:开篇

    一.概述 Python基础 是对 Python(2.7版本)基本语法的要点总结,主要参考了以下资料: Python核心编程(第二版) Learning Python (3rd Edition) Pyt ...

  2. windbg学习进阶之——dump分析常用命令收集

    #重要说明 (1) windbg命令分为标准命令,元命令和扩展命令. 标准命令提供最基本的调试功能,不区分大小写.如:bp  g  dt  dv  k等 元命令提供标准命令没有提供的功能,也内建在调试 ...

  3. .NET开发 正则表达式中的 Bug

    又发现了一个 .net 的 bug!最近在使用正则表达式的时候发现:在忽略大小写的时候,匹配值从 0xff 到 0xffff 之间的所有字符,正则表达式竟然也能匹配两个 ASCII 字符:i(code ...

  4. MySQL架构

    一.MySQL逻辑架构         第一层,即最上一层,所包含的服务并不是MySQL所独有的技术.它们都是服务于C/S程序或者是这些程序所需要的 :连接处理,身份验证,安全性等等.         ...

  5. 购物车信息存cookie

    //以商品为单位分别存入到各个不同的cookie中,避免因为cookie值过大,导致数据存储失败 $cart_info_one[brand_name] = $parent['brand_name']; ...

  6. AC自动机基础知识讲解

    AC自动机 转载自:小白 还可参考:飘过的小牛 1.KMP算法: a. 传统字符串的匹配和KMP: 对于字符串S = ”abcabcabdabba”,T = ”abcabd”,如果用T去匹配S下划线部 ...

  7. oauth授权协议的原理

    http://oauth.net/2/ 协议的原文.原来是1.0版本,现在是2.0版本了 https://ruby-china.org/topics/15396 https://blog.yorkxi ...

  8. 轻量级SaaS在线作图工具ProcessOn

    俗话说“一图胜千言”,在办公应用领域,流程图是一个非常好的表现企业业务流程或工作岗位规范等内容的展现形式,比如去给客户做调研,回来后都要描述出客户的关键业务流程,谁.什么时候.在什么地方.负责什么事情 ...

  9. python基础(2)

    1.lambda函数 学习条件运算时,对于简单的 if else 语句,可以使用三元运算来表示,函数同样有简单的表示方法 # ###################### 普通函数 ######### ...

  10. arcgis andriod 长按获得当前信息

    // 长按显示鼠标点坐标及比例尺 private class myLongPressListener implements OnLongPressListener { private static f ...