Linux 第n次驱动入门(一) ---- helloWorld

来源:互联网 发布:水电安装 知乎 编辑:程序博客网 时间:2024/06/08 16:47

Linux驱动 helloWorld


根据下面的helloWorld.c驱动来了解总结内核中的printk,__init,__exit

helloWorld.c

#include <linux/init.h>#include <linux/module.h>static char* name = "the man like wind ";module_param(name, charp, S_IRUGO);static int age = 18;module_param(age, int, 0644);static int __init helloWorld_init(void){    int ret = 0;    printk(KERN_INFO "------------------>see you again , world   :-)\n");    printk(KERN_INFO "-  name :     %s\n",name);    printk(KERN_INFO "-  age   :      %d\n",age);    return ret;}static void __exit helloWorld_exit(void){    printk(KERN_INFO"------------------>bye %s  , good world   :(\n",name);}int add_integer(int a,int b){    return a+b;}EXPORT_SYMBOL(add_integer);int sub_integer(int a,int b){    return a-b;}EXPORT_SYMBOL(sub_integer);module_init(helloWorld_init);module_exit(helloWorld_exit);MODULE_AUTHOR("Qian <XXXXXXXX@qq.com>");MODULE_LICENSE("GPL v2");MODULE_VERSION("V9.9");MODULE_ALIAS("a simple helloWorld driver");

1.__exit 和 __init

__init,__initdata,__exit,__exitdata的定义全部在<Linux/init.h>,这些宏定义的作用就是告诉编译器把这些函数或者数据放在相应的selection中,其中__init修饰函数。__initdata修饰变量。

静态加载(直接编入内核)

__init函数在区段.initcall.init中还保存了一份函数指针,在初始化时内核会通过这些函数指针调用这些__init函数指针,并在整个初始化完成后,释放整个init区段,当模块直接编进内核时,在linux启动到最后可以看到类似下面的提示,静态加载时__exit将没有作用也不会编入内核,因为内置了就不能卸载
PHY: 0:01 - Link is Up - 100/FullVFS: Mounted root (nfs filesystem) on device 0:14.devtmpfs: mountedFreeing init memory: 196KINIT: version 2.86 booting

动态加载(insmod modprobe)

__init进行初始化工作,在模块装载之后,模块装载就会将初始化函数扔掉,这样可以将该函数占用的内存释放出来。__exit进行清理工作

2.printk打印函数

日志级别一共有8个级别,printk的日志级别定义如下(在include/Linux/kernel.h中):  #define KERN_EMERG 0/*紧急事件消息,系统崩溃之前提示,表示系统不可用*/  #define KERN_ALERT 1/*报告消息,表示必须立即采取措施*/  #define KERN_CRIT 2/*临界条件,通常涉及严重的硬件或软件操作失败*/  #define KERN_ERR 3/*错误条件,驱动程序常用KERN_ERR来报告硬件的错误*/ #define KERN_WARNING 4/*警告条件,对可能出现问题的情况进行警告*/  #define KERN_NOTICE 5/*正常但又重要的条件,用于提醒*/  #define KERN_INFO 6/*提示信息,如驱动程序启动时,打印硬件信息*/  #define KERN_DEBUG 7/*调试级别的消息*/

查看修改打印级别

#查看Ubuntu打印级别study@ubuntu:~$ cat /proc/sys/kernel/printk4   4   1   7######################################################################打印出来4个数字的意义                                              #控制台日志级别:优先级高于该值的消息将被打印至控制台             #默认的消息日志级别:将用该优先级来打印没有优先级的消息#最低的控制台日志级别:控制台日志级别可被设置的最小值(最高优先级)#默认的控制台日志级别:控制台日志级别的缺省值######################################################################发现在Ubuntu下无法修改打印级别,本着好学的态度,在一块开发板上,这样当我们在驱动中为了调试,应该把打印级别调到最低就是7~ # cat /proc/sys/kernel/printk7       4       1       7~ # echo 6 4 1 7 > /proc/sys/kernel/printk ~ # cat /proc/sys/kernel/printk6       4       1       7

3.几个驱动使用的命令

insmod  #加载驱动,会执行module_init()修饰的函数rmmod   #卸载驱动,执行module_exit()修饰的函数lsmod   #列出目前加载的驱动 等同于cat /proc/modulestree /sys/module/  #insmod后会在/sys/moudle生成驱动名的一个目录modprobe #在加载时会同时加载依赖的模块 依赖关系在/lib/modules/<kerel_version>/modules.dep,格式很简单,下面是copy过来的一个    kernel/lib/cpu-notifier-error-inject.ko: kernel/lib/notifier-error-inject.komodinfo #查看模块信息

4驱动程序结构

  • 模块加载函数 ,insmod时会被内核执行,完成初始化工作
  • 模块卸载函数 ,rmmod时会被内核执行,完成清理工作
  • 模块许可声明 ,MODULE_LICENSE(“GPL v2”),不声明会在加载时提示污染内核
  • 模块参数(可选),外界可以传给驱动变量的一个数值
  • 导出符号(可选),可以导出一个函数给其他驱动使用
  • 作者别名等其他描述信息(可选) , 使用MODULE_xx()声明

4.1模块参数

在/sys/module/驱动名字/parameters所以该驱动导出模块参数的文件,可以cat查看参数值

#可以传入的参数类型有byte short ushort int uint long ulong charp boo等static char* name = "the man like wind ";module_param(name, charp, S_IRUGO);static int age = 18;module_param(age, int, 0644);#这样在使用时可以insmod *.ko name="leo" age=23#如果是静态加载进入内核可以在uboot的bootargs里设置“模块名.参数名=值”

4.2导出符号

使用下面宏导出符号到内核符号表中
EXPORT_SYMBOL()
EXPORT_SYMBOL_GPL()

#可以通过/proc/kallsyms查找导出符号相关信息int add_integer(int a,int b){    return a+b;}EXPORT_SYMBOL(add_integer);int sub_integer(int a,int b){    return a-b;}EXPORT_SYMBOL(sub_integer);
原创粉丝点击