块设备驱动编写总结

大华摄像头GB28181协议接入EasyCVR全流程(附常见错误排查) 本文详细阐述了大华摄像头通过GB28181协议接入EasyCVR视频管理平台的全流程,涵盖从核心原理、前期参数规划、Web界面配置到平台侧验证的完整步骤。重点针对网络端口、SIP参数配置等关键环节提供了深度排错指南,并总结了设备无法注册、在线不稳定、拉流失败等常见问题的排查思路与解决方案,旨在帮助安防工程师与系统集成商高效完成设备接入与调试。 阅读详情

块设备与字符设备的区别

1、 从字面上理解,块设备和字符设备最大的区别在于读写数据的基本单元不同。块设备读写数据的基本单元为块,例如磁盘通常为一个sector,而字符设备的基本单元为字节。所以Linux中块设备驱动往往为磁盘设备的驱动,但是由于磁盘设备的IO性能与CPU相比很差,因此,块设备的数据流往往会引入文件系统的Cache机制。

2、 从实现角度来看,Linux为块设备和字符设备提供了两套机制。字符设备实现的比较简单,内核例程和用户态API一一对应,用户层的Read函数直接对应了内核中的Read例程,这种映射关系由字符设备的file_operations维护。块设备接口相对于字符设备复杂,read、write API没有直接到块设备层,而是直接到文件系统层,然后再由文件系统层发起读写请求。

块设备读写流程

在学习块设备原理的时候,我最关系块设备的数据流程,从应用程序调用Read或者Write开始,数据在内核中到底是如何流通、处理的呢?然后又如何抵达具体的物理设备的呢?下面对一个带Cache功能的块设备数据流程进行分析。

1、 用户态程序通过open()打开指定的块设备,通过systemcall机制陷入内核,执行blkdev_open()函数,该函数注册到文件系统方法(file_operations)中的open上。在blkdev_open函数中调用bd_acquire()函数,bd_acquire函数完成文件系统inode到块设备bdev的转换,具体的转换方法通过hash查找实现。得到具体块设备的bdev之后,调用do_open()函数完成设备打开的操作。在do_open函数中会调用到块设备驱动注册的open方法,具体调用如下:gendisk->fops->open(bdev->bd_inode, file)。

2、 用户程序通过read、write函数对设备进行读写,文件系统会调用相应的方法,通常会调用如下两个函数:generic_file_read和blkdev_file_write。在读写过程中采用了多种策略,首先分析读过程。

3、 用户态调用了read函数,内核执行generic_file_read,如果不是direct io方式,那么直接调用do_generic_file_read->do_generic_mapping_read()函数,在do_generic_mapping_read(函数位于filemap.c)函数中,首先查找数据是否命中Cache,如果命中,那么直接将数据返回给用户态;否则通过address_space->a_ops->readpage函数发起一个真实的读请求。在readpage函数中,构造一个buffer_head,设置bh回调函数end_buffer_async_read,然后调用submit_bh发起请求。在submit_bh函数中,根据buffer_head构造bio,设置bio的回调函数end_bio_bh_io_sync,最后通过submit_bio将bio请求发送给指定的快设备。

4、 如果用户态调用了一个write函数,内核执行blkdev_file_write函数,如果不是direct io操作方式,那么执行buffered write操作过程,直接调用generic_file_buffered_write函数。Buffered write操作方法会将数据直接写入Cache,并进行Cache的替换操作,在替换操作过程中需要对实际的快设备进行操作,address_space->a_ops提供了块设备操作的方法。当数据被写入到Cache之后,write函数就可以返回了,后继异步写入的任务绝大部分交给了pdflush daemon(有一部分在替换的时候做了)

5、 数据流操作到这一步,我们已经很清楚用户的数据是如何到内核了。与用户最接近的方法是file_operations,每种设备类型都定义了这一方法(由于Linux将所有设备都看成是文件,所以为每类设备都定义了文件操作方法,例如,字符设备的操作方法为def_chr_fops,块设备为def_blk_fops,网络设备为bad_sock_fops)。每种设备类型底层操作方法是不一样的,但是通过file_operations方法将设备类型的差异化屏蔽了,这就是Linux能够将所有设备都理解为文件的缘由。到这里,又提出一个问题:既然这样,那设备的差异化又该如何体现呢?在文件系统层定义了文件系统访问设备的方法,该方法就是address_space_operations,文件系统通过该方法可以访问具体的设备。对于字符设备而言,没有实现address_space_operations方法,也没有必要,因为字符设备的接口与文件系统的接口是一样的,在字符设备open操作的过程中,将inode所指向的file_operations替换成cdev所指向的file_operations就可以了。这样用户层读写字符设备可以直接调用cdev中file_operations方法了。

6、 截至到步骤(4),读操作在没有命中Cache的情况下通过address_space_operations方法中的readpage函数发起块设备读请求;写操作在替换Cache或者Pdflush唤醒时发起块设备请求。发起块设备请求的过程都一样,首先根据需求构建bio结构,bio结构中包含了读写地址、长度、目的设备、回调函数等信息。构造完bio之后,通过简单的submit_bio函数将请求转发给具体的块设备。从这里可以看出,块设备接口很简单,接口方法为submit_bio(更底层函数为generic_make_request),数据结构为struct bio。

7、 submit_bio函数通过generic_make_request转发bio,generic_make_request是一个循环,其通过每个块设备下注册的q->make_request_fn函数与块设备进行交互。如果访问的块设备是一个有queue的设备,那么会将系统的__make_request函数注册到q->make_request_fn中;否则块设备会注册一个私有的方法。在私有的方法中,由于不存在queue队列,所以不会处理具体的请求,而是通过修改bio中的方法实现bio的转发,在私有make_request方法中,往往会返回1,告诉generic_make_request继续转发比bio。Generic_make_request的执行上下文可能有两种,一种是用户上下文,另一种为pdflush所在的内核线程上下文。

8、 通过generic_make_request的不断转发,最后请求一定会到一个存在queue队列的块设备上,假设最终的那个块设备是某个scsi disk(/dev/sda)。generic_make_request将请求转发给sda时,调用__make_request,该函数是Linux提供的块设备请求处理函数。在该函数中实现了极其重要的操作,通常所说的IO Schedule就在该函数中实现。在该函数中试图将转发过来的bio merge到一个已经存在的request中,如果可以合并,那么将新的bio请求挂载到一个已经存在request中。如果不能合并,那么分配一个新的request,然后将bio添加到其中。这一切搞定之后,说明通过generic_make_request转发的bio已经抵达了内核的一个站点——request,找到了一个临时归宿。此时,还没有真正启动物理设备的操作。在__make_request退出之前,会判断一个bio中的sync标记,如果该标记有效,说明请求的bio是一个是实时性很强的操作,不能在内核中停留,因此调用了__generic_unplug_device函数,该函数将触发下一阶段的操作;如果该标记无效的话,那么该请求就需要在queue队列中停留一段时间,等到queue队列触发闹钟响了之后,再触发下一阶段的操作。__make_request函数返回0,告诉generic_make_request无需再转发bio了,bio转发结束。

9、 到目前为止,文件系统(pdflush或者address_space_operations)发下来的bio已经merge到request queue中,如果为sync bio,那么直接调用__generic_unplug_device,否则需要在unplug timer的软中断上下文中执行q->unplug_fn。后继request的处理方法应该和具体的物理设备相关,但是在标准的块设备上如何体现不同物理设备的差异性呢?这种差异性就体现在queue队列的方法上,不同的物理设备,queue队列的方法是不一样的。举例中的sda是一个scsi设备,在scsi middle level将scsi_request_fn函数注册到了queue队列的request_fn方法上。在q->unplug_fn(具体方法为:generic_unplug_device)函数中会调用request队列的具体处理函数q->request_fn。Ok,到这一步实际上已经将块设备层与scsi总线驱动层联系在了一起,他们的接口方法为request_fn(具体函数为scsi_request_fn)。

10、明白了第(9)点之后,接下来的过程实际上和具体的scsi总线操作相关了。在scsi_request_fn函数中会扫描request队列,通过elv_next_request函数从队列中获取一个request。在elv_next_request函数中通过scsi总线层注册的q->prep_rq_fn(scsi层注册为scsi_prep_fn)函数将具体的request转换成scsi驱动所能认识的scsi command。获取一个request之后,scsi_request_fn函数直接调用scsi_dispatch_cmd函数将scsi command发送给一个具体的scsi host。到这一步,有一个问题:scsi command具体转发给那个scsi host呢?秘密就在于q->queuedata中,在为sda设备分配queue队列时,已经指定了sda块设备与底层的scsi设备(scsi device)之间的关系,他们的关系是通过request queue维护的。

11、 在scsi_dispatch_cmd函数中,通过scsi host的接口方法queuecommand将scsi command发送给scsi host。通常scsi host的queuecommand方法会将接收到的scsi command挂到自己维护的队列中,然后再启动DMA过程将scsi command中的数据发送给具体的磁盘。DMA完毕之后,DMA控制器中断CPU,告诉CPU DMA过程结束,并且在中断上下文中设置DMA结束的中断下半部。DMA中断服务程序返回之后触发软中断,执行SCSI中断下半部。

12、     在SCSi中断下半部中,调用scsi command结束的回调函数,这个函数往往为scsi_done,在scsi_done函数调用blk_complete_request函数结束请求request,每个请求维护了一个bio链,所以在结束请求过程中回调每个请求中的bio回调函数,结束具体的bio。Bio又有文件系统的buffer head生成,所以在结束bio时,回调buffer_head的回调处理函数bio->bi_end_io(注册为end_bio_bh_io_sync)。自此,由中断引发的一系列回调过程结束,总结一下回调过程如下:scsi_done->end_request->end_bio->end_bufferhead。

13、  回调结束之后,文件系统引发的读写操作过程结束。

Dify 系列教程 | 第 6 篇:工作流入门 -- 用可视化方式编排 AI 逻辑 Dify 工作流是一个基于有向无环图(DAG)的可视化 AI 逻辑编排系统。你可以把多个处理步骤(节点)用连线串联起来,数据从起点流向终点,每经过一个节点就被处理一次。用一个类比来理解:如果单次 Prompt 调用是"一道菜",那工作流就是"一道完整的生产线" – 原材料(输入)进入流水线,经过清洗(预处理)、切配(数据转换)、烹饪(AI 推理)、装盘(格式化输出)等工序,最终产出成品。创建一个工作流类型的应用(或高级聊天类型),就会进入工作流编辑器。编辑器基于ReactFlow构建( 阅读详情

相关推荐

Groq LPU架构解析:确定性线性处理器如何重塑AI推理

语言模型推理正从‘通用算力堆叠’转向‘确定性硬件定制’。LPU(Language Processing Unit)作为一种面向大模型推理的领域专用处理器,摒弃传统GPU的并行调度、多级缓存与动态执行范式,转而采用空间计算、静态编译与单线程线性流水线设计,实现纳秒级延迟可控性和92%+的HBM带宽利用率。其核心价值在于将模型结构、内存访问、时序路径全部在编译期固化,从而消除运行时抖动——这正是当前MLOps工程师应对A100显存碎片、vLLM调度开销与P99延迟不稳等痛点的关键技术路径。Groq 3 LPU与

weixin_30599521的博客 325

Linux块设备总结(二)

上一篇博客中《Linux内核块设备总结(一)》[link][https://blog.youkuaiyun.com/weixin_37867857/article/details/88316757]介绍了从qemu中启动增加块设备的方法。最后一步gdb调试中提及了blkdev_open函数的系统调用,但是只是提及了系统调用,并没有深度的去解析这个函数。只是我们提及在执行cat /dev/sda操作之后gdb调...

weixin_37867857的博客 3340

【Scrapy】Scrapy教程4——命令行工具

Scrapy支持使用模块设置自己的项目命令。如何实现可参考的实践方法。然后在项目的settings文件中配置来添加自定义命令。以上就是对Scrapy命令行工具的说明,有不明确的地方,可参考官方文档欢迎大家在各大平台关注我,我会不定期发布技术相关文章和各种小工具,并且欢迎共同技术探讨!

jayhgq的博客 1250

Linux内核——块设备驱动(一)

一个 gendisk 是一个被引用计数的结构(它含有一个 kobject). 有 get_disk 和 put_disk 函数用来操作引用计数, 但是驱动应当从不需要做这个. 正常地, 对 del_gendisk 的调用去掉了最一个 gendisk 的最终的引用, 但是不保证这样. 因此, 这个结构可能继续存在(并且你的方法可能被调用)在调用 del_gendisk 之后. 但是, 如果你删除这个结构当没有用户时(即, 在最后的释放之后, 或者在你的模块清理函数), 你可确信你不会再收到它的信息.

m0_74282605的博客 536

docker 安装oracle

本文介绍了使用Docker部署Oracle数据库的实践指南。主要内容包括:常用Oracle镜像的阿里云和华为云拉取地址,11g/12c/19c等不同版本的启动命令示例,以及连接数据库的基本信息。实战部分详细演示了Oracle 11g的安装过程,包括镜像拉取、容器启动、密码修改和数据库连接步骤,并通过Sql Developer和命令行两种方式创建用户。最后提到数据持久化的重要性,建议将数据库文件挂载到宿主机目录以防止容器删除导致数据丢失。文章提供了完整的操作命令和截图说明,适合需要快速部署Oracle测试环境

weixin_39766667的博客 373

阿里巴巴-AI Agent工程师面试题精选:10道高频考题+答案解析(附PDF)

阿里巴巴AIAgent技术面试要点摘要(149字) 阿里巴巴在AIAgent领域布局完整,面试重点考察系统架构、幻觉治理、成本优化等核心能力。候选人需掌握分层架构设计、多Agent协作死锁处理、Token成本优化策略等关键技术。面试强调工程落地能力,包括工具调用容错、记忆系统设计、安全防护等实践要点。评估体系需覆盖效果、效率、成本多维度指标。未来趋势关注垂直领域专业化、多模态融合等方向。建议准备实际项目案例,突出技术深度与业务价值的结合,展现系统思维和工程能力。

ZHHHHH15的博客 615

【驱动】块设备驱动(四)-块设备驱动层

接着,通过调用通用的文件写入函数进行实际的写入操作,并返回写入操作的结果。例如,如果读取出现错误(retval小于0),或者已经读取完所有数据(iov_iter_count(iter)为0),或者已经读取到文件末尾(*ppos >= size),或者文件是DAX文件(IS_DAX(inode)),则跳过剩余的读取操作,并更新文件的访问时间。:调用通用的执行写入操作的函数generic_perform_write,执行标准缓冲区写入操作,并将写入的起始位置保存到pos中,返回写入的状态码。

嵌入式与Linux那些事的博客 1567

实验记录 | 如何拆分scATAC-seq数据成为单个细胞?(1、解决barcode问题)

在师兄的帮助下,我得到了师兄筛选过的细胞的barcode的数据,那么接下来我想根据这些barcode,将来源于同一个barcode的细胞的reads提取出来,这个还是一个蛮有技术含量的事情的,因为上次我有使用过perl的脚本进行提取,最后的实验结果很糟糕,故而这件事情一直搁置到现在,没有继续去做(我的脚本的编写能力还是有待于加强,可能是刚刚喝了奶茶,和老妈弟弟聊了好多闲话,很开心,觉得在某种程度上休息够了,对于工作也是一种非常快乐和积极的状态了)。 0。前期失败尝试回顾 ...

weixin_40640700的博客 1887

7.块设备驱动的I/O请求处理(1)

1.使用请求队列 块设备驱动在使用请求队列的场景下,会用blk_init_queue()初始化reqyest_queue,而函数的第一个参数就是请求处理函数的指针。request_queue会作为参数传递给我们在调用blk_init_queue()时指定的请求处理函数,块设备驱动请求处理函数的原型为: static void xxx_req(struct request_queue *q); 这个函数不能由驱动自己调用,只有当内核认为是时候让驱动处理对设备的读写等操作时,他才调用这个函数。该函数的主要工作

weixin_42097108的博客 731

【图像拼接】论文精读:As-Rigid-As-Possible Shape Manipulation(ARAP)

文章目录前言1.摘要2.算法的总体流程总结 前言 图像变形算法 1.摘要 论文提出了一个二维图形互动系统。图形是建立在三角形网格上的,用户只需移动几个网格顶点(限制顶点),系统通过最小化每个三角形的误差计算网格剩余顶点(自由顶点)的位置。 算法分两步: 适当旋转每个三角形。 调整大小。 关键是使用二次度量误差将最小化问题转换成线性方程组,然后再解这个方程组。 刚性形状变形是很难的,论文的研究使其变得简单。 2.算法的总体流程 总的流程分三步: 将图形用三角形网格覆盖 选择三个点(限制顶点) 操纵限

主要更新底层视觉(去噪、超分等)相关的科研内容,形式为【论文精读】+【论文复现】 15万+

Android编译系统——Android.bp语法介绍(十五)

Android.bp 是 Android 构建系统(Android Build System)中的配置文件,用于描述如何编译和打包项目中的模块。它是用 Blueprint 编写的,其语法类似于 JSON,但专为 Android 编译系统设计,提供了更高效、灵活的模块化管理。

小旭的专栏 939

编写块设备驱动程序

写一个块设备内核驱动模块,利用ramdisk,即利用系统内存来构造一个虚拟的块设备,在这个块设备上实现文件系统的格式化和一些文件操作。(广州大学Linux操作系统实验四第二部分Linux内核学习11——编写块设备驱动程序(下)_编写my_block块设备驱动程序_Backlight~~的博客-优快云博客参考的是上的,代码也是上面大佬的提前说明,这次使用的Liunx是14.04的,运行起来就不会出现错误,新版本可能很多路径函数都发生了变化,如果需要,要重新进行编写。

jojojojoi的博客 524

【推荐100个unity插件】基于关节物理控制的自行车、电动车、摩托车模拟解决方案——Simple Bicycle Physics

摘要 "简易自行车物理"是Unity的自行车模拟资源包,支持Animation Rigging,实现基于关节物理的自行车控制。兼容所有平台和渲染管线,包含预制体、材质、脚本等资源。核心脚本Bicycle Controller管理自行车物理属性,支持自定义角色设置和移动端控制。安装后,用户可快速配置骑行场景,调整转向、速度、重心等参数,并支持地面贴合、兔跳等功能。适用于开发各类自行车模拟游戏和应用。 (字数:149)

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 1366

超长文解析Linux块设备驱动编写方法

块设备的初始化过程要比字符设备复杂,它既需要像字符设备一样在加载内核时完成一定的工作,还需要在内核 编译时增加一些内容。块设备驱动程序初始化时,由驱动程序的 init()完成。 块设备的初始化过程:初始化并注册 gendisk 结构struct gendisk (定义于 ) 在内核中代表一个单独的磁盘驱动器。事实上, 内核还使用 gendisk 来表示分区,所以物理上的一块磁盘,可以在内核中由多个逻辑上的磁盘(gendisk)来表示。每个 gendisk 中包含了本物理实体的全

Leon的博客 3537

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

块设备驱动编写

<br />块设备驱动程序描述符<br />块设备通常指一些需要以块(如512字节)的方式写入的设备。<br /> <br />块设备驱动程序描述符是一个包含在<linux/blkdev.h>中的blk_dev_struct<br />struct blk_dev_struct { request_queue_t request_queue; /* 包含了初始化之后的I/O请求队列 */ queue_proc *queue; void *data; }; <br />所有块设

dadaguo3000的专栏 721

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

编写并分析简单的块设备驱动程序

块设备也和字符设备一样可以通过/dev目录下的设备文件来访问,而网络设备不可以。此外块设备(例如磁盘)上能够容纳文件系统。

qq_58538265的博客 645

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

6.块设备驱动程序--二:块设备驱动编写

如何写块设备驱动程序 1., 使用register_blkdev()创建一个块设备 2,以面向对象的思想分配 gendisk 结构体。用 alloc_disk 函数。 3,设置 gendisk 结构体。 ①,分配/设置一个队列:request_queue_t. (提供读写能力)用 blk_init_queue 函 数。 ②,设置 gendisk 其他信息。(提供磁盘属性:磁盘容量,扇区大小等) 4...

weixin_42918587的博客 632

块设备驱动分析及编写

块设备驱动分析及编写

ygy767的专栏 608

一文彻底搞清Linux中块设备驱动的深层次原理和编写方法

本文主要讲述了在Linux环境下的块设备驱动的常见数据结构和内核接口,并以一个实际例子讲述了块设备驱动的编写方法。文章参考LDD3。

Leon的博客 2799

linux块设备驱动在哪里,Linux 块设备驱动代码编写

按照ldd的说法,linux的设备驱动包括了char,block,net三种设备。char设备是比较简单的,只要分配了major、minor号,就可以进行读写处理了。相对而言,block和net要稍微复杂些。net设备姑且按下不谈,我们在以后的博文中会有涉及。今天,我们可以看看一个简单的block是怎么设计的。为了将block和fs分开,kernel的设计者定义了request queue这一种形...

weixin_29315569的博客 188

React 360 分层包架构全解析:从 webgl-lite 到 react-360 的 monorepo 设计

React 360 并非一个单一的大库,而是由一系列职责单一的 npm 包逐层叠加而成的"360 沉浸式环境"。本文以仓库 [packages/README.md](https://link.gitcode.com/i/a3a51c12260f03dc54883194736cf438) 为核心骨架,深入拆解这套分层架构:底层 `webgl-lite` 负责原始 WebGL 资源管理,`webgl-

gitblog_00048的博客 312

10块设备驱动程序的编写

块设备文件通常指一些需要以块(如512字节)的方式写入的设备,如IDE硬盘、SCSI硬盘、光驱等。它的驱动程序的编写过程与字符型设备驱动程序的编写有很大的区别。 为了把各种块设备的操作请求队列有效地组织起来,内核中设置了一个结构数组blk_dev,该数组中的元素类型是blk_dev_struct结构。这个 结构由三个成分组成,其主体是执行操作的请求队列request_queue,还有一个函数

u010291330的博客 1077

一个极简单的 块设备驱动 的编写

目标平台:loongson 1B 目标系统:1b-linux-3.0 目标开发板架构:mipsel架构 实现平台:ubuntu 13.04  #include #include #include #include #include #include #include #include #include #include #include

aba13579的专栏 1606

驱动专题:第六章块设备驱动 4.块设备驱动编写

通过上节的块设备驱动分析,本节便通过内存来模拟块设备驱动 ,方便我们更加熟悉块设备驱动框架 参考内核自带的块设备驱动程序:drivers/block /xd.c  drivers/block /z2ram.c   1.本节需要的结构体如下:1.1 gendisk磁盘结构体: struct gendisk { int major; //设备主设备号...

中年润的博客 227

编写块设备驱动之内存模拟磁盘

可以参考一下内核里面的文件 在内核里面收索blk_init_queue 然后就会发现xd.c和Z2ram.c 大概看一眼,看一个去驱动程序从入口开始看, 这里有个register_blkdev注册块设备驱动,跟我们字符设备相比少了一个fops,注册块设备已经退化了,它只不过是cat /prok/device 可以看到一些信息。或者前面XT_DISK_MAJOR写为0的时候可以返回一个主设备

fight_onlyfor_you的博客 678
上一篇: RamDisk块设备驱动实例开发讲解
下一篇: android 耳机监测驱动
xp_super
博客等级 码龄14年 10粉丝 17原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值