底层硬件设备驱动插入链表的顺序(驱动)
哪些是驱动:
驱动就是对底层硬件设备的操作进行封装,并向下层提供函数插口。
设备分类:
linux系统将设备分为3类:字符设备、块设备、网络设备。
字符设备:指只能一个字节一个字节读写的设备,不能随机读取设备显存中的某一数据,读取数据须要根据先后次序。字符设备是面向流的设备linux 驱动 开发,常见的字符设备有滑鼠、键盘、串口、控制台和LED设备等。块设备:指可以从设备的任意位置读取一定厚度数据的设备。块设备包括硬碟、磁盘、U盘和SD卡等。网路设备:网路设备可以是一个硬件设备,如网卡;但也可以是一个纯粹的软件设备,例如回环插口(lo).一个网路插口负责发送和接收数据报文。驱动认知:
先看一张图,图中描述了流程,有助了解驱动。
用户态:
内核态:
驱动数组:管理所有设备的驱动,添加或查找,添加是发生在我们编撰完驱动程序,加载到内核。查找是在调用驱动程序,由应用层用户空间去查找使用open函数。驱动插入数组的次序由设备号检索,就是说主设备号和次设备号不仅能分辨不同种类的设备和不同类型的设备,能够起到将驱动程序加载到数组的某个位置,在下边介绍的驱动代码的开发无非就是添加驱动(添加设备号、设备名和设备驱动函数)和调用驱动。
补充:
每位系统调用都对应一个系统调用号,而系统调用号就对应内核中的相应处理函数。所有系统调用都是通过中断0x80来触发的。使用系统调用时,通过eax寄存器将系统调用号传递到内核,系统调用的入参通过ebx、ecx……依次传递到内核和函数一样,系统调用的返回值保存在eax中,所有要从eax中取出字符设备驱动工作原理
字符设备驱动工作原理在linux的世界里一切皆文件,所有的硬件设备操作到应用层就会被具象成文件的操作。我们晓得倘若应用层要访问硬件设备,它必将要调用到硬件对应的驱动程序。Linux内核有这么多驱动程序,应用如何能够精确的调用到底层的驱动程序呢?
补充:
(1)当open函数打开设备文件时,可以依据设备文件对应的structinode结构体描述的信息,可以晓得接出来要操作的设备类型(字符设备还是块设备),都会分配一个structfile结构体。
(2)依据structinode结构体上面记录的设备号,可以找到对应的驱动程序。这儿以字符设备为例。在Linux操作系统中每位字符设备都有一个structcdev结构体。此结构体描述了字符设备所有信息,其中最重要的一项就是字符设备的操作函数插口。
(3)找到structcdev结构体后,linux内核都会将structcdev结构体所在的显存空间首地址记录在structinode结构体i_cdev成员中,将structcdev结构体中的记录的函数操作插口地址记录在structfile结构体的f_ops成员中。
(4)任务完成,VFS层会给应用返回一个文件描述符(fd)。这个fd是和structfile结构体对应的。接出来下层应用程序就可以通过fd找到structfile,之后在structfile找到操作字符设备的函数插口file_operation了。
其中,cdev_init和cdev_add在驱动程序的入口函数中就早已被调用,分别完成字符设备与file_operation函数操作插口的绑定,和将字符驱动注册到内核的工作。
相关视频推荐
免费学习地址:LinuxC/C 开发(前端/音视频/游戏/嵌入式/高性能网路/储存/基础构架/安全)
须要C/C Linux服务器构架师学习资料加qun579733396获取(资料包括C/C ,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDNlinux 输入法,P2P,K8S,Docker,TCP/IP,解释器,DPDK,ffmpeg等),免费分享
字符设备、字符设备驱动与用户空间访问该设备的程序两者之间的关系
如图,在Linux内核中使用cdev结构体来描述字符设备,通过其成员dev_t来定义设备号(分为主、次设备号)以确定字符设备的惟一性。通过其成员file_operations来定义字符设备驱动提供给VFS的插口函数,如常见的open()、read()、write()等。
在Linux字符设备驱动中,模块加载函数通过register_chrdev_region()或alloc_chrdev_region()来静态或则动态获取设备号,通过cdev_init()构建cdev与file_operations之间的联接,通过cdev_add()向系统添加一个cdev以完成注册。模块卸载函数通过cdev_del()来注销cdev,通过unregister_chrdev_region()来释放设备号。
用户空间访问该设备的程序通过Linux系统调用,如open()、read()、write(),来“调用”file_operations来定义字符设备驱动提供给VFS的插口函数。
驱动程序开发步骤
Linux内核就是由各类驱动组成的,内核源码中有大概85%是各类驱动程序的代码。内核中驱动程序种类齐全,可以在同类驱动的基础上进行更改以符合具体单板。
编撰驱动程序的难点并不是硬件的具体操作,而是弄清楚现有驱动程序的框架,在这个框架中加入这个硬件。
通常来说,编撰一个linux设备驱动程序的大致流程如下:
下边就以一个简单的字符设备驱动框架代码来进行驱动程序的开发、编译等。
基于驱动框架的代码开发
下层调用代码
#include #include #include #include void main() { int fd,data; fd = open("/dev/pin4",O_RDWR); if(fd<0){ printf("open failn"); perror("reson:"); } else{ printf("open successn"); } fd=write(fd,'1',1); }
驱动框架代码
#include //file_operations声明 #include //module_initmodule_exit声明 #include //__init__exit 宏定义声明 #include //classdevise声明 #include//copy_from_user 的头文件 #include//设备号dev_t 类型声明 #include //ioremap iounmap的头文件 static struct class *pin4_class; static struct device *pin4_class_dev; static dev_t devno;//设备号 static int major = 231; //主设备号 static int minor = 0; //次设备号 static char *module_name = "pin4"; //模块名 //led_open函数 static int pin4_open(struct inode *inode,struct file *file) { printk("pin4_openn");//内核的打印函数和printf类似 return 0; } //led_write函数 static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos) { printk("pin4_writen"); return 0; } static struct file_operations pin4_fops = { .owner = THIS_MODULE, .open= pin4_open, .write = pin4_write, }; int __init pin4_drv_init(void) //真实驱动入口 { int ret; devno = MKDEV(major, minor);//创建设备号 ret = register_chrdev(major, module_name, &pin4_fops);//注册驱动告诉内核,把这个驱动加入到内核驱动的链表中 pin4_class=class_create(THIS_MODULE, "myfirstdemo"); //用代码在dev自动生成设备 pin4_class_dev =device_create(pin4_class, NULL, devno, NULL, module_name);//创建设备文件 return 0; } void __exit pin4_drv_exit(void) { device_destroy(pin4_class, devno); class_destroy(pin4_class); unregister_chrdev(major, module_name);//卸载驱动 } module_init(pin4_drv_init);//入口,内核加载该驱动(insmod)的时候,这个宏被使用 module_exit(pin4_drv_exit); MODULE_LICENSE("GPL v2");
里面说过驱动开发的重点难点在于看懂框架代码,在上面进行设备的添加和更改,下边就来了解一下这个框架逻辑。
驱动框架设计流程
1.确定主设备号
2.定义结构体类型file_operations
3.实现对应的drv_open/drv_read/drv_write等函数,填入file_operations结构体
4.实现驱动入口:安装驱动程序时,才会去调用这个入口函数,执行工作:
①把file_operations结构体告诉内核:注册驱动程序register_chrdev.
②创建类class_create.
③创建设备device_create.
5.实现出口:卸载驱动程序时,才会去调用这个出口函数,执行工作:
①把file_operations结构体从内核注销:unregister_chrdev.
②销毁类class_create.
③销毁设备结点device_destroy.
6.其他建立:GPL合同、入口加载
1、确定主设备、变量定义
static struct class *pin4_class; static struct device *pin4_class_dev; static dev_t devno;//设备号 static int major = 231; //主设备号 static int minor = 0; //次设备号 static char *module_name = "pin4"; //模块名
2、定义file_operations结构体,加载到内核驱动数组中
这是Linux内核中的file_operations结构体
按照下层调用函数定义结构体成员
static struct file_operations pin4_fops = { .owner = THIS_MODULE, .open= pin4_open, .write = pin4_write, .read= pin4_read, };
3、实现结构体成员pin4_read等函数
static int pin4_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { printk("pin4_readn"); return 0; } //led_open函数 static int pin4_open(struct inode *inode,struct file *file) { printk("pin4_openn");//内核的打印函数和printf类似 return 0; } //led_write函数 static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos) { printk("pin4_writen"); return 0; }
4、驱动入口
int __init pin4_drv_init(void) //真实驱动入口 { int ret; devno = MKDEV(major, minor);//创建设备号 ret = register_chrdev(major, module_name, &pin4_fops);//注册驱动告诉内核,把这个驱动加入到内核驱动的链表中 pin4_class=class_create(THIS_MODULE, "myfirstdemo");//由代码在dev自动生成设备 pin4_class_dev =device_create(pin4_class, NULL, devno, NULL, module_name);//创建设备文件 return 0; }
其中pin4_class=class_create(THIS_MODULE,"myfirstdemo");//由代码在dev手动生成设备,除此之外还可以自动生成设备,在dev目录下sudomknod+设备名子+设备类型(c表示字符设备驱动)+主设备号+次设备号。
5、出口
void __exit pin4_drv_exit(void) { device_destroy(pin4_class, devno); class_destroy(pin4_class); unregister_chrdev(major, module_name);//卸载驱动 }
6、GPI合同,入口加载,出口加载
module_init(pin4_drv_init);//入口,内核加载该驱动(insmod)的时候,这个宏被使用 module_exit(pin4_drv_exit); MODULE_LICENSE("GPL v2");
驱动模块代码编译和测试
编译阶段
驱动模块代码编译(模块的编译须要配置过的内核源码,编译、连接后生成的内核模块后缀为.ko,编译过程首先会到内核源码目录下,读取顶楼的Makefile文件,之后再返回模块源码所在目录。)
#include //file_operations声明 #include //module_initmodule_exit声明 #include //__init__exit 宏定义声明 #include //classdevise声明 #include//copy_from_user 的头文件 #include//设备号dev_t 类型声明 #include //ioremap iounmap的头文件 static struct class *pin4_class; static struct device *pin4_class_dev; static dev_t devno;//设备号 static int major = 231;//主设备号 static int minor = 0;//次设备号 static char *module_name = "pin4"; //模块名 static int pin4_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { printk("pin4_readn"); return 0; } //led_open函数 static int pin4_open(struct inode *inode,struct file *file) { printk("pin4_openn");//内核的打印函数和printf类似 return 0; } //led_write函数 static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos) { printk("pin4_writen"); return 0; } static struct file_operations pin4_fops = { .owner = THIS_MODULE, .open= pin4_open, .write = pin4_write, .read= pin4_read, }; int __init pin4_drv_init(void) //真实驱动入口 { int ret; devno = MKDEV(major, minor);//创建设备号 ret = register_chrdev(major, module_name, &pin4_fops);//注册驱动告诉内 核,把这个驱动加入到内核驱动的链表中 pin4_class=class_create(THIS_MODULE, "myfirstdemo");//用代码 在dev自动生成设备 return 0; } void __exit pin4_drv_exit(void) { device_destroy(pin4_class, devno); class_destroy(pin4_class); unregister_chrdev(major, module_name);//卸载驱动 } module_init(pin4_drv_init);//入口,内核加载该驱动(insmod)的时候,这个宏被使>用 module_exit(pin4_drv_exit); MODULE_LICENSE("GPL v2");
将该驱动代码拷贝到linux-rpi-4.14.y/drivers/char目录下文件中(也可选择设备目录下其它文件)
更改该文件夹下Makefile(驱动代码放在那个目录,就更改该目录下的Makefile),将前面的代码编译生成模块,文件内容如右图所示:(-y表示编译进内核,-m表示生成驱动模块,CONFIG_表示是按照config生成的),所以只须要将obj-m+=pin4drive.o添加到Makefile中即可。
回到linux-rpi-4.14.y/编译驱动文件
使用指令:ARCH=armCROSS_COMPILE=arm-linux-gnueabihf-KERNEL=kernel7makemodules进行编译生成驱动模块。
编译生成驱动模块会生成以下几个文件:
.o的文件是object文件,.ko是kernelobject,与.o的区别在于其多了一些sections,例如.modinfo。.modinfosection是由kernelsource里的modpost工具生成的,包括MODULE_AUTHOR,MODULE_DESCRIPTION,MODULE_LICENSE,deviceIDtable以及模块依赖关系等等。depmod工具按照.modinfosection生成modules.dep,modules.*map等文件,便于modprobe更便捷的加载模块。
编译过程中,经历了这样的步骤:先步入Linux内核所在的目录,并编译出pin4drive.o文件,运行MODPOST会生成临时的pin4drive.mod.c文件linux 驱动 开发,而后依据此文件编译出pin4drive.mod.o,然后联接pin4drive.o和pin4drive.mod.o文件得到模块目标文件pin4drive.ko,最后离开Linux内核所在的目录。
将生成的.ko文件发送给猕猴桃派:[email protected]:/home/pi
将pin4test.c(下层调用代码)进行交叉编译后发送给猕猴桃派,就可以看见pi目录下存在发送过来的.ko文件和pin4test这两个文件,
加载内核驱动
sudo insmod pin4drive.ko
加载内核驱动(相当于通过insmod调用了module_init这个宏,之后将整个结构体加载到驱动数组中)加载完成后就可以在dev下边见到名子为pin4的设备驱动(这个和驱动代码上面staticchar*module_name="pin4";//模块名这行代码有关),设备号也和代码上面相关。
lsmod查看系统的驱动模块,执行下层代码,赋于权限
查看内核复印的信息,
dmesg |grep pin4
如右图所示:表示驱动调用成功
在装完驱动后可以使用指令:sudormmod+驱动名(不须要写ko)将驱动卸载。
调用流程:
我们下层空间的open去查找dev下的驱动(文件名),文件名背后包含了驱动的主设备号和次设备号,此时用户open触发一个系统调用linux是什么系统,系统调用经过vfs(虚拟文件系统),vfs按照文件名背后的设备号去调用sys_open去判定,找到内核中驱动数组的驱动位置,再去调用驱动上面自己的dev_open函数
为何生成驱动模块须要在虚拟机上生成?猕猴桃派不行吗?
生成驱动模块须要编译环境(linux源码而且编译,须要下载和系统版本相同的Linux内核源代码),也可以在猕猴桃派里面编译,但在猕猴桃派里编译,效率会很低,要特别久。
以上是底层硬件设备驱动插入链表的顺序(驱动)的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

3d渲染,电脑配置?1电脑配置对于3D渲染非常重要,需要足够的硬件性能才能保证渲染效果和速度。23D渲染需要大量的计算和图像处理,因此需要高性能的CPU、显卡和内存。3建议配置至少一台搭载至少6核12线程的CPU、16GB以上的内存和一张高性能显卡的电脑,才能满足较高的3D渲染需求。同时,还需要注意电脑的散热和电源等方面的配置,以确保电脑的稳定运行。做设计3D渲染需要配置什么样的电脑?本人也是做设计的,给你一套配置吧(本人再用)CPU:amd960t开6核(或者1090t直接超频)内存:1333

重装系统是很多电脑用户经常遇到的问题,无论是因为系统崩溃、中毒还是想要升级系统,重装系统都是一个不错的选择。但是,很多人在重装系统的时候会遇到各种问题,比如不知道如何设置BIOS、不知道如何选择合适的安装盘等等。今天,我们就来聊聊u盘重装系统必知的一些事项,教你如何正确设置BIOS,顺利完成系统重装。工具原料:系统版本:Windows1020H2品牌型号:联想小新Air14软件版本:口袋装机一键重装系统软件v1.0一、准备工作1、准备一个容量不低于8GB的U盘,最好是USB3.0的,这样速度会更

acer的台式电脑怎么组装?Acer台式电脑的组装方法如下:打开机箱,将电源装入机箱,拧紧螺丝。将CPU安装到主板上,注意CPU上的针脚要与主板上的插槽对齐,拧紧CPU散热器的螺丝。将内存条安装到主板上,注意内存条上的凹凸位要与主板上的插槽对齐,用力下压,听到“啪”的一声后即可完成安装。将显卡、声卡、网卡等板卡安装到主板上,注意板卡上的螺丝要拧紧。将硬盘、光驱等存储设备安装到机箱内,注意螺丝要拧紧。将主板与机箱的连线连接好,包括电源线、硬盘线、光驱线等。最后将机箱盖盖上,拧紧螺丝即可完成组装。在

小米推出的新拍照软件莱卡相机非常的火,但是这款软件在拍照时会出现闪退的现象,不少用户不知道怎么回事,怎么才能解决,下面一起来看看小米莱卡相机闪退解决方法吧。小米莱卡相机闪退怎么办解决方法一:1、首先将手机关机,关机三十秒后再重启手机。2、然后下载一款同类型的相机软件,试试能否正常运作。3、打开手机设置功能-相机程序-清除缓存数据。4、依然没能解决问题,试试备份手机数据,恢复出厂设置。5、如果以上这几点都不是,可能是相机组件硬件问题,需要将手机返厂维修检测。解决方法二:1、备份重要数据,打开安全中

随着电脑使用时间的增长,系统会逐渐积累许多垃圾文件和无用程序,导致运行速度变慢,甚至出现故障。这时,恢复出厂设置就成为一个很好的选择。本文将以联想笔记本电脑为例,介绍怎么一键恢复win11系统的出厂设置,让你的电脑重获新生。工具原料:系统版本:Windows11品牌型号:联想小新Pro162022软件版本:无需额外软件一、准备工作1、备份重要数据:恢复出厂设置会删除C盘中的所有数据,因此在操作前务必将重要文件备份到其他盘符或外部存储设备中。2、确保电脑电量充足:恢复过程可能需要一段时间,建议连接

Windows11是微软最新推出的操作系统,它带来了全新的界面设计和更多实用功能。然而,部分用户在升级或使用过程中遇到了黑屏问题,导致无法正常工作。本文将详细介绍几种有效解决Win11黑屏问题的方法,帮助你快速恢复电脑正常运行。工具原料:系统版本:Windows11(21H2)品牌型号:联想小新Pro162022、戴尔XPS13Plus、华硕ZenBookS13OLED软件版本:NVIDIAGeForceExperience3.26.0.160、IntelDriver&SupportAssist

电脑与电视已成为人们生活中不可或缺的娱乐工具、在数字化时代。如果能够将电脑上的内容无线投射到电视上,播放游戏以及进行演示等活动、我们就能够在更大的屏幕上欣赏电影,想象一下。让你享受更震撼的视听体验、本文将带你了解如何通过简单的步骤实现电脑与电视的无线投屏。1.准备一台支持无线投屏的电视确保你拥有一台支持无线投屏功能的电视。如果你的电视没有,则可以购买一个外接的无线投屏设备、现在市面上大多数智能电视都内置了这一功能,如Chromecast或AppleTV等。2.选择合适的投屏协议接下来,选择合适的

重装系统是一项常见的电脑维护操作,但有时会遇到一些意外情况,比如u盘重装系统后内存变小了。这究竟是怎么回事呢?下面我们就来分析一下原因,并提供一些实用的建议。工具原料:系统版本:Windows1021H2品牌型号:联想ThinkPadX1Carbon软件版本:小鱼一键重装系统V5.0一、重装系统后内存变小的原因1、安装了过多的驱动程序和软件。有些用户在重装系统后,会安装大量不必要的驱动程序和软件,占用了内存空间。2、系统盘分区方案不合理。如果在重装系统时没有合理分配系统盘的空间,可能会导致内存不
