https://www.zhihu.com/question/20186057/answer/15893951)
我就是做 USB 驱动的,U 盘驱动也做过,控制器驱动也做过,我来解释一下速度的问题:
首先实际速度肯定低于理论速度,比如百兆以太网,能到 10MB/s 的速度就不错了,但是为什么 USB 差距这么大,主要是 USB 传输的无用包实在是太多了。
对于 USB2.0,480Mbps 是指总线的频率,也就说,总线信号每秒最多能传输这么多 bit,这些信号包括控制信号和数据信号,现在来看看那数据信号都有什么:
每 125us 就有一个 micro frame(微帧),每 1ms 还有一个 frame,在 USB 规范里叫 SOF,类似于一种同步信号。
标准 USB 传输过程:
in/out token,data0/1,ack,真正有效的数据就在 data0/1 里,如果一个 data 包放不下,那么会放到多个 data 里。
传输之前,对于 2.0 设备还要发起一个 ping 请求,确认设备是活着的。
USB 不是一个全双工设备,通常的行为是这样:总线空闲,主机端请求数据,总线空闲,设备回应,总线空闲,主机准备接受数据,总线开始传输,传输完成总线空闲,主机检查数据无误,给设备回应说数据传输正常结束(不需要重传)。
对于 U 盘本身:
由于 U 盘规范的原因,大多数操作系统要求定期检查 U 盘状态(是否是 alive 的),这个请求叫做 test unit ready(各个 OS 都有,大家就不要吵了,U 盘规范上的)。
同时传输 512 字节(一个扇区的数据)要包括命令标识,命令号,LBN(逻辑块地址)以及乱七八糟一堆堆的东西,一个扇区
大概需要将近 600 字节的数据。同时主机端还要给予相应的回应。
至于前面有人说 bulk 传输不会占满带宽,这也不是完全对的,确实 USB 传输分为 interrupt 传输,bulk 传输,ISO 传输,但是只有 interrupt 预留了很少,ISO 会保留 30% 左右,但当没有 ISO 传输存在的时候,bulk 是可以占掉这一部分的。
USB2.0 规范里给了一个公式,算传输时间的(算法解释就太复杂了,见 USB2.0 5.11.3 Calculating Bus Transaction Times):
High-speed (Input):
Non-Isochronous Transfer (Handshake Included)
= (55 * 8 * 2.083) + (2.083 * Floor(3.167 + BitStuffTime(Data_bc))) + Host_Delay
BitStuffTime(Data_bc) 这部分就是数据传输需要的时间,算起来麻烦,但是看到前面有一个 2.083 就能看出来,传输一个 bit 基本上需要 2.083 倍的时间,所以,简单的把 480Mbps 除以 2.083,再转换成字节大概是:28.8MB/s,也就是说,最多就这么快,再刨除 bulk-only 模式里的那一堆堆的多余指令:一个包需要 16 个字节左右,Windows 一次请求是 4KB(可能是为了页对齐),再浪费掉 1% 左右的时间,以及 host delay,也就是主机的校验延迟,那么实际速度就 20 多 MB/s,不管是读还是写。
但是我知道肯定有人说,测得的实际速度比这个快,当然了,我也见过比这个快的,为什么?恰好我做过 Windows 里文件系统开发,也研究过 Linux 里的 fat 驱动,先说 Linux,它很变态,你的写操作不一定真正写到磁盘上(证据我有,因为我有 USB 分析仪,能抓总线传输),Linux 会在后台慢慢的写,前台看上去已经写进去。
Windows 比 Linux 强一些,但是基于强大的预读和缓存,Windows 也不是实时读写,所以会看到数字有跳动的情况(Windows 内核里 cc 开头的函数就是干这个的)。
我们开发产品的过程中,看到过的最高总线利用率也就是 80% 左右(分析仪获得的总线数据),也就是说 28MB 的速度可能还要打个八折,崩溃去吧。
另外,U 盘(flash 介质)会更慢一些,因为 U 盘本身写的就慢,SSD 和硬盘会好些,虽然 SSD 也是 flash,但是 SSD 缓存大,并且有保障机制。
所以,作为一个 USB 驱动的开发人员(维护过 EHCI 驱动,改过 U 盘驱动,写过鼠标、FTDI 串口驱动,改过 USBD),我觉得 U 盘的速度是很坑爹的。
解释完了。