linux IO Block layer 解析

2023-11-10

早期的 Block 框架是单队列(single-queue)架构,适用于“硬件单队列”的存储设备(比如机械磁盘),随着存储器件技术的发展,支持“硬件多队列”的存储器件越来越常见(比如 NVMe SSD),传统的单队列架构也因此被改成了多队列(multi-queue)架构。早在 3.13 内核就已经加入了多队列代码,但是还不太稳定,经过多年的发展 multi-queue 越来越稳定,linux 5.0+ 已经默认使用 multi-queue。本篇文章介绍 Block 层框架及调度器相关知识,让读者对 Block 层有一个宏观的认识。

一、Block 层的作用

用户发起读写操作时,并不是直接操作存储设备,而是需要经过较长的 IO 栈才能完成数据的读写。读写操作大体上需依次经过虚拟文件系统 vfs、磁盘文件系统、block 层、设备驱动层,最后到达存储器件,器件处理完成后发送中断通知驱动程序,流程见图 1。

图1 IO 栈

备注:page cache 机制用来提高性能。在内存资源不紧张的情况下,用户访问过的数据不会被丢弃,而是缓存在内存中,下次可以访问快速的内存中数据,无需访问慢速的存储设备。mapper layer 用来将用户操作文件偏移量转换成磁盘文件系统的 block 偏移量。

Block 层连接着文件系统层和设备驱动层,从 submit_bio 开始,bio 就进入了 block 层,这些 bio 被 Block 层抽象成 request 管理,在适当的时候这些 request 离开 Block 层进入设备驱动层。IO 请求完成后,Block 层的软中断负责处理 IO 完成后的工作。Block 层主要负责:

管理 IO 请求

IO 请求暂存、合并,以及决定以何种顺序处理IO请求。这里面涉及到 single-queue、multi-queue 框架以及具体的 IO 调度器。

IO 统计

主要是task io accounting统计各个进程的读写情况,统计信息见struct task_io_accounting。

注意,虽然 Block 层中存放着很多的 request,但正常情况下 Block 层不会主动“下发” request 给设备驱动程序(在线切换 IO 调度器、存储器件 offline 场景会主动下发 request)。当设备空闲时,设备驱动程序从 Block 层的“分发队列”头部依次取 request 进行处理,设备驱动程序拿到 request 后,根据 request 中的信息及器件协议生成 cmd 命令交由器件处理。

二、Block 框架演变

Block 层软件设计与存储器件的特性紧密相关,大致经历了 2 个阶段。

图2 single-queue与multi-queue 架构

*引用自Linux Block IO: Introducing Multi-queue SSD Access on Multi-core Systems

single-queue 框架

早期的存储设备是磁盘,特点是机械运动寻址、且不支持多硬件队列并发处理 io,所以代码逻辑自然地设计了一个软件分发队列,这种软件逻辑上只有一个分发队列的架构称作 single-queue 架构,由于软件本身的开销(多核访问 request queue 需要获取 request_queue->queue_lock 等原因),single-queue 的 IOPS 能达到百万到千万级别的数据量,由于早期存储器件速度慢,百万的 IOPS 已经完全能够满足需求。

multi-queue 架构

当支持多队列的高速存储器件出现后,器件端处理的时间变短,single-queue 引入的软件开销变得突出,软件成为性能瓶颈,导致性能瓶颈的因素有 3 个:

1) 所有 cpu 共享一个 request queue,对 request_queue->queue_lock 的竞争比较多。

2) 大多数情况下,完成一次 io 需要两次中断,一个是硬件中断,一个是 IPI 核间中断用于触发其他 cpu 上的软中断。

3) 如果提交 io 请求的 cpu 不是接收到硬件中断的 cpu,还存在远端内存访问的问题。

Jens Axboe(block maintainer) 针对 single-queue 存在的问题,提出了 multi-queue 架构,这种架构为每个 cpu 分配一个软件队列(称为 soft context dispatch q),又根据存储器件的硬件队列(hardware q)数量分配了相同数量的硬件上下文分发队列(hard context dispatch q,这是软件逻辑上的队列),通过固定的映射关系,将 1 个或多个 soft context dispatch q 映射到 1 个 hard context dispatch q,再将 hard context dispatch q 与存储器件的 hardware q 一一对应起来,达到并发处理的效果,提升 IO 性能。

三、数据结构

相关的数据结构可以分成两大类,一是 IO 请求本身,二是管理 IO 请求用到的队列,理解这些数据结构是了解 block 层设计逻辑的基础,数据结构描述如下。

1.  IO 请求

按照 IO 请求的生命周期,IO 请求被抽象成了 bio、request(简称 rq)、cmd,见图 3。访问存储器件上相邻区域的 bio、request 可能会被合并,称为 bio merge、request merge。若 bio 的长度超过软件或者硬件的限制,bio 会被拆分成多个,称为 bio split。Block 层接收到一个 bio 后,这个 bio 将生成一个新的 request,或者合并到已有的 request 中。图 3 中 bio2 被拆分,bio3、bio4 合并到一个 request 中。

图3 IO 请求的生命周期

bio 是描述 io 请求的最小单位,bio 描述了数据的位置属性,表示为:

表1 bio结构体说明

POSIX标准定义了scatter-gather I/O,这种IO通过一个读/写系统调用可以往多个不连续的内存段中读/写,从而提高IO性能并且能确保访问多段内存的原子性。linux系统调用readv、writev支持scatter-gather I/O,所以bio的内存端需用多个[ page地址, 页内偏移, 长度 ]描述不连续的内存段,每一个[ page地址, 页内偏移, 长度 ]在linux中称为bio_vector。bio结构体的示意图见下图。

图4 bio 结构体示意图

request是IO调度的最小单位,多个bio访问存储器件上相邻的区域数据并且是同种类型的(读/写),则会被合并到一个request中,所以一个request可能包含多个bio。系统中request数量是有限制的,不是无限多的,否则当io请求太多,而存储器件来不及处理时,就会出现struct request结构体占用太多内存的情况。

在single-queue中用rq资源池管理request的申请释放,一个存储器件的读写请求数量各自限制最多q->nr_requests个(默认的q->nr_requests = BLKDEV_MAX_RQ = 128)。当该器件上待处理的读或写请求数超过=7/8 * q->nr_requests时进入拥塞状态,此时会限制新生成request的速度。比如读请求拥塞时,page_cache_async_readahead函数中关闭预读功能以较少新生成的request。当存储器件待处理读或写的request比低时退出拥塞状态。以默认q->nr_requests = 128个请求为例:

图5 single-queue request 数量管理

在multi-queue中,没有上述的congestion state逻辑,因为multi-queue用于支持多硬件队列存储器件的场景(当然也可以用于单硬件队列的存储器件),这些存储器件的速度很快,不需要过多考虑器件处理慢的问题(但是也不能一味地任由rqeuest增长)。在multi-queue中request最大数量与调度器的tag数量有关,同single-queue一样,默认值也是q->nr_requests = BLKDEV_MAX_RQ = 128,当存储器件待处理的读/写超过调度器tag数量时,申请rq的task睡眠,当有rq处理完成被释放后,再唤醒当前的task。默认的调度器tag数量可以通过sysfs接口修改,内核里通过blk_mq_update_nr_requests更新。

cmd是设备驱动处理的IO请求,设备驱动程序根据器件协议,将request转换成cmd,然后发送给器件处理。cmd已经不属于block层管理了,所以这篇文章不做描述。

2.IO队列

上面的IO请求需要经过多级缓冲队列管理,见图6。

图6 IO队列

注:multi-queue如果器件支持hardware multi q,plug list功能关闭

所有的bio都由submit_bio提交到block层,bio依次经过下面队列:

进程私有的plug list

队列中存放的是io请求(rq),引入这个缓冲队列的目的是为了性能。进程提交一个bio后,短时间类很可能还会有新的bio,这些bio被暂存在plug list中,因为这个队列只有本进程能操作,所以不用加锁就可以进行bio merge操作(在后面提到的调度器队列中做merge需要加锁)。

调度器队列elevator q

队列中存放的是io请求(rq)。single-queue的调度器有noop、cfq;multi-queue的调度器有mq-deadline、bfq、kyber。每个调度器有都实现了专门的数据结构管理rq(链表、红黑树等),这里统以elevator q称呼。

系统中的调度器队列可能有很多,比如cfq为每个进程维护各自的同步请求队列,又为所有进程维护了公用的异步请求队列。调度器需要决定先处理哪个队列以及队列中的哪个rq。一般情况下,调度器不会主动将rq移到设备分发队列中,而是由设备驱动程序主动来取rq。

设备分发队列device dispatch q(也可以称作hardware dispatch q)

这是软件实现的队列。存储器件空闲时,其设备驱动程序主动从调度器中拉取一个rq存在设备分发队列中,分发队列中的rq按照先进先出顺序被封装成cmd下发给器件。

对于multi-queue,设备分发队列包中还额外包含per-core软件队列,它是为硬件分发队列服务的,可以把它理解成设备分发队列中的一部分。

硬件队列HW q

队列中存放的是按器件协议封装的cmd,一些器件是单HW队列,比如UFS内部是一个队列深度为32的HW q,NVMe SSD最大支持的队列数量为64K、队列深度64K。

四、常用调度器

single-queue用到的调度器有noop,deadline,cfq。

multi-queue用到的调度器有none(类似于noop),mq-deadline(类似于deadline),bfq(类似于cfq),kyber。这里选取几个具有代表意义的调度器对比分析。

noop调度器

最简单的调度器,IO请求放入一个FIFO队列,逐个执行这些IO请求(rq)。

noop调度器基本上对rq不做额外,仅仅在将rq插入到调度器队列时,将rq与已有的rq做前向、后向合并(2个rq的sector连续)。从调度器队列中发送一个rq给设备驱动程序代码如下:

cfq调度器(Completely Fair Queuing)

CFQ公平对待每个进程,给每个进程分配相同的“虚拟”时间片,在时间片内进程可以访问存储器件,时间片用完后,选择下一个进程运行。

1)cfq支持“优先级”策略

“虚拟”时间片 = 实际访问存储设备时间*优先级系数,优先级越高实际获得的时间片越长,优先级越低实际获得的时间片越短。

cfqq用完时间片后,通过cfq_resor_rr_list调整cfqq在红黑树中的位置,由于红黑树key值rb_key近似等于jiffies,所以cfqq近似于按照round-roubin执行,代码如下。

图7 cfqq round-roubin策略

2)cfq支持“权重策略”

多个cfqq可以归属于一个group,这些group按照占用存储设备的时长(cfq中称作vdisktime)组织在红黑树中。当group被调度运行结束后,cfq_group_served更新group 的vdisktime(vdisktime增长量=实际占用disk时间*权重系数),权重越高vdisktime增长的越慢,权重越低vdisktime增长的越快。

cfq优先选择vdisktime小的group执行,所以权重越大,group被调度的越频繁。

cfq_group_served更新vdisktime的代码如下:

vfr表示在group与其他group构成的父group中,group在父group中的权重占比。

cfqg_scale_charge将group使用存储设备的时长(以charge表示)做个虚拟转换,本质上等价于cfqg->vdisktime = charge / vfr

bfq调度器(Budget Fair Queuing)

bfq从cfq演变过来,大部分代码也借鉴cfq的。cfq在各个进程间平分存储器件时间来达到公平,这样做是有问题的,一个随机访问存储设备的进程与一个顺序访问存储设备的进程,虽然占用存储器件的时间是一样的,但二者访问的数据量有很大差距,难以保证公平。

bfq通过budget(就是block sector)确保公平,不管进程占用了存储设备多长时间,只管进程访问存储设备的数据量。

bfq的这种思想带来了一个优势,即提高了交互式进程的响应性。因为交互式进程每次IO的数据量很少,而batch类进程数据量很大,为了确保budget公平(访问相同数量的block sector),必须频繁调度交互式进程运行,从而提高了交互式进程的响应性。

图8 bfqq红黑树

bfq同cfq一样,也为每个进程分配一个队列称作bfqq,通过时间戳维护在红黑树中(cfq是通过vdisktime维护在红黑树中)。与cfqq轮询机制不同,bfqq红黑树中只有eligible状态的bfqq才会被选择调度。我们定义如下变量:

与cfqq权重的作用不一样,cfqq中权重越大时间片越长,但bfqq中权重与时间片无关,与调度频率有关,假设有3个进程P1~P3,每次访问100个budget,权重比例P1:P2:P3 = 2:1:1,bfqq执行效果如下(绿色表示eligible,灰色表示不可选):

表2 bffq调度

从上面执行步骤可以看出,P权重占比50%,第一次到第三次下来,P1执行2次,P2、P3累计执行2次,执行的频率与各进程的权重占比相等。

以上3个调度器的差异对比如下:

表3 noop/cfq/bfq对比

五、总结

Block主要涉及框架和调度器两部分,都是为了吞吐量合IO响应性设计的。框架代码与存储器件紧密相关,从慢速的存储设备到高速的存储设备,Block框架变成了multi-queue架构,软件、硬件的紧密结合才能把存储器件性能发挥到最大,期待未来新存储器件的出现,将存储性能再提高一个级别。调度器也越来越智能,能够兼顾交互进程的响应性和batch类进程的吞吐量,用户体验在Block新框架、新调度器的支持下将会越来越好。
 

参考资料

[1] Linux Block IO: Introducing Multi-queue SSD Access on Multi-core Systems

[2] https://kernelnewbies.org/Linux_5.0#Block_layer

扫描关注
“内核工匠”微信公众号
Linux 内核黑科技 | 技术文章 | 精选教程

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux IO Block layer 解析 的相关文章

  • ctfshow萌新web17

    c传参过滤掉php 思路 include文件包含 利用日志文件包含 访问日志文件 c var log nginx access log 发现日志文件记录了user agent头 于是在该头中插入一句话木马 再访问日志文件 看日
  • AMBA总线协议AHB、APB、AXI对比分析

    一 AMBA概述 AMBA Advanced Microcontroller Bus Architecture 高级处理器总线架构 AHB Advanced High performance Bus 高级高性能总线 ASB Advanced
  • Uniapp中vueX实现登录状态功能

    uniapp使用Vuex实现登录状态的判断 退出登录 使用action commit实现登录功能 Vue use Vuex export default new Vuex Store state token userid username
  • 了解CMS(Concurrent Mark-Sweep)垃圾回收器

    原文地址为 了解CMS Concurrent Mark Sweep 垃圾回收器 一字不差的贴的人家的 就是感觉写的比较好 贴出来了 羞羞 1 总体介绍 CMS Concurrent Mark Sweep 是以牺牲吞吐量为代价来获得最短回收停
  • 使用git和maven过程一些命令

    git常用命令 1 git status 查看文件在工作目录与缓存的状态 2 git add 添加所有的文件到缓存 3 git commit m 提交的描述信息 如果我们这里不用 m参数的话 git将调到一个文本编译器 通常是vim 来让你
  • mysql5.7 leftjoin group by(获取关联表最新数据)

    用户表 users 日志表 logs 外键 user id 创建时间 created at 获取所有用户最新的日志 SELECT b from SELECT user id max created at as maxtime from lo
  • nafxcwd.lib(afxmem.obj) error lnk2005

    最近写程序突然遇到个错误 nafxcwd lib afxmem obj error lnk2005 查了下msdn发现主要原因是同时使用了CRT中的new delete和MFC中的new delete重载导致的 参考 http suppor
  • RabbitMq基本概念 = >实践DirectExchange和TopicExchange交换机模式

    AMQP 和IM的区别 AMQP 高级消息队列 1 可以一对多广播 也可以一对一广播 2 生产者和消费者不知道对方是谁 IM 1 只能一对一广播 2 生产者和消费者知道对方是谁 RabbitMQ 只是消息代理 我们不生产消息 我们只是消息的
  • 怎样成为一名优秀的程序员?

    新加坡国立大学计算机系有两门课 CS 1101 1102 几乎所有的大学计算机系课程都有两门类似的课程 但几乎所有的学生都误解了这两门课 以为前者是教C 后者是教java 但实际上前者是 Programming Methodology 后者
  • Java学习笔记——34多线程01

    多线程 实现多线程 进程和线程的区别 多线程的实现方式 方式一 继承Thread类 设置线程名称 线程调度 线程控制 线程生命周期 方式二 实现Runnable接口 实现多线程 进程和线程的区别 进程 是正在运行的程序 是系统进行资源分配和
  • Netstat命令详解(Windows)

    Netstat 用于显示与IP TCP UDP 和ICMP 协议相关的统计数据 一般用于检验本机各端口的网络连接情况 如果你的计算机有时候接收到的数据报导致出错数据或故障 你不必感到奇怪 TCP IP 可以容许这些类型的错误 并能够自动重发
  • Kotlin/Java之常用“集合型”数据类型

    先说说数组 Array 和 ArrayList lt 数组 Array gt 整型数组初始化 val point intArrayOf 0 0 实为IntArray 对应Java类型 int 其他还有 charArrayOf byteArr
  • 两数求和

    给定一个整数数组 nums 和一个目标值 target 请你在该数组中找出和为目标值的那 两个 整数 并返回他们的数组下标 你可以假设每种输入只会对应一个答案 但是 你不能重复利用这个数组中同样的元素 示例 给定 nums 2 7 11 1
  • 服务器租用机房机房的类型应该如何选择

    服务器租用机房机房的类型应该如何选择 1 单电信机房 单电信服务器机房业务模式比较固定 访问量也不是很大 适合新闻类网站或政务类网站 如果网站的PV流量持续增加 建议后期采用租赁CDN的方式解决非电信用户访问网站速度过慢的问题 2 双线机房
  • QKL123区块链排行榜(2019年04月)

    QKL123区块链排行榜包括区块链项目 区块链交易平台 区块链媒体 区块链公众号 区块链矿机 区块链矿池 EOS Dapp ETH Dapp 区块链钱包九大榜单 目前 区块链项目榜单选取的客观指标包括流通市值 GitHub提交数 区块链交易
  • 嵌入式stm32基础项目开发:心率检测仪的设计与实现

    嵌入式stm32基础项目开发 心率检测仪的设计与实现 本教程主要给大家谅解了嵌入式stm32开发 心率检测仪的设计与实现 需要的朋友们可以下载来看看 作为参考 项目描述 通过心律传感器采集我们的心律数据 然后通过串口传送到上位机中 上位机用
  • 这篇文章教大家怎么生成ai图片

    在数字化时代 人工智能技术的发展正在改变我们的生活方式 其中之一就是在艺术领域的应用 ai绘画是人工智能技术在艺术领域的一种应用 它可以自动创作出各种各样的图片 为艺术家和设计师提供了更加便捷和高效的绘画工具 ai绘画的出现 不仅可以缩短绘
  • 素数环(回溯算法)

    回溯算法 在包含问题的所有可能解的解空间树中 从根节点出发 按照深度优先遍历的策略进行搜索 对于解空间树种的某个节点 如果该节点满足问题的约束条件 则进入该子树继续进行搜索 否则将以该节点为根节点的子树进行剪枝 回溯法常常可以避免所有的可能
  • layui table.js表格一直返回数据异常

    1 排查数据是否已经正常返回 2 layui table 返回格式默认不能自定义的 返回的分页json格式需要和table js中规定的返回键一致 如下 3 经过测试 其实最重要的是code需要和上图中statusName后的resultC
  • Cisco 路由器VOIP 配置解析

    在企业网络中推广 IP 语音技术有很多优点 例如可以控制数据流量 保证语音质量 充分利用企业租用的数据线路资源 节省传统的长途话费等等 企业使用 IP 语音技术 可以将语音 数据和多媒体通信融合在一个集成的网络中 并在一个企业解决方案中 把

随机推荐

  • 简易版的飞机大战(C语言)

    一 只会发射激光 画质不清晰的飞机大战 游戏的总体结构根据C语言的循环制作的 本来还想说点什么但是注释里面都有 代码 include
  • ansys18安装以后打不开_ansys18.0安装过程及常见问题解决方案【图文】

    1 首先打开ansys18 0安装文件夹 一般情况下通过网络渠道下载的ansys18 0安装包会有四个文件夹 crack文件夹为授权配置文件夹 disk1 disk2 disk3文件夹为安装程序包 我们首先打开disk1文件夹 双击setu
  • 物联网LoRa系列-31:通过LoRa终端实现远程抄表的原理与系统框架(水、电、气、热等通用)

    LoRa终端远程抄表的系统架构图 抄表系统由 无线电表 线集中器 业务数据中心组成 1 无线电表 又称为LoRa终端 内嵌LoRa模块 进行数据的采集 并LoRa WAN协议实现远程数据的传输 LoRa智能终端能将传统水表 电表等读数通过电
  • 可迭代(iterable)和类数组(array-like)

    可迭代 iterable 和类数组 array like 可迭代 iterable 是实现了 Symbol iterator 方法的对象 可以应用 for of 的对象被称为 可迭代的 类数组 array like 是有索引和 length
  • Redis主从复制的原理

    更多内容 欢迎关注微信公众号 全菜工程师小辉 公众号回复关键词 领取免费学习资料 在Redis集群中 让若干个Redis服务器去复制另一个Redis服务器 我们定义被复制的服务器为主服务器 master 而对主服务器进行复制的服务器则被称为
  • pyautogui库的使用教程(超详细)

    一 前言 PyAutoGUI 让您的 Python 脚本控制鼠标和键盘以自动与其他应用程序交互 官方文档 PyAutoGUI documentation 常用函数列表 函数名 功能 基本 pyautogui size 返回包含分辨率的元组
  • 在编辑操作时,el-select多选下拉组件,选中label标签后,框中无法回显选中的label,,,

    1 问题描述 在编辑操作时 页面的el select多选下拉组件 在选择新的label标签时 change事件和监听数组对象都能确定数据已发生改变 ngmodel绑定就是最新的id集合 但就是框中不显示最新选中的label 而change事
  • 论文导读

    图的最大独立集问题 MIS problem 是图论研究中的一个重要问题 具有广泛的应用 本文介绍了最大独立集求解相关的三篇工作 包括一篇启发式方法和两篇基于学习的方法 希望能让大家对这个问题有所了解 问题定义 一个图G V E 的顶点集子集
  • 放弃手写代码吧!用低代码你能生成各种源码

    很多同学不知道为什么要用Low code做开发 传统IT开发不行么 当然可以 传统IT自研软件开发 通过编程去写代码 还有数据库 API 第三方基础架构等 这个方式很好 但不可避免的会带来开发周期长 难度大 技术人员不易开发维护 因此价格及
  • EDUCODER---WEB__JavaScript学习手册十:正则表达式

    第一关 字符串字面量 请在此处编写代码 Begin var pattern js n End 第二关 字符类 请在此处编写代码 Begin var pattern1 a zA Z 0 9 var pattern2 A 0 9 End 第三关
  • Linux下Python环境安装与部署

    因为我是Python零基础 所以如何部署全靠百度 这边我把我查到的资料和安装使用过程中遇到写下来 如果有写的不对的或者有更好的方式 欢迎评论指出 一 Python环境安装 网上有很多安装教程 可以自行百度安装 我参考的是这个 仅第一步安装p
  • The Lost House【树形DP+期望+构造路径】

    题目链接 POJ 2057 题意 有一棵N的点的树 开始的时候蜗牛在1号结点 它不知道它的家在哪个叶子结点 树上的有些结点有虫虫 虫虫会告诉你 你的家是否在以它所在结点为根的子树上 现在需要你规划走的方案 使得找到哪个叶子结点才是家的所走路
  • python将word表格转写入excel

    Notes 想将一份 word 文件中的几个表格转写入 excel 文件中 后续用 excel 处理 用到 python docx 和 pandas 分别处理 word 和 excel 安装 python docx pip install
  • pytorch中网络loss传播和参数更新理解

    相比于2018年 在ICLR2019提交论文中 提及不同框架的论文数量发生了极大变化 网友发现 提及tensorflow的论文数量从2018年的228篇略微提升到了266篇 keras从42提升到56 但是pytorch的数量从87篇提升到
  • 利用R包ggmap进行空间可视化

    ggmap 是在R环境里调用地图作用可视化的利器 它的语法结构跟ggplot2非常相似 也使R语言的用户可以迅速上手 ggmap 结合 ggplot 可以方便快速绘制基于地图的可视化图表 下面的文章里 我将用两个例子 三藩市的犯罪记录 和
  • require 方法详解

    在 NodeJS 中有一个方法是我们使用频率最高的 那就是 require 方法 NodeJs 遵循 CommonJS 规范 该规范的核心是通过 require来加载其他依赖的模块 几个问题 module exports 或者 export
  • 朴素贝叶斯算法python sklearn实现_朴素贝叶斯算法——实现新闻分类(Sklearn实现)...

    1 朴素贝叶斯实现新闻分类的步骤 1 提供文本文件 即 2 准备数据 将数据集划分为训练集和测试集 使用jieba模块进行分词 词频统计 停用词过滤 文本特征提取 将文本数据向量化 3 分析数据 使用matplotlib模块分析 4 训练算
  • 76. 如何理解 Python 中字符串中的\字符?

    Python字符串中的 字符代表转义字符 路径名中用来连接路径名 编写太长代码手动软换行 转义符 转义符 描述 续行符 在行尾时 反斜杠符号 单引号 双引号 a 响铃 b 退格 Backspace e 转义 000 空 n 换行 v 纵向制
  • border-sizing之border-box、content-box

    border sizing是CSS3的属性之一 其属性值为border box content box 我们正常理解的盒模型其实是border sizing的属性值是content box 即正常盒模型 属性值为border box的盒模型
  • linux IO Block layer 解析

    早期的 Block 框架是单队列 single queue 架构 适用于 硬件单队列 的存储设备 比如机械磁盘 随着存储器件技术的发展 支持 硬件多队列 的存储器件越来越常见 比如 NVMe SSD 传统的单队列架构也因此被改成了多队列 m