c++ - 文件流实际上是如何工作的?

标签 c++ c assembly file-io fstream

我一直在想,文件流到底是如何工作的?对于文件流,我的意思是访问文件的一部分而不将整个文件加载到内存中。
我(相信)知道 C++ 类 (i|o)fstream正是这样做,但它是如何实现的?是否可以自己实现文件流?
它如何在最低的 C/C++(或任何支持文件流的语言)级别上工作?做 C 函数 fopen , fclose , freadFILE*指针已经处理流(即,不将整个文件加载到内存中)?如果没有,您将如何直接从硬盘读取,是否已经在 C/C++ 中实现了这样的功能?

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

忍者编辑 :如果有人知道如何在汇编/机器代码级别进行此操作,并且可以自己实现,或者您必须依赖系统调用,那就太棒了。 :) 不需要答案,尽管指向正确方向的链接会很好。

最佳答案

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

  • open
  • close
  • read
  • write
  • lseek

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

    还有类似于 UNIX 上的系统调用的 Windows API 调用:
  • CreateFile
  • CloseHandle
  • ReadFile / ReadFileEx
  • WriteFile / WriteFileEx
  • SetFilePointer / SetFilePointerEx

  • Windows 绕过 HANDLE s,类似于文件描述符,但我相信,灵活性稍差一些。 (例如,在 UNIX 上,文件描述符不仅可以表示文件,还可以表示套接字、管道等东西)

    C 标准库函数 fopen , fclose , fread , fwrite , 和 fseek只是这些系统调用的包装器。

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

    忍者编辑的回答:

    您询问了这在机器代码级别是如何工作的。我只能真正解释它在 Linux 和 Intel 32 位架构上的工作原理。当您使用系统调用时,一些参数被放入寄存器。将参数放入寄存器后,中断 0x80被提出。因此,例如,从 stdin 读取 1 KB (文件描述符 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
    
    int 0x80引发操作系统通常会在中断 vector 表或中断描述符表中注册的软件中断。无论如何,处理器将跳转到内存中的特定位置。一旦到达那里,操作系统通常会进入内核模式(如有必要),然后执行等效于 C 的 switcheax .从那里,它将跳转到 read 的实现中。 .在 read ,它通常会从调用进程的文件描述符表中读取一些关于描述符的元数据。一旦它拥有它需要的所有数据,它就会做它的事情,然后返回给用户代码。

    为了“做它的事”,让我们假设它正在从磁盘读取,而不是管道或 stdin或其他一些非物质的地方。我们还假设它正在从主硬盘读取。另外,假设操作系统仍然可以访问 BIOS 中断。

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

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

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

    关于c++ - 文件流实际上是如何工作的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6066774/

    相关文章:

    c++ - 函数A调用函数B,函数B调用函数A,你怎么调用它?

    c - 如何从 Oracle OCI select 查询中获取列类型

    c - 使用 sigaction() 进行 I/O 处理

    c - C 中函数比较器的冒泡排序

    汇编atoi函数

    assembly - OP 代码 0x20 和 0x30 处的 Intel 8080 指令

    debugging - 是否可以在即时窗口中执行汇编语言指令

    C++ VS17 给出与 Linux 子系统中相同代码不同的输出

    c++ - 虚基类的构造函数参数

    c++ - 如果达到结束条件,是否可以在 C++ 中退出 for before time?