热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

LDD学习课程之一

先前一直在外地有事情,耽搁了一些时间。希望能够赶上。一个简单的LDDHELLO#include<linuxmodule.h>#include<linuxinit.

先前一直在外地有事情,耽搁了一些时间。希望能够赶上。

 

一个简单的LDD  HELLO

 

 

 

 

 

以上是一个简单的LDD例子,其中

Hello_init()函数是当驱动加载的时候调用的函数

Hello_out()函数是当驱动卸载的时候调用的函数

__init __exit 表明该函数分别用于初始化和退出模块的函数。

 

linux有内核空间和用户空间之分,printf()用于用户空间,而printk则用于kernel空间

用printk,内核会根据日志级别,可能把消息打印到当前控制台上,这个控制台通常是一个字符模式的终端、一个串口打印机或是一个并口打印机。这些消息正常输出的前提是──日志输出级别小于console_loglevel(在内核中数字越小优先级越高)。

没有指定日志级别的printk语句默认采用的级别是 DEFAULT_ MESSAGE_LOGLEVEL(这个默认级别一般为<4>,即与KERN_WARNING在一个级别上),其定义在linux26/kernel/printk.c中可以找到

  日志级别一共有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/*调试级别的消息*/

 

 

MODULE_LICENSE("GPL")用于给驱动模块加入许可证

GPL            GNU通用许可证

GPL V2      vertion2

GPL and additional rights       GPL和附加权力

Dual BSD/GPL                          兼具BSD/GPL

Dual MPL/GPL                          兼具MPL/GPL

Proprietary                                  专有

 

MODULE_AUTHOR () 描述模块作者

MODULE_DESCRIPTION() 模块描述

MODULE_VERSION()模块版本号

MODULE_DEVICE_TABLE() 支持设备列表

MODULE_ALIAS 别名

 

 

makefile编写,写makefile让我有点云里雾里的感觉

 

 

通过make后生成hello.ko文件,用insmod hello.ko加载驱动模块,也可以使用modeprobe加载模块

 

modprobe命令比insmod命令要强大,它在加载某模块时会同时加载该模块所依赖的其他模块
使用modprobe -r filename的方式卸载将同时其依赖的模块

 

在虚拟终端是无法看到相关信息的.键入dmesg命令后,可以观察到相关的printk打印的信息如下所示:

 

 

利用命令rmmod hello.ko可以卸载该模块。

 

 

带模块参数的写法:

 

 

参数不能像用户空间那样写在init函数中,需要通过module_param()来注册参数,如上述代码所示。关于模块参数,在别人的blog中找了些资料,摘录如下:

http://www.360doc.com/content/09/0428/11/36491_3298911.shtml

、模块中的参数
    (内核允许对驱动程序指定参数,而这些参数可在装载驱动程序模块时改变[color=#000000]。 example : insmod hello-param.ko howmany=2 whom="KeKe" TNparam=4,3,2,1[/color])
     [color=#000000]对于如何向模块传递参数,Linux kernel 提供了一个简单的框架。其允许驱动程序声明参数,并且用户在系统启动或模块装载时为参数指定相应值,在驱动程序里,参数的用法如同全局变量。这些模块参数也能够在sysfs中显示出来。结果,有许许多多的方法用来创建和管理模块参数。
    通过宏module_param()定义一个模块参数:
module_param(name, type, perm);
    这里,name既是用户看到的参数名,又是模块内接受参数的变量; type表示参数的数据类型,是下列之一:byte, short, ushort, int, uint, long, ulong, charp, bool, invbool。这些类型分别是:a byte, a short integer, an unsigned short integer, an integer, an unsigned integer, a long integer, an unsigned long integer, a pointer to a char, a Boolean, a Boolean whose value is inverted from what the user specifies. The byte type is stored in a single char and the Boolean types are stored in variables of type int. The rest are stored in the corresponding primitive C types. 最后,perm指定了在sysfs中相应文件的访问权限。访问权限用通常的八进制格式来表示,例如,用0644(表示ower具有读写权限,group和everyone只读权限), 或者用通常的S_Ifoo定义,例如,S_IRUGO | S_IWUSR (表示everyone具有读权限,用户具有写权限)。用0表示完全关闭在sysfs中相对应的项。
    其实宏不会声明变量,因此在使用宏之前,必须声明变量。所以,典型地用法如下:
static unsigned int use_acm = 0;   
module_param(use_acm, uint, S_IRUGO);
    这些必须写在模块源文件的开头部分。即use_acm是全局的。
    我们也可以使模块源文件内部的变量名与外部的参数名有不同的名字。这通过宏module_param_named()定义。
module_param_named(name, variable, type, perm);
    这里name是外部可见的参数名,variable是源文件内部的全局变量名。例如:
static unsigned int max_test = 9;
module_param_name(maximum_line_test, max_test, int, 0);
    如果模块参数是一个字符串时,通常使用charp类型定义这个模块参数。内核复制用户提供的字符串到内存,并且相对应的变量指向这个字符串。例如:
static char *name;
module_param(name, charp, 0);
    另一种方法是通过宏module_param_string()让内核把字符串直接复制到程序中的字符数组内。
module_param_string(name, string, len, perm);
    这里,name是外部的参数名,string是内部的变量名,len是以string命名的buffer大小(可以小于buffer的大小,但是没有意义),perm表示sysfs的访问权限(或者perm是零,表示完全关闭相对应的sysfs项)。例如:
static char species[BUF_LEN];
module_param_string(specifies, species, BUF_LEN, 0);
    上面说得只是给模块传入一个参数的情况,如果给模块传入多个参数,那该怎么办呢?可以通过宏module_param_array()给模块传入多个参数。 用法如下:
module_param_array(name, type, nump, perm);
    这里,name既是外部模块的参数名又是程序内部的变量名,type是数据类型,perm是sysfs的访问权限。指针nump指向一个整数,其值表示有多少个参数存放在数组name中。值得注意是name数组必须静态分配。例如:
static int finsh[MAX_FISH];
static int nr_fish;
module_param_array(fish, int, &nr_fish, 0444);
    通过宏module_param_array_named()使得内部的数组名与外部的参数名有不同的名字。例如:
module_param_array_named(name, array, type, nump, perm);
    这里的参数意义与其它宏一样。
    最后,通过宏MODULE_PARM_DESC()对参数进行说明:
static unsigned short size = 1;
module_param(size, ushort, 0644);
MODULE_PARM_DESC(size, “The size in inches of the fishing pole” /
“connected to this computer.” );

7、导出符号
   当装载模块的时候,模块动态地链接入内核之中。动态链接的二进制代码只能调用外部函数,然而,外部函数必须明确地输出,在内核中,通过EXPORT_SYMBOL()和EXPORT_SYMBOL_GPL来达到这个目的。
输出的函数可以被其它模块调用。没有输出过的函数不能被其它模块调用。模块比核心内核映像代码具有更严格的链接和调用规则。因为所有核心源文件链接成一个单一的作为基础的映像,因此在内核中核心代码可以调用任何非静态的接口。当然,输出符号也必须是非静态属性。
一套输出的内核符号称之为输出的内核接口,甚至称之为kernel API。
输出一个内核符号是举手之劳之事。当函数声明之时,在其后用EXPORT_SYMBOL()把函数输出。
例如:
/* it will receive control requests including set_configuration(), which enables non-control requests. 
*/
int usb_gadget_register_driver(struct usb_gadget_driver *driver)
{

}
EXPORT_SYMBOL(usb_gadget_register_driver) ;
    从此以后,任何模块都可以调用函数usb_gadget_register_driver(),只要在源文件中包含声明这个函数的头文件,或者extern这个函数的声明。
    有些开发者希望他们的接口只让遵从GPL的模块调用。通过MODULE_LICENSE()的使用,内核链接器能够强制保证做到这点。如果你希望前面的函数仅被标有GPL许可证的模块访问,那么你可以用如下方式输出符号:
EXPORT_SYMBOL_GPL(usb_gadget_register_driver);
    如果你的代码配置为模块方式,那么必须确保:源文件中使用的所有接口必须是已经输出的符号,否则导致在装载时链接错误。

 

 

 


推荐阅读
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文介绍了在rhel5.5操作系统下搭建网关+LAMP+postfix+dhcp的步骤和配置方法。通过配置dhcp自动分配ip、实现外网访问公司网站、内网收发邮件、内网上网以及SNAT转换等功能。详细介绍了安装dhcp和配置相关文件的步骤,并提供了相关的命令和配置示例。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文介绍了一个Java猜拳小游戏的代码,通过使用Scanner类获取用户输入的拳的数字,并随机生成计算机的拳,然后判断胜负。该游戏可以选择剪刀、石头、布三种拳,通过比较两者的拳来决定胜负。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • HDFS2.x新特性
    一、集群间数据拷贝scp实现两个远程主机之间的文件复制scp-rhello.txtroothadoop103:useratguiguhello.txt推pushscp-rr ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 概述H.323是由ITU制定的通信控制协议,用于在分组交换网中提供多媒体业务。呼叫控制是其中的重要组成部分,它可用来建立点到点的媒体会话和多点间媒体会议 ... [详细]
author-avatar
手机用户2502853847
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有