首先贴代码helloworld.c和Makefile

/*************************************************************************
> File Name: helloworld.c
> Author: hailin.ma
> Mail: mhl2018@126.com
> Created Time: Wed 15 Jul 2015 02:39:35 PM CST
************************************************************************/ #include <linux/init.h>
#include <linux/module.h> static char* mod_name = "hello";
static int mod_num = ; static int __init hello_init(void)
{
printk(KERN_INFO"hello world init! mod_name = %s,mod_num = %d\n",mod_name,mod_num);
return ;
} static void __exit hello_exit(void)
{
printk(KERN_INFO"hello world exit!\n");
} module_init(hello_init);
module_exit(hello_exit); module_param(mod_name,charp,S_IRUGO);
module_param(mod_num,int,S_IRUGO);
MODULE_AUTHOR("hailin.ma <mhl2018@126.com>");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("A simple hello world module");
MODULE_ALIAS("simple driver test");

Makefile

#CFLAGS = -g
KVERS = /lib/modules/$(shell uname -r)/build obj-m += hello.o
hello-objs:=helloworld.o all:
make -C $(KVERS) M=$(PWD) modules
@rm *.o clean:
make -C $(KVERS) M=$(PWD) clean

make 执行后将生成hello.ko文件,命令:insmod hello.ko 即可加载模块到内核。加载 hello.ko 后,内核中将包含
/sys/module/hello 目录,该目录下又包含一个 refcnt 文件和一个 sections 目录,在/sys/module/hello
目录下运行“ tree –a” 得到如下目录树:

modinfo <模块名 >命令可以获得模块的信息,包括模块作者、模块的说明、模块所支持
的参数以及 vermagic:

insmod  加载模块

lsmod  查看已加载模块, lsmod 命令实际上是读取并分析“/proc/modules” 文件。

rmmod  卸载模块

tail /var/log/messages 查看printk打印消息

一个 Linux 内核模块主要由如下几个部分组成。
( 1 )模块加载函数(一般需要)。
当通过 insmod 或 modprobe 命令加载内核模块时,模块的加载函数会自动被内核执行,完成
本模块的相关初始化工作。

static int _ _init initialization_function(void)      //初始化函数
{
/* 初始化代码 */
}
module_init(initialization_function);    //指定模块初始化函数

模块加载函数必须以“ module_init(函数名 )” 的形式被指定。它返回整型值,若初始化成功,
应返回 0。而在初始化失败时,应该返回错误编码。在 Linux 内核里,错误编码是一个负值,在
<linux/errno.h>中定义,包含-ENODEV、 -ENOMEM 之类的符号值。总是返回相应的错误编码是
种非常好的习惯,因为只有这样,用户程序才可以利用 perror 等方法把它们转换成有意义的错误
信息字符串。
在 Linux 2.6 内核中,可以使用 request_module(const char *fmt, …)函数加载内核模块,驱动开
发人员可以通过调用
request_module(module_name);

request_module("char-major-%d-%d", MAJOR(dev), MINOR(dev));
这种灵活的方式加载其他内核模块。
在 Linux 中,所有标识为_ _init 的函数在连接的时候都放在.init.text 这个区段内,此外,所有
的_ _init 函数在区段.initcall.init 中还保存了一份函数指针,在初始化时内核会通过这些函数指针
调用这些_ _init 函数,并在初始化完成后,释放 init 区段(包括.init.text、 。initcall.init等)。

( 2)模块卸载函数(一般需要)。
当通过 rmmod 命令卸载某模块时,模块的卸载函数会自动被内核执行,完成与模块卸载函数
相反的功能。

static void _ _exit cleanup_function(void)    //模块卸载函数
{
/* 释放代码 */
}
module_exit(cleanup_function);      //指定模块卸载函数

模块卸载函数在模块卸载的时候执行,不返回 任何值,必须以“ module_exit(函数名 )” 的形
式来指定。
通常来说,模块卸载函数要完成与模块加载函数相反的功能, 如下所示。
! 若模块加载函数注册了 XXX,则模块卸载函数应该注销 XXX。
! 若模块加载函数动态申请了内存,则模块卸载函数应释放该内存。
! 若模块加载函数申请了硬件资源(中断、 DMA 通道、 I/O 端口和 I/O 内存等)的占用,
则模块卸载函数应释放这些硬件资源。
! 若模块加载函数开启了硬件,则卸载函数中一般要关闭之。
和_ _init 一样, _ _exit 也可以使对应函数在运行完成后自动回收内存。实际上, _ _init 和_ _exit
都是宏,其定义分别为:
#define _ _init _ _attribute_ _ (( _ _section_ _ (".init.text")))

#ifdef MODULE
#define _ _exit _ _attribute_ _ (( _ _section_ _(".exit.text")))

#else
#define _ _exit _ _attribute_used_ _attribute_ _ (( _ _section_ _(".exit.text")))
#endif
数据也可以被定义为_ _initdata 和_ _exitdata,这两个宏分别为:
#define _ _initdata _ _attribute_ _ (( _ _section_ _ (".init.data")))

#define _ _exitdata _ _attribute_ _ (( _ _section_ _(".exit.data")))

( 3)模块许可证声明(必须)。
许可证( LICENSE)声明描述内核模块的许可权限,如果不声明 LICENSE,模块被加载时,
将收到内核被污染 ( kernel tainted)的警告。
在 Linux 2.6 内核中,可接受的 LICENSE 包括“GPL”、“GPL v2”、“GPL and additional rights”、
“ Dual BSD/GPL”、 “ Dual MPL/GPL” 和“ Proprietary”。
大多数情况下,内核模块应遵循 GPL 兼容许可权。 Linux 2.6 内核模块最常见的是以
MODULE_LICENSE( "Dual BSD/GPL" )语句声明模块采用 BSD/GPL 双 LICENSE。
( 4)模块参数(可选)。
模块参数是模块被加载的时候可以被传递给它的值,它本身对应模块内部的全局变量。

我们可以用 “ module_param(参数名 ,参数类型,参数读/写权限)” 为模块定义一个参数,例如下
列代码定义了 1 个整型参数和 1 个字符指针参数:

static char *book_name = " dissecting Linux Device Driver ";
static int num = ;
module_param(num, int, S_IRUGO);
module_param(book_name, charp, S_IRUGO);

在装载内核模块时,用户可以向模块传递参数,形式为“ insmode(或 modprobe)模块名 参
数名 =参数值”,如果不传递,参数将使用模块内定义的缺省值。
参数类型可以是 byte、 short、 ushort、 int、 uint、 long、 ulong、 charp(字符指针)、 bool 或 invbool
(布尔的反),在模块被编译时会将 module_param 中声明的类型与变量定义的类型进行比较,判断
是否一致。
模块被加载后,在/sys/module/目录下将出现以此模块名命名的目录。当“参数读/写权限” 为 0
时,表示此参数不存在 sysfs 文件系统下对应的文件节点,如果此模块存在“参数读/写权限” 不为 0
的命令行参数,在此模块的目录下还将出现 parameters 目录,包含一系列以参数名命名的文件节点,
这些文件的权限值就是传入 module_param()的“参数读/写权限”,而文件的内容为参数的值。
除此之外,模块也可以拥有参数数组,形式为“module_param_array(数组名,数组类型,数组长,参数
读/写权限)”。从 2.6.0~2.6.10 版本, 需将数组长变量名赋给“数组长”,从 2.6.10 版本开始, 需将数组
长变量的指针赋给“数组长”,当不需要保存实际输入的数组元素个数时,可以设置“数组长” 为 NULL。
运行 insmod,不同参数用空格隔开

( 5)模块导出符号(可选)。
内核模块可以导出符号( symbol,对应于函数或变量),这样其他模块可以使用本模块中的变
量或函数。

linux 驱动学习笔记04--简单驱动的更多相关文章

  1. Linux 驱动学习笔记05--字符驱动实例,实现一个共享内存设备的驱动

    断断续续学驱动,好不容易有空,做了段字符驱动的例子.主要还是跟书上学习在此记录下来,以后说不定能回过头来温故知新. 首先上驱动源码 gmem.c: /************************* ...

  2. linux 驱动学习笔记01--Linux 内核的编译

    由于用的学习材料是<linux设备驱动开发详解(第二版)>,所以linux驱动学习笔记大部分文字描述来自于这本书,学习笔记系列用于自己学习理解的一种查阅和复习方式. #make confi ...

  3. IIC驱动学习笔记,简单的TSC2007的IIC驱动编写,测试

    IIC驱动学习笔记,简单的TSC2007的IIC驱动编写,测试 目的不是为了编写TSC2007驱动,是为了学习IIC驱动的编写,读一下TSC2007的ADC数据进行练习,, Linux主机驱动和外设驱 ...

  4. input子系统学习笔记六 按键驱动实例分析下【转】

    转自:http://blog.chinaunix.net/uid-20776117-id-3212095.html 本文接着input子系统学习笔记五 按键驱动实例分析上接续分析这个按键驱动实例! i ...

  5. linux kernel学习笔记-5内存管理_转

    void * kmalloc(size_t size, gfp_t gfp_mask); kmalloc()第一个参数是要分配的块的大小,第一个参数为分配标志,用于控制kmalloc()的行为. km ...

  6. Redis:学习笔记-04

    Redis:学习笔记-04 该部分内容,参考了 bilibili 上讲解 Redis 中,观看数最多的课程 Redis最新超详细版教程通俗易懂,来自 UP主 遇见狂神说 10. Redis主从复制 1 ...

  7. 20135316王剑桥Linux内核学习笔记

    王剑桥Linux内核学习笔记 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 计算机是如何工作的 个人理 ...

  8. JSP学习笔记(三):简单的Tomcat Web服务器

    注意:每次对Tomcat配置文件进行修改后,必须重启Tomcat 在E盘的DATA文件夹中创建TomcatDemo文件夹,并将Tomcat安装路径下的webapps/ROOT中的WEB-INF文件夹复 ...

  9. Linux系统学习笔记:文件I/O

    Linux支持C语言中的标准I/O函数,同时它还提供了一套SUS标准的I/O库函数.和标准I/O不同,UNIX的I/O函数是不带缓冲的,即每个读写都调用内核中的一个系统调用.本篇总结UNIX的I/O并 ...

随机推荐

  1. 一种扩大View点击范围的方法

    Rect rect = new Rect();mBt0.getHitRect(rect); rect.bottom += 400; TouchDelegate touchDelegate = new ...

  2. IIS7.5 伪静态 脚本映射 配置方法

    首先,是IIS7.0的配置,由于Windows Server 2008操作系统默认的IIS版本为7.0,我们知道,IIS7.0与IIS6.0 核心注意的地方:先要将应用池设置为集成模式,修改OK后,再 ...

  3. sql查询工程结算分包款转出

    总一 借工程结算负数 贷工程结算对冲问题 oralce使用聚合函数wmsys.wm_concat字段显示 clob :应该是,10.2.0.4以前,是varchar2,10.2.0.5开始,是CLOB ...

  4. 常用Java排序算法

    常用Java排序算法 冒泡排序 .选择排序.快速排序 package com.javaee.corejava; public class DataSort { public DataSort() { ...

  5. translate和replace的区别

    今天在oracle数据库中看到replace和translate的嵌套就有点蒙了,于是就上网看了一下,感觉豁然开朗: 今天遇到的问题如下: replace(TRANSLATE(a.deal_msg,' ...

  6. yum提示This system is not registered with RHN.RHN support will be disabled.

    [root@cactiez ~]# yum install mlocateLoading "security" pluginLoading "rhnplugin" ...

  7. inux grep 命令 搜索含有"zynq"字符的文件

    使用命令grep -rl 'zynq' /work/xilinx/u-boot-xlnx-master (有引号)-r 选项表示递归(recursive)遍历所有子目录-l 选项表示只列出文件名 /w ...

  8. [转]Linux软连接和硬链接

    1.Linux链接概念 Linux链接分两种,一种被称为硬链接(Hard Link),另一种被称为符号链接(Symbolic Link).默认情况下,ln命令产生硬链接. [硬连接]硬连接指通过索引节 ...

  9. 【原创】Android内存管理-OnTrimMemory

    Application中有两个与内存管理相关的方法:onLowMemory()和 onTrimMemory(int level),源码如下 @CallSuper public void onLowMe ...

  10. CentOS 6下Apache的https虚拟主机实践

    题目:1.建立httpd服务器,要求: 提供两个基于名称的虚拟主机: (a)www1.buybybuy.com,页面文件目录为/web/vhosts/www1:错误日志为/var/log/httpd/ ...