linux内核篇-输入输出系统(块设备,字符设备)

news2024/11/19 2:19:23

在这里插入图片描述
计算机系统的输入和输出系统都有哪些呢?有键盘、鼠标、显示器、网卡、硬盘、打印机、CD/DVD 等等,多种多样。但是对于操作系统来讲,却是一件复杂的事情,因为这么多设备,形状、用法、功能都不一样,怎么才能统一管理起来呢?

可以体会到操作系统设计的思想,和网络差不多,都是分层,每一层做自己的事并且屏蔽差异。

用设备控制器屏蔽差异
计算机系统也是这样的,CPU并不直接和设备打交道,它们中间有一个叫做设备控制器(device control unit)的组件。
控制器其实有点儿像一台小电脑。它有它的芯片,类似小CPU,执行自己的逻辑。它也有自己的寄存器。CPU对于寄存器的读写,比直接控制硬件,要标准和轻松很多。这就相当于和代理商的标准产品交付

输入输出设备大致可以分为两类:块设备(Block Device)和字符设备(Character Device)。
块设备将信息存储在固定大小的块中,每个块都有自己的地址。硬盘就是常见的块设备
字符设备发送或者接收的是字节流,而不用考虑任何块结构,没有办法寻址。鼠标就是常见的字符设备
由于块设备传输的数据量比较大,控制器里面往往会有缓冲区。CPU写入缓冲区的数据攒够一部分,才会发给设备。CPU读取的数据,也需要在缓冲区攒够一部分,才拷贝到内存。

CPU如何同控制器的寄存器和数据缓冲区进行通信呢
每个控制寄存器被分配一个IO端口,我们可以通过特殊的汇编指令(比如in/out类似的指令)操作这些寄存器;
数据缓冲区,可内存映射IO,可以分配一段内存空间给它,就像读写内存一样读写数据缓冲区。内存空间的区域ioremap就是干这个的;

当你给设备发了一个指令,让它读取一些数据,它读完的时候,怎么通知你呢?
当设备完成任务后发中断到中断控制器,中断控制器就通知CPU,一个中断就产生了,CPU需要停下当前手中的事情来处理中断。
一种是软中断,比如代码调用INT指令触发(比如系统调用,溢出等);一种是硬件中断,就是硬件通过中断控制器触发(IO操作完成)

DMA
有的设备需要读取或者写入大量数据。如果所有过程都让CPU协调的话,就需要占用CPU大量时间。CPU只需要对DMA控制器下指令,说它想读取多少数据,放在内存的某个地方就可以了

用驱动程序屏蔽设备控制器差异
由于每种设备的控制器的寄存器、缓冲区等使用模式、指令都不同。这里需要注意的是,设备控制器不属于操作系统的一部分,但是设备驱动程序属于操作系统的一部分;不同的设备驱动程序,可以用同样的方式接入操作系统,而操作系统的其他部分的代码,也可以无视不同设备的区别,以同样的接口调用设备驱动程序。设备驱动程序初始化的时候,要先注册一个该设备的中断处理函数。当有中断到达时,就是进程切换的时机。中断的时候,触发的函数是do_IRQ,这个函数是中断处理的统一入口。

总结:
可以看到,从设备控制器屏蔽设备的差异,设备驱动程序屏蔽设备的设备控制器的差异。驱动会注册中断处理函数,中断分为软终端和硬中断。中断过程就是CPU收到中断信号,保存上下文,进入do_IRQ,执行中断处理程序,恢复上下文,继续执行。 为了减少CPU对于中断的频繁响应,DMA可以实现自动化。

用文件系统接口屏蔽驱动程序的差异保证用户使用的接口统一

首先要统一的是设备名称。所有设备都在 /dev/ 文件夹下面创建一个特殊的设备文件。这个设备特殊文件也有 inode,但是它不关联到硬盘或任何其他存储介质上的数据,而是建立了与某个设备驱动程序的连接。

对于设备文件,ls 出来的内容和我们原来讲过的稍有不同。首先是第一位字符。如果是字符设备文件,则以 c 开头,如果是块设备文件,则以 b 开头。其次是这里面的两个号,一个是主设备号,一个是次设备号。用来定位设备驱动程序。

有了设备文件,我们就可以使用对于文件的操作命令和API来操作文件了。
如果linux操作系统新添加了一个设备,应该做哪些事情呢?
在linux上面,如果一个新的设备从来没有加载过驱动,也需要安装驱动。在linux里面,安装驱动程序,其实就是加载一个内核模块
我们可以用lsmod,查看有没有加载过相应的内核模块:
如果没有安装过相应的驱动,可以通过 insmod 安装内核模块。内核模块的后缀一般是 ko

一旦有了驱动,我们就可以通过命令 mknod 在 /dev 文件夹下面创建设备文件,就像下面这样:
mknod filename type major minor
其中 filename 就是 /dev 下面的设备名称,type 就是 c 为字符设备,b 为块设备,major 就是主设备号,minor 就是次设备号。一旦执行了这个命令,新创建的设备文件就和上面加载过的驱动关联起来,这个时候就可以操作设备文件来操作驱动程序,从而操作设备

(安装驱动,创建设备文件)

通过设备文件,建立和某个设备驱动程序的连接,从而控制设备。

字符设备输入的比如鼠标键盘,输出的比如打印机

设备驱动程序是一个内核模块。怎么构建一个内核模块呢?
第一部分,头文件部分。一般的内核模块,都需要include下面两个头文件
#include <linux/module.h>
#include <linux/init.h>
第二部分,定义一些函数,用于
处理内核模块的主要逻辑
。比如打开、关闭、读取、写入设备的函数或者响应中断的函数。
第三部分,定义一个file_operations函数。设备是可以通过文件系统的接口进行访问的。对于某种文件系统的操作,都是放在file_operations里面的。设备要想被文件系统的接口操作,也需要定义一个这样的结构
第四部分,定义整个模块的初始化函数和退出函数,用于加载和卸载这个ko的时候调用

打开字符设备
字符设备可不是一个普通的内核模块,它有自己独特的行为。
1.首先要加载驱动(内核模块)这个时候,先调用的就是module_init调用的初始化函数。最重要的一件事情就是,注册这个字符设备。注册的方式是调用__register_chrdev_region,注册字符设备的主次设备号和名称,cdev_add会将这个字符设备添加到内核中一个叫做struct kobj_map *cdev_map的结构,来统一管理所有字符设备。

2、内核模块加载完毕后,接下来要通过 mknod 在 /dev 下面创建一个设备文件,只有有了这个设备文件,我们才能通过文件系统的接口,对这个设备文件进行操作。mknod 也是一个系统调用。在文件系统上,顺着路径找到/dev/xxx所在的文件夹,然后为这个新创建的设备文件创建一个dentry。这是维护文件和inode之间的关联关系的结构。

3、打开字符设备。打开文件的进程的task_struct里,有一个数组代表它打开的文件,下标就是文件描述符fd,每一个打开的文件都有一个struct file结构,会指向一个dentry项。dentry可以用来关联inode。这个dentry就是上面mknod时创建的。在进程里面调用open函数,最终对调用到这个特殊的inode的open函数,也就是chrdev_open
(打开文件的操作是通用的。首先一个文件描述符,和文件结构绑定,文件结构里有一个dentry项,它会指向inode,找到文件名和inode映射。没有文件就新建,新创建文件就是调用 dir_inode,从位图中找一个空闲的分配)

写入设备驱动
先调用copy_from_user将数据从用户态拷贝到内核态的缓存中,然后调用parport_write 写入外部表设备。这里还有一个schedule 函数,也就是写入的过程中,给其他的线程抢占CPU的机会。然后,如果count还是大于0,也就是数据还没有写完,就接着 copy_from_user,接着 parport_write,直到写完为止。(跟写缓存差不多,只不过写缓存需要日志相关、映射到内核虚拟地址,脏页标记,回写等;而写设备就是先写到内核缓存,再写到设备中)

对于IO设备来讲,除了读写设备,还会调用ioctl,做一些特殊的IO操作。ioctl 也是一个系统调用。参数会传一个命令是一个int整数
它由几部分组成:
最低八位为 NR,是命令号;
然后八位是 TYPE,是类型;
然后十四位是参数的大小;
最高两位是 DIR,是方向,表示写入、读出,还是读写。
由于组成比较复杂,有一些宏是专门用于组成这个 cmd 值的。

中断处理机制
鼠标就是通过中断,把自己的位置和案件信息,传递给设备驱动程序。
其实中断的处理过程也是屏蔽差异的。
中断首先要保存现场;比如系统调用就保存在内核栈的ptg结构体中。外部中断也就是进入驱动程序的中断处理函数,也是要进入内核态,保存到ptg结构体中。
1、中断是从外部设备发起的,会形成外部中断。外部中断会到达中断控制器,中断控制器会发送中断向量Interrupt Vector给CPU

2、对于每一个CPU,都要求有一个idt_table,里面存放了不同的中断向量的处理函数。中断向量表已经填好了前32位,外加一个32位系统调用,其他都是用于设备中断。(包括系统调用和异常,是必须要处理的,在系统初始化trap_init的时候就已经写好了)

3、因为每个CPU的中断向量是局部的,需要转换成全局的。硬件中断的处理函数是 do_IRQ 进行统一处理,在这里会让中断向量,通过 vector_irq 映射为 irq_desc(irq_desc 是一个用于描述用户注册的中断处理函数的结构,为了能够根据中断向量得到 irq_desc 结构,会把这些结构放在一个基数树里面,方便查找。)

4 irq_desc 里面有一个成员是 irqaction,指向设备驱动程序里面注册的中断处理函数。

处理完成后要恢复现场。

硬中断:保存现场-转换成中断向量-转换成全局的中断向量-找到设备驱动程序里面注册的中断处理函数-恢复现场;
如果是软中断:保存现场-直接进入trap_inti中断门指定的处理函数-恢复现场。

块设备
1、注册管理。这点和字符设备差不多。都需要加载驱动,mkond注册设备文件;
2、mount挂载。devtmpfs文件系统转换为ext4文件系统;
3、在将一个硬盘的块设备 mount 成为 ext4 的时候,我们会调用 ext4_mount->mount_bdev。这点是有区别的。转变成了bdev 伪文件系统。
有点儿绕,我们再捋一下。设备文件 /dev/xxx 在 devtmpfs 文件系统中,找到 devtmpfs 文件系统中的 inode,里面有 dev_t。我们可以通过 dev_t,在伪文件系统 bdev 中找到对应的 inode,然后根据 struct bdev_inode 找到关联的 block_device

4,在找到 block_device 之后,要调用 blkdev_get 打开这个设备。blkdev_get 会调用 __blkdev_get。
也就是说文件会有file_struct,而块设备就有*block_device。这个结构和其他几个结构有着千丝万缕的联系,比较复杂。这是因为块设备本身就比较复杂。(比如一个硬盘不同区可以分成不同的文件系统)
通过 gendisk 里面的 block_device_operations 打开设备。

struct gendisk 是用来描述整个设备的(包括主设备号,分区设备号和数目以及分区对应的操作比如文件打开)

读写块设备
如何将块设备 I/O 请求送达到外部设备。 这里涉及到了通用块设备层BIO
之前说了IO有直接和缓存的。
我们先来看第一种情况,直接 I/O 调用到 ext4_direct_IO。do_direct_IO 里面有两层循环,第一层循环是依次处理这次要写入的所有块。对于每一块,取出对应的内存中的页 page,在这一块中,有写入的起始地址 from 和终止地址 to,所以,第二层循环就是依次处理 from 到 to 的数据,调用 submit_page_section,提交到块设备层进行写入 submit_bio 。
(通过inode的特殊类型,就可以知道是普通的文件直接写还是块设备直接写了)

缓存IO读写
无论是哪种 I/O,最终都会调用 submit_bio 提交块设备 I/O 请求。
在这里插入图片描述

对于每一种块设备,都有一个 gendisk 表示这个设备,它有一个请求队列,这个队列是一系列的 request 对象。每个 request 对象里面包含多个 BIO 对象(通用块设备层),指向 page cache。所谓的写入块设备,I/O 就是将 page cache 里面的数据写入硬盘。
对于请求队列来讲,还有两个函数,一个函数叫 make_request_fn 函数,用于将请求放入队列。submit_bio 会调用 generic_make_request,然后调用这个函数。
另一个函数往往在设备驱动程序里实现,我们叫 request_fn 函数,它用于从队列里面取出请求来,写入外部设备。

总结一下文件系统,设备的读写区别之处
打开的区别
首先,文件的打开open,是有个task_struct结构,里面保存了fd,以及文件结构体记录文件信息的。文件结构里有一个dentry项,它会指向inode,找到文件名和inode映射(其中还有cache哈希来快速查找,lru动态淘汰)。没有文件就新建,新创建文件就是调用 dir_inode,从位图中找一个空闲的分配。

而字符设备的打开需要先注册mknod,挂载它,它会形成ext4文件系统的设备文件,其中有dentry实现名字到inode的映射。打开字符设备和打开文件一样的,用了他自己的open函数。只不过字符设备inode里面记录的是设备驱动程序的连接。目录也是文件,打开一样的。

块设备的打开就复杂一点,因为中间还涉及到一个伪文件系统。设备文件 /dev/xxx 在 devtmpfs 文件系统中,找到 devtmpfs 文件系统中的 inode,里面有 dev_t。我们可以通过 dev_t,在伪文件系统 bdev 中找到对应的 inode,然后根据 struct bdev_inode 找到关联的 block_device。块设备结构体是比较复杂的,因为它本身就复杂(比如一个硬盘不同分区可以是不同的文件系统)所以有一个gendisk来描述整个设备的(包括主设备号,分区设备号和数目以及分区对应的操作比如文件打开),通过操作gendisk的open操作来打开块设备。()

读写的区别
共同之处都有缓存IO和直接IO。主要讨论缓存IO
读会简单一点。对于读文件来说,如果是直接IO,就跨过了缓存层,直接到了文件系统的设备驱动层。由于文件系统是块文件,所以这个调用的是blockdev相关的函数。如果是缓存IO,就会先在页缓存读,没有再放到缓存,并把内核缓存页拷贝到用户内存空间;
文件写,一个循环写所有页,循环里面先日志相关,再内核态映射,再标记脏页完成,回写;而字符设备写就是先写写到内核态映射,再写到设备中;
块设备写麻烦一点,有一个通用块设备层,找到并写缓存后,要提交IO,这里会有一个请求队列,里面是request对象,每个对象包含多个BIO请求。这里面还涉及到IO请求的调度算法比如电梯算法等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/537452.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot中整合Swagger2接口文档

目录 1、maven依赖 2、swagger2 注解整体说明 2.1、请求类的描述 2.2、方法和方法参数的描述 2.3、方法的响应状态的描述 2.4、对象的描述 3、请求类的描述 3.1、Api&#xff1a;请求类的说明 3.2、示例&#xff1a; 4、方法和方法参数的描述 4.1、ApiOperation&#xff1a…

Linux 中的文件类型

目录 Linux文件类型说明 软链接 软链接特点 软链接工作过程 正常原文件a工作流程 文件a的软链接工作流程 硬链接 硬链接的特点 文件a的硬链接工作流程 软链接与硬链接的比较 测试软链接以及硬链接 1.进入根目录 2.创建test目录 3.进入test目录中&#xff0c;并创建a…

基于Redis的分布式锁,Redisson的简单使用和常用配置

介绍 Redisson是一个在Redis基础上实现的Java驻内存数据网格。Redisson提供了使用Redis的最简单最便捷的方法。Redisson的宗旨是促进使用者对Redis的关注分离&#xff0c;从而让使用者能够将精力更集中的放在处理业务逻辑上。 Redisson官方文档地址&#xff1a;https://githu…

蛋糕烘焙店小程序开发 让生活多点甜

蛋糕甜品因为较高的颜值、香甜的口感深受大众喜欢&#xff0c;当我们路过一家蛋糕烘焙店的时候&#xff0c;飘香的味道让我们流连忘返。但是互联网时代&#xff0c;各个行业都在转型&#xff0c;蛋糕烘焙店也需要由传统线下店面向线上线下结合的方式转变&#xff0c;以求摆脱区…

数据对象属性分类

数据集由数据对象组成&#xff0c;一个数据对象代表一个实体。数据对象又称样本、实例、数据点或对象。属性&#xff08;attribute&#xff09;是一个数据字段&#xff0c;表示数据对象的一个特征。属性向量&#xff08;或特征向量&#xff09;是用来描述一个给定对象的一组属性…

MySQL之单表访问方法

前言 本文章收录在MySQL性能优化原理实战专栏&#xff0c;点击此处查看更多优质内容。 本文摘录自 ▪ 小孩子4919《MySQL是怎样运行的&#xff1a;从根儿上理解MySQL》 对于我们这些MySQL的使用者来说&#xff0c;MySQL其实就是一个软件&#xff0c;平时用的最多的就是查询功…

快码住!!! 二叉树概念、重要性质、存储结构 技巧大总结!

文章目录 树树的概念树的表示树在实际中的应用 二叉树二叉树的概念特殊的二叉树 二叉树的性质二叉树性质应用的练习题 二叉树的存储结构顺序结构链式结构 树 树的概念 树是一种非线性的数据结构&#xff0c;它是由n(n>0)个有限结点组成的一个具有层次关系的集合。把它叫做…

语言模型及Word2vec与Bert简析

语言模型可以对一段文本的合理性概率进行估计&#xff0c;对信息检索&#xff0c;机器翻译&#xff0c;语音识别等任务有着重要的作用。就以前的学习笔记&#xff0c;本文简单总结了NLP语言模型word2vec和bert分享给大家&#xff0c;疏漏之处&#xff0c;望请指出&#xff0c; …

Go语言的简介和环境搭建

Go语言的简介和环境搭建 带你了解什么是Go语言 如何安装和配置Go的开发环境 静态强类型&#xff0c;编译型语言&#xff01;&#xff01;&#xff01; 1.简介 1.1介绍 Go 也称为 Golang&#xff0c;两个是一个东西。谷歌弄得。创造者都是大佬&#xff0c;所以说这个编程语言很…

PSP - AlphaFold2 Multimer 的 Heteromer (异聚体) MSA 逻辑

欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://blog.csdn.net/caroline_wendy/article/details/130733737 同源多聚体 (Homomer) 是由相同的蛋白质亚基组成的,而异源多聚体 (Heteromer) 是由不同的蛋白质亚基组成的。同源多聚体的亚基之间通常有对称的相…

BFT 最前线 | OpenAI开放网络浏览和插件;“360AI商店”上线;Bing市场份额不升反降;亚马逊机器人配送中心投产

原创 | BFT机器人 AI视界 TECHNOLOGY NEWS 01 OpenAI将向所有ChatGPT Plus用户推出网络浏览和插件 OpenAI将向所有ChatGPT Plus用户推出网络浏览和插件近日&#xff0c;OpenAI发文称&#xff0c;将在本周&#xff08;5.15-5.21日&#xff09;内向所有ChatGPT Plus用户推出网络…

【Python scikit-learn】零基础也能轻松掌握的学习路线与参考资料

Python是一种广泛使用的编程语言&#xff0c;随着数据科学领域的不断发展&#xff0c;Python成为了数据科学的主要工具之一。scikit-learn是Python中一款非常流行的机器学习库&#xff0c;它为广大科学家和工程师提供了一种简单而有效的方法来解决机器学习问题。 本文将从以下…

客服软件强攻略——改善客户自助服务

客户自助服务相对容易采用并集成到您现有的客户服务产品中。也就是说&#xff0c;任何自助服务计划都应该经过充分研究&#xff0c;跟踪明确的目标和成功指标&#xff0c;以确保其成功。 有效的自助服务通常可以通过软件工具形成&#xff0c;比如SaleSmartly&#xff08;ss客服…

微服务保护(线程隔离、降级、熔断)

线程隔离 线程隔离有两种方式实现: 线程池隔离信号量隔离 线程池隔离 假设服务A依赖于服务B和服务C&#xff0c;那么服务A就会分别对服务B和服务C创建线程池&#xff0c;当有请求进来时不会使用服务A本身的线程&#xff0c;而是到对应的线程池中取一个线程来调用feign的客户…

ViLT论文精读笔记

ViLT论文精读笔记 0.摘要1.引言2.背景知识&#xff08;小综述&#xff09;2.1对VLP模型分类2.2模态的融合2.3融合前特征的抽取 3.模型方法3.1预训练目标函数&#xff1a;3.1.1 Image Text Matching&#xff1a;3.1.2 Masked Language Modeling3.1.3 Masked Image Modeling 3.2W…

本周刷题记录

截至周三刷了六道题 题目1&#xff1a; 这是个交互题&#xff0c;目前遇到的交互题都是用二分解决的。 本题使用二分精准定位拥有重量为2的石头的堆。 为避免时间超限&#xff0c;应该再输入数据时计算好前缀和。 二分过程中&#xff0c;如果哪边的重量总和不等于石头数&…

关键词采集软件-关键词自动生成器

网站关键词对于SEO优化至关重要&#xff0c;在搜索引擎排名和流量中扮演着重要的角色。而147seo关键词生成软件可以帮助用户更好地发现与他们的业务和目标相关的关键词和话题。其中的147SEO关键词挖掘软件是其核心功能之一&#xff0c;能够自动批量实时挖掘关键词和短语&#x…

day33_css

今日内容 零、 复习昨日 一、CSS 零、 复习昨日 见代码 一 、引言 1.1CSS概念 ​ 层叠样式表(英文全称&#xff1a;Cascading Style Sheets)是一种用来表现HTML&#xff08;标准通用标记语言的一个应用&#xff09;或XML&#xff08;标准通用标记语言的一个子集&#xff09;等文…

以转账案例说明Spring事务

文章目录 1 Spring事务简介1.1 相关概念介绍1.2 转账案例-需求分析1.3 转账案例-环境搭建步骤1:准备数据库表步骤2:创建项目导入jar包步骤3:根据表创建模型类步骤4:创建Dao接口步骤5:创建Service接口和实现类步骤6:添加jdbc.properties文件步骤7:创建JdbcConfig配置类步骤8:创建…

Istio零信任安全架构设计

主要分为几个模块 安装安全概念整体安全架构源码 1.安装istio (windows环境) windows安装Rancher的步骤 : https://docs.rancherdesktop.io/getting-started/installation, docker desktop开始面向中大型企业收费: https://baijiahao.baidu.com/s?id1709665495660071676&…