Linux内核驱动学习(三)----内核模块基础 | 设计 | 可选项

内核模块基础--特点及其命令使用

1、模块本身并不被编译进内核文件(zImage或bzImage)

2、可以根据需要在内核运行时动态加载、卸载----》进而达到节省空间的目的

命令详解(以下载驱动DNW为例):

insmod 模块名称(注意有.ko后缀)--安装

insmod dnw_usb.ko

lsmod-->查看安装的内核模块

rmmod 模块名称(注意没有.ko后缀)---->卸载内核模块

rmmod dnw_usb


内核模块设计--简单的模块编写

根据上图范例代码,比较应用程序代码的区别

与应用程序代码区别(主要涉及三个要素-->缺一不可)

1、引出内核模块的入口在哪里?(内核模块函数没有main函数)

内核模块函数的入口是由宏module_init指明的,,其参数即是其函数入口。当使用insmod加载模块时,有宏module_init指定的初始化函数被调用。

2、出口(卸载函数)---宏module_exit指出。当使用rmmod卸载函数时,由module_exit指定的模块清除函数被调用。

3、头文件 #include<linux/init.h>   #include <linux/module.h>

注:应用程序由main函数指定程序入口。

除了以上使用module_init、module_exit指定初始化、卸载函数外。也可以给初始化函数与卸载函数取固定的名字:

初始化------>int init_module(void)

卸载函数---->void cleanup_module(void)

一、根据以上三要素编写简单的驱动

二、编写该模块的Makefile。。(其格式较为固定)内容如下:

obj-m := helloworld.o //obi-m指明生成的内核模块名称
helloworld-objs := file1.o file2.o file3.o//多文件生成一个内核模块时的书写格式 ,
KDIR := /home/kindlyde/Desktop/arm-5/lesson3_3/linux-ok6410 //编译依赖的内核的路径
all:
         make -C ${KDIR} M=${PWD} modules ARCH=arm CROSS_COMPILE=arm-linux- //选项M代表需编译的内核模块的位置
rm:
         rm -f *.o *.ko *.order *.symvers

三、之后编译make;生成helloworld.ko文件,,把该文件放置到NFS文件系统中。。

四、挂载nfs文件系统之后,安装helloworld.ko内核模块

五、卸载helloworld内核模块


内核模块可选项:

1、模块声明

1.1MODULE_LICENSE(“遵守的协议”)----“GPL“、”GPL
v2‘

申明该模块遵守的许可证协议

1.2MODULE_AUTHOR(“作者”)

申明模块的作者

1.3MODULE_DESCRIPTION(“模块的功能描述”)

申明模块的功能

1.4MODULE_VERSION(“v1.0”)

申明模块的版本

以申明模块遵守的协议为例:

声明之后安装该内核模块(不会出现 "module  license ‘unspecified‘ taints kernel"字样-->):

2、向内核模块的传入参数

主要通过宏module_param指定保存模块参数的变量;然后模块参数在加载模块时传递参数给模块

形式如下: module_param(name,type,perm)

参数:name: 变量的名称

type:变量的类型,bool:布尔型 int:整型  charp:字符串型

perm:访问权限。S_IRUGO:读权限
 S_IWUSR:写权限

3、符号导出

---->在不同内核模块之间实现函数调用

使用符号导出所使用的宏:(注意导出语句的位置,应在该变量定义之后。)

EXPORT_SYMBOL(符号名)

EXPORT_SYMBOL_GPL(符号名)

注:EXPORT_SYMBOL_GPL只能用于包含GPL许可证的模块

建立另一个add.c文件,在helloworld内核模块中实现调用

helloworld文件如下:

add.c文件如下:

修改Makefile(生成两个独立的模块)

编译生成两个内核模块: helloworld.ko  和 add.ko

先安装add.ko驱动后安装helloworld.ko驱动。。调试出现以下结果,。。


注--调试时遇到的错误

1、调试printk时未打印。

如下:画线的printk变量未打印。。

制作的文件系统的关于日志消息的默认设置为 7 7 1 7:如下

这四个值是在kernel/printk.c 中被定义的,如下:

int console_printk[4] = {

DEFAULT_CONSOLE_LOGLEVEL,/* console_loglevel */-->控制台日志级别

DEFAULT_MESSAGE_LOGLEVEL,/* default_message_loglevel */--->默认消息日志级别

MINIMUM_CONSOLE_LOGLEVEL,/* minimum_console_loglevel */-->最低控制台日志级别

DEFAULT_CONSOLE_LOGLEVEL, /* default_console_loglevel */--->控制台默认优先级

};

以上关于四个数字的意义:

控制台日志级别:优先级高于该值的消息将被打印至控制台

默认的消息日志级别:将用该优先级来打印没有优先级的消息--->(即没有指定优先级的日志默认为优先级为7,,由上知等于控制台优先级。。则不打印消息。。)

最低的控制台日志级别:控制台日志级别可被设置的最小值(最高优先级)

默认的控制台日志级别:控制台日志级别的缺省值

更改默认消息级别低于7后。打印出消息;如下

设置不同级别的日志的意义:

更好地控制不同级别的信息显示在控制台上,内核设置了控制台的日志级别console_loglevel。printk日志级别的作用是打印一定级别的消息,与之类似,控制台只显示一定级别的消息。

2、安装内核模块驱动时遇见如下错误:

原因:内核模块函数写错了。。缺少入口函数。。

时间: 2024-10-16 17:26:22

Linux内核驱动学习(三)----内核模块基础 | 设计 | 可选项的相关文章

linux内核驱动学习指南

目录: 1.参考链接 1. 参考链接 小白的博客 ONE_Tech 你为什么看不懂Linux内核驱动源码? 求教怎么学习linux内核驱动 原文地址:https://www.cnblogs.com/agui125/p/10071452.html

Linux内核驱动学习(二)----根文件系统的构成 (root filesystem)

1.建立根文件系统目录和文件 1.1创建目录 1.2创建设备文件(命令mknod):必须创建设备文件---consle\null 1.3创建配置文件---复制已有的/etc目录下的文件 1.4添加内核模块 进入Linux内核目录下,(注意,应该先编译内核,即命令make uImage ARCH=arm  CROSS_COMPILE=arm-linux-) 1.4.1.编译内核模块---命令 make modules ARCH=arm CROSS_COMPILE=arm-linux- 1.4.2.

linux内核驱动学习(八)----驱动分类 | 学习方法 | 硬件访问

驱动分类: 对于驱动,我们一般按两种方法进行分类:常规分类法和总线分类法. 按照常规分类法,可以分为以下三类: 1.字符设备: 以字节为最小访问单位的设备.一般通过字符设备文件来访问字符设备驱动程序.字符驱动程序则负责驱动字符设备, ,这样的驱动通常支持open.close.read.write系统调用,应用程序可以通过设备文件(比如/dev/ttySAC0等)来访问字符设备(串口).例如:串口\led\按键 2.块设备: 以块(一般512字节)为最 小传输单位的设备.大多数UNIX系统中,块设

Linux内核驱动学习(一)----内核简介 | 配置 | 编译| 安装(PC平台下)

Linux体系结构: 由上图知:Linux架构分为用户空间和内核空间 划分成用户空间与内核空间的原因? 保护操作系统,使应用程序和内核拥有不同的权限.为不同的代码制造不同的等级... 内核空间与用户空间是程序执行的两种不同的状态,通过系统调用和硬件中断能够完成从用户空间到内核空间的转换 内核的构成: a-->系统调用接口(SCI). b-->进程管理(PM). c-->内存管理(MM) . d-->关于处理器代码(arch) . e-->虚拟文件系统(VFS). f-->

Linux内核驱动编程

Linux内核驱动编程 2015-02-12 驱动程序基础的东西这儿就不罗嗦了,百度上有更好的资料,此处我们只是注重实际用处. 下面我们开始写程序: 一.初步helloword程序 首先是来一个简单的hello. hello.c代码: 1 /****************************** 2 3 the first program 4 5 Hello World! 6 7 ******************************/ 8 9 #include <linux/mod

LINUX内核分析第三周学习总结——构造一个简单的Linux系统MenuOS

LINUX内核分析第三周学习总结——构造一个简单的Linux系统MenuOS 黄韧(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 回顾: [计算机三个法宝] 1)存储程序计算机 2)函数调用堆栈 3)中断 [操作系统两把宝剑] 1)中断上下文的切换:保存现场和恢复现场 2)进程上下文的切换 一.使用gdb跟踪调试内核从start_kernel到init进程启动 使用实验楼的虚拟机打开

linux内核数据结构学习总结(undone)

本文旨在整理内核和应用层分别涉及到的数据结构,从基础数据结构的角度来为内核研究作准备,会在今后的研究中不断补充 目录 1. 进程相关数据结构 1) struct task_struct 2. 内核中的队列/链表对象 3. 内核模块相关数据结构 2) struct module 1. 进程相关数据结构 0x1: task_struct 我们知道,在windows中使用PCB(进程控制块)来对进程的运行状态进行描述,对应的,在linux中使用task_struct结构体存储相关的进程信息,task_

你为什么看不懂Linux内核驱动源码?

学习嵌入式Linux驱动开发,最核心的技能就是能够编写Linux内核驱动.深入理解Linux内核.而做到这一步的基础,就是你要看得懂Linux内核源码,了解其基本的框架和具体实现,了解其内核API的使用方法,然后才能根据自己的需求写出高质量的内核驱动程序. 说易行难,很多新人.甚至工作1-2年的开发者刚接触Linux内核时,别说写了,看内核代码可能都是一脸懵逼:明明是C语言,但是就是看不懂是什么意思,除了根据函数名.函数参数.函数的返回值以及注释,了解整个函数的基本功能外,一旦分析其细节,你会发

Linux 网卡驱动学习(五)(收发包具体过程)

函数接口 设备初始化函数 网络设备驱动在 Linux 内核中是以内核模块的形式存在的,对应于模块的初始化,需要提供一个初始化函数来初始化网络设备的硬件寄存器.配置 DMA 以及初始化相关内核变量等.设备初始化函数在内核模块被加载时调用,它的函数形式如下: static int __init xx_init (void) { -- } module_init(xx_init); // 这句话表明模块加载时自动调用 xx_init 函数 设备初始化函数主要完成以下功能: 1. 硬件初始化 因为网络设