# 文件，第 1 部分：使用文件

> 原文：<https://github.com/angrave/SystemProgramming/wiki/Files%2C-Part-1%3A-Working-with-files>

## 两种类型的文件

在 linux 上，有两个带文件的抽象。第一个是 linux `fd`级抽象，意味着你可以使用

*   `open`
*   `read`
*   `write`
*   `close`
*   `lseek`
*   `fcntl` ......

等等。 linux 界面非常强大且富有表现力，但有时我们需要可移植性（例如，如果我们正在为 mac 或 windows 编写）。这就是 C 的抽象发挥作用的地方。在不同的操作系统上，C 使用低级函数来创建可以在任何地方使用的文件的包装器，这意味着 Linux 上的 C 使用上述调用。 C 有以下几点

*   `fopen`
*   `fread`或`fgetc/fgets`或`fscanf`
*   `fwrite`或`fprintf`
*   `fclose`
*   `fflush`

但是你没有得到 linux 给你系统调用的表达能力，你可以使用`int fileno(FILE* stream)`和`FILE* fdopen(int fd...)`在它们之间来回转换。

需要注意的另一个重要方面是 C 文件是**缓冲**，这意味着默认情况下可能无法写入内容。您可以使用 C 选项更改它。

## 如何判断文件有多大？

对于小于 long 的文件，使用 fseek 和 ftell 是一种简单的方法来完成此任务：

移动到文件末尾并找出当前位置。

```c
fseek(f, 0, SEEK_END);
long pos = ftell(f);
```

这告诉我们文件中的当前位置（以字节为单位） - 即文件的长度！

`fseek`也可用于设置绝对位置。

```c
fseek(f, 0, SEEK_SET); // Move to the start of the file 
fseek(f, posn, SEEK_SET);  // Move to 'posn' in the file.
```

父进程或子进程中的所有未来读取和写入都将遵循此位置。注意从文件中写入或读取将改变当前位置。

有关更多信息，请参见 fseek 和 ftell 的手册页。

## 但尽量不要这样做

**注意：由于 C 语言**的怪癖，在通常情况下不建议这样做。这个怪癖是多头只需要 **4 字节大**意味着 ftell 可以返回的最大大小略低于 2 千兆字节（我们现在知道我们的文件可能是几百千兆字节甚至太字节数分布式文件系统）。我们该怎么做呢？使用`stat`！我们将在后面的部分介绍 stat，但这里有一些代码可以告诉你文件的大小

```c
struct stat buf;
if(stat(filename, &buf) != -1){
    return -1;
}
return (ssize_t)buf.st_size;
```

buf.st_size 的类型为 off_t，足够 _ 疯狂 _ 大文件。

## 如果子进程使用`fclose`或`close`关闭文件流会发生什么？

关闭文件流对每个进程都是唯一的。其他进程可以继续使用自己的文件句柄。请记住，在创建子项时，甚至文件的相对位置都会复制所有内容。

## mmap for files 怎么样？

mmap 的一般用途是将文件映射到内存。这并不意味着文件立即被 malloc'ed 到内存中。以下面的代码为例。

```
int fd = open(...); //File is 2 Pages
char* addr = mmap(..fd..);
addr[0] = 'l'; 
```

内核可能会说，“好吧，我看到你想把文件映射到内存中，所以我会在你的地址空间中预留一些空间，即文件的长度”。这意味着当您写入 addr [0]时，您实际上正在写入文件的第一个字节。内核实际上也可以做一些优化。它不是将文件加载到内存中，而是一次只能加载页面，因为如果文件是 1024 页;您只能访问 3 或 4 页，这使得加载整个文件浪费时间（这就是页面错误如此强大的原因！它们让操作系统控制您使用文件的程度）。

## 对于每个 mmap

请记住，一旦完成`mmap` ping 您`munmap`告诉操作系统您不再使用已分配的页面，因此操作系统可以将其写回磁盘并在需要时将地址返回给您 malloc 以后。