文件流如何实际工作?

Xeo*_*Xeo 13 c c++ file-io assembly fstream

我一直想知道一段时间,文件流是如何工作的?使用文件流,我的意思是访问文件的一部分而不将整个文件加载到内存中.
我(相信)知道C++类(i|o)fstream正是这样做的,但它是如何实现的?是否可以自己实现文件流?
它是如何在最低的C/C++(或任何支持文件流的语言)级别下工作的?难道C函数fopen,fclose,freadFILE*指针已经采取流(即不加载整个文件到内存)的照顾?如果没有,您将如何直接从硬盘驱动器读取并且是否已在C/C++中实现了这样的设施?

任何正确方向的链接,提示,指针都会非常有用.我用谷歌搜索了,但似乎谷歌并不太明白我追求的是什么......


Ninja-Edit:如果有人知道如何在汇编/机器代码级别工作,如果可以自己实现,或者你必须依赖系统调用,那就太棒了.:)不是答案的要求,虽然正确方向的链接会很好.

ick*_*fay 24

在最低级别(至少对于userland代码),您将使用系统调用.在类UNIX平台上,这些包括:

  • open
  • close
  • read
  • write
  • lseek

...和别的.这些工作通过传递这些称为文件描述符的东西.文件描述符只是不透明的整数.在操作系统内部,每个进程都有一个文件描述符表,包含所有文件描述符和相关信息,例如它是哪个文件,文件类型等等.

还有类似于UNIX上的系统调用的Windows API调用:

Windows传递HANDLEs,类似于文件描述符,但我相信,它的灵活性稍差.(例如,在UNIX上,文件描述符不仅可以表示文件,还可以表示套接字,管道和其他东西)

C标准库函数fopen,fclose,fread,fwrite,和fseek仅仅是围绕这些系统调用封装.

打开文件时,通常没有文件的内容被读入内存.当您使用fread或时read,您告诉操作系统将特定数量的字节读入缓冲区.这个特定的字节数可以是,但不一定是文件的长度.因此,如果需要,您只能将文件的一部分读入内存.

回答忍者编辑:

您询问了它在机器代码级别的工作原理.我只能解释它在Linux和Intel 32位架构上是如何工作的.使用系统调用时,某些参数将放入寄存器中.将参数放入寄存器后,会产生中断0x80.因此,例如,要从stdin文件描述符0 读取一千字节到地址0xDEADBEEF,您可以使用此汇编代码:

mov eax, 0x03       ; system call number (read = 0x03)
mov ebx, 0          ; file descriptor (stdin = 0)
mov ecx, 0xDEADBEEF ; buffer address
mov edx, 1024       ; number of bytes to read
int 0x80 ; Linux system call interrupt
Run Code Online (Sandbox Code Playgroud)

int 0x80引发操作系统通常会在中断向量表或中断描述符表中注册的软件中断.无论如何,处理器将跳转到内存中的特定位置.在那里,通常操作系统将进入内核模式(如果需要),然后执行相当于C的switch操作eax.从那里开始,它将进入实施阶段read.在read,它通常会从调用进程的文件描述符表中读取有关描述符的一些元数据.一旦它拥有所需的所有数据,它就会完成它的工作,然后返回到用户代码.

为了"做它的东西",我们假设它是从磁盘读取,而不是管道或stdin其他非物理位置.我们也假设它是从主硬盘读取的.另外,我们假设操作系统仍然可以访问BIOS中断.

要访问该文件,它需要做一堆文件系统的事情.例如,遍历目录树以查找实际文件的位置.我不打算这么说,因为我打赌你可以猜到.

有趣的部分是从磁盘读取数据,无论是文件系统元数据,文件内容还是其他内容.首先,您获得逻辑块地址(LBA).LBA只是磁盘上数据块的索引.每个块通常是512个字节(尽管这个数字可能已过时).仍假设我们可以访问BIOS并且操作系统使用它,然后它会将LBA转换为CHS表示法.CHS(Cylinder-Head-Sector)表示法是引用硬盘驱动器部件的另一种方式.它曾经与物理概念相对应,但现在它已经过时了,但几乎每个BIOS都支持它.从那里,操作系统将数据填入寄存器并触发中断0x13,BIOS的磁盘读取中断.

这是我能解释的最低级别,我确信在我假设操作系统使用BIOS之后的部分已经过时了.之前的一切都是它仍然有效,但我相信,如果不是简化的话.