韦东山uboot_内核_根文件系统学习笔记5.1~5.3-第005课_字符设备驱动_第001~003节_字符设备驱动程序之概念介绍/字符设备驱动程序之LED驱动程序_编写编译

一 驱动程序概念介绍

在这里插入图片描述
在这里插入图片描述

  1. 执行swi+XXX汇编指令,触发内核的异常处理中断。
  2. System Call Interface根据中断源调用不同的处理函数。eg:open->swi value1/read->swi value2/write->swi value3。根据传入的不同值(value1/value2/value3)调用sys_read/sys_open/sys_write。
  3. sys_read/sys_open/sys_write这些函数属于Virtual File System(VFS)。

VFS作为内核子系统,为用户空间程序提供了文件系统相关的接口。所有实际文件系统依赖VFS共存,依靠VFS系统工作。
1、VFS提供通用文件系统接口:用户空间程序可以利用标准的UNIX文件系统调用,如open()\read()\write(),对不同物理介质上的不同文件系统进行操作。
2、VFS提供文件系统抽象层:VFS提供了一个通用文件系统模型,该模型囊括了所有实际文件系统的常用功能和行为。VFS抽象层定义了所有实际文件系统支持的基本的、概念上的接口和数据结构,所以VFS能衔接各种实际文件系统。

  1. 对于同样的open函数,下面这两个行为显然是不一样的,谁来实现不一样的行为呢?VFS根据打开的不同的文件找到对应不同的驱动程序,调用不同驱动程序中的open/read/write函数。
    在这里插入图片描述
    打开设备的属性是c(字符型设备),找到主设备号major

二 字符设备驱动程序之LED驱动程序_编写编译

  1. 关于主设备号major
    设备文件通常都在 /dev 目录下,查看ubuntu虚拟机上加载的设备:
    在这里插入图片描述
    在这里插入图片描述

c:字符型设备;
-:常规文件
d:目录
10:主设备号,major
175:次设备号,minor

  1. 关于主设备号和register_chrdev函数作用:
    应用程序根据主设备号找到特定驱动程序对应的file_operations。在执行register_chrdev函数的时候,内核会在驱动程序对应的一个链表中以major为索引找到最后一个位置,把需要挂载的驱动程序的file_operations结构体填充进去。
major = register_chrdev(0, "hello", &hello_drv); /* /dev/hello,其中0表示主设备号,hello表示驱动程序名字,hello_drv为file_operations结构体*/

在这里插入图片描述

  1. 总结过程:驱动程序和应用程序的联系
    (1)驱动程序侧
    在这里插入图片描述
    (2)从应用程序侧
    在这里插入图片描述

  2. makefile分析

(1)makefile源码如下:

KERN_DIR = /work/system/linux-2.6.22.6

all:
	make -C $(KERN_DIR) M=`pwd` modules 			//-C表示进入某个目录

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order

obj-m	+= myleds.o

(2)Makefile分析

obj-m += module_test.o  

表示将我们的module_test.o编译成一个模块。

make -C $(KERN_DIR) M=`pwd` modules  

-C $(KERN_DIR) :参数指定内核源码树目录,当执行make命令的时候会跳转到这个目录下去执行,而不是在当前目录下执行make,这个也是为什么在ubuntu和开发板运行模块时,KERN_DIR不同的原因。

M=pwd :“ ` ”这里不是但引号,是单反引号,用来指定执行完make命令之后的返回目录,也就是当前目录。

modules就是一个目标,所以这个语句连起来就是: 跳转到指定的目录下 执行 make modules,执行完之后在返回到当前的目录,并把编译好的模块复制到当前目录下;所以由此可知modules肯定是一个内核源码树下的Makefile中的一个目标,这个目标定义了内核模块的编译规则,所以切不可胡乱改modules,所以得知,我们这里的Makefile只不过是一个入口,真正的模块编译工作是在内核源码树下的Makefile中进行的,所以我们才需要在这个Makefile中指定一个入口地址(也就是内核源码树的路径)给我们的make管理器。

.PHONY:clean

声明clean目标是一个伪目标最后那一句也是一样的解析,只不过定义的目标不一样。

总结:模块的makefile非常简单,本身并不能完成模块的编译,而是通过make -C进入到内核源码树下借用内核源码的体系来完成模块的编译链接的。这个Makefile本身是非常模式化的,3和4部分是永远不用动的,只有1和2需要动。1是内核源码树的目录,你必须根据自己的编译环境修改路径。

三 字符设备驱动程序之LED驱动程序_测试改进

  1. 记录一个错误
    (1)ubuntu编译完成的可执行程序在arm开发板上执行上报以下错误
    在这里插入图片描述
    (2)经过网上检索,发现是由于编译的时候编译指令错误
    是我在写个简单的makefile的时候,在写编译命令的时候,写成了:
$(CC) -o $(OUT_FILE) -c $(SRC_FILE)

而实际应该是去掉-c选项,直接编译成可执行文件:

$(CC) -o $(OUT_FILE) $(SRC_FILE)

其中gcc的-c选项的意思是:

-c 只激活预处理,编译,和汇编,也就是他只把程序做成obj文件。例子用法: gcc -c hello.c 。他将生成.o的obj文件

也就是,如果不加-c,默认就直接编译生成可执行文件了
加上-c就只编译成目标obj文件,就不往下继续编译成可执行文件了。
具体参见一下博客文章:

https://www.cnblogs.com/cainiaoaixuexi/p/3890453.html

  1. register_chrdev函数讲解
    驱动程序中执行了register_chrdev函数后,查看/proc/devices文件(执行指令cat /proc/devices

/proc/devices文件:这个文件列出linux已经加载的字符和块设备的主设备号,以及分配到这些设备号的设备名称。
lsmod(list modules)指令:列出所有已载入系统的模块。
/dev目录:在这个目录中包含了所有Linux系统中使用的外部设备。

  1. 自动创建设备节点
    (1)最简单的驱动程序框架
    在这里插入图片描述

手动通过主设备号创建设备节点命令语法:

 mknod /dev/??? 设备类型 主设备号 次设备号
 eg:mknod /dev/xxx c 252 0

对应app程序侧操作这个设备的时候的语法:

fd = open("/dev/xxx", O_RDWR);

加载驱动程序的时候必须手动创建设备节点很繁琐,那么如何自动创建设备节点呢?
(2)自动创建设备节点
应用程序中有一个"udev"机制,对于 busybox 来说是 mdev。通过读取内核信息来创建设备节点即/dev/xxx文件(用法参见:busybox-1.7.0/doc/mdev.txt文件)。
a. 系统信息是哪些?
驱动程序需要在/sys/module目录下生成对应的信息提供给mdev!

/sys目录解析:Sysfs文件系统是一个类似于proc文件系统的特殊文件系统,用于将系统中的设备组织成层次结构,并向用户模式程序提供详细的内核数据结构信息。其实就是在用户态可以通过对sys文件系统的访问,来看内核态的一些驱动或者设备等。
在这里插入图片描述

/sys/devices目录解析:该目录下是全局设备结构体系,包含所有被发现的注册在各种总线上的各种物理设备。一般来说,所有的物理设备都按其在总线上的拓扑结构来显示。/sys/devices是内核对系统中所有设备的分层次表达模型,也是/sys文件系统管理设备的最重要的目录结构。为什么?因为其他目录基本是分类组织链接文件,实际指向该目录内容。
在这里插入图片描述

/sys/module目录解析:该目录下有系统中所有的模块信息,不论这些模块是以内联(inlined)方式编译到内核映像文件中还是编译为外模块(.ko文件),都可能出现在/sys/module中。即module目录下包含了所有的被载入kernel的模块。
在这里插入图片描述

/sys/class目录解析:该目录下包含所有注册在kernel里面的设备类型,这是按照设备功能分类的设备模型,每个设备类型表达具有一种功能的设备。每个设备类型子目录下都是这种哦哦那个设备类型的各种具体设备的符号链接,这些链接指向/sys/devices/name下的具体设备。设备类型和设备并没有一一对应的关系,一个物理设备可能具备多种设备类型;一个设备类型只表达具有一种功能的设备,比如:系统所有输入设备都会出现在/sys/class/input之下,而不论它们是以何种总线连接到系统的。(/sys/class也是构成linux统一设备模型的一部分)
在这里插入图片描述

在这里插入图片描述

如何提供?

firstdrv_class = class_create(THIS_MODULE, "firstdrv");//在sys目录下创建firstdrv设备类目录:/sys/module/firstdrv

在这里插入图片描述
在这里插入图片描述

如何根据以上信息创建设备节点?即取代mknod指令。

firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); //在dev目录下创建xyz目录:/dev/xyz
//firstdrv_class:创建设备所属的类
//NULL:该设备的父设备,如果没有就指定为NULL
//MKDEV(major, 0):主设备号
//NULL:从设备号
//"xyz":设备名称

下图中:主设备号:252/次设备号:0/c:字符设备
在这里插入图片描述

在这里插入图片描述

  1. 为什么自动加载驱动的时候/sys目录下的信息只要更新,mdev就会自动识别呢?
    因为创建根文件系统的时候,在脚本文件/etc/init.d/rcS里面如下图所示。一旦有设备加载或者卸载,…不理解
    在这里插入图片描述

  2. 自动加载设备节点对应的app程序

上面提到了驱动程序下面代码创建了/dev/xyz目录后,应用程序可以直接使用fd = open("/dev/xyz", O_RDWR);而忽略其主设备号而调用到驱动程序。

firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); //在dev目录下创建xyz目录:/dev/xyz

猜你喜欢

转载自blog.csdn.net/xiaoaojianghu09/article/details/104228404