最近有接触到一些嵌入式设备,因为长期用Go,对C语言比较生疏,那也就直接使用CGO直接对数据进行读写。当我以为是很简单的一件事的时候,但我发现Linux下,直接对块设备通信,发生了一些问题。

通过挂载的文件,和设备进行通信,但如果使用简单的openfopen,并不能直接讲数据写到设备中,非常奇怪,先天然的就想到是不是缓存问题,当文件被close掉,数据则应该被落盘才对,怎么一点反应都没有呢?

询问过后发现,open函数中,有一个flag参数O_DIRECT,可以绕过缓冲区高速缓存,直接进行IO操作。

直接IO:Linux允许应用程序在执行磁盘IO时绕过缓冲区高速缓存,从用户空间直接将数据传递到文件或磁盘设备,称为直接IO(direct IO)或者裸IO(raw IO)。

也就是这行图显示的:

NewImage

上图中,左侧虚线方框中为可于任何时刻显式强制刷新各类缓冲区的调用。

右侧所示为促使刷新自动化的调用:通过禁用stdio的缓冲,和在文件输出类的系统调用中启用同步,从而使每个write()调用立刻刷新到磁盘。

但使用直接IO是有一些限制的:

  • 用于传递数据的缓冲区,其内存边界必须对齐为块大小的整数倍
  • 数据传输的开始点,即文件和设备的偏移量,必须是块大小的整数倍
  • 待传递数据的长度必须是块大小的整数倍。

不遵守上述任一限制均将导致EINVAL错误。这些错误我也遇到了。

如何遵守呢?按照其规定,定义一个字节对齐,且长度为块大小的整数倍的buffer。即使数据长度不足buffer长度,写入数据时,也需要是直接写入整个buffer。

1
2
3
#define BUFFRE_LENGTH 4096

unsigned char g_buff[BUFFRE_LENGTH] __attribute__((aligned(BUFFRE_LENGTH)));

__attribute__可以设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)。

aligned (alignment):该属性设定一个指定大小的对齐格式(以字节 为单位)

参考链接:

O_DIRECT与O_SYNC区别

c语言中__attribute__的意义