# 10.3 打开和关闭文件

进程是通过调用 open 函数来打开一个已存在的文件或者创建一个新文件的：

```c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(char *filename, int flags, mode_t mode);

// 返回：若成功则为新文件描述符，若出错为 -1。
```

open 函数将 filename 转换为一个文件描述符，并且返回描述符数字。返回的描述符总是在进程中当前没有打开的最小描述符。flags 参数指明了进程打算如何访问这个文件：

* O\_RDONLY：只读。
* O\_WRONLY：只写。
* O\_RDWR：可读可写。

例如，下面的代码说明如何以读的方式打开一个已存在的文件：

```c
fd = Open("foo.txt", O_RDONLY, 0);
```

flags 参数也可以是一个或者更多位掩码的或，为写提供给一些额外的指示：

* O\_CREAT：如果文件不存在，就创建它的一个**截断的**（truncated）（空）文件。
* O\_TRUNC：如果文件已经存在，就截断它。
* O\_APPEND：在每次写操作前，设置文件位置到文件的结尾处。

例如，下面的代码说明的是如何打开一个已存在文件，并在后面添加一些数据：

```c
fd = Open("foo.txt", O_WRONLY|O_APPEND, 0);
```

mode 参数指定了新文件的访问权限位。这些位的符号名字如图 10-2 所示。

| 掩码       | 描述                |
| -------- | ----------------- |
| S\_IRUSR | 使用者（拥有者）能够读这个文件   |
| S\_IWUSR | 使用者（拥有者）能够写这个文件   |
| S\_IXUSR | 使用者（拥有者）能够执行这个文件  |
| S\_IRGRP | 拥有者所在组的成员能够读这个文件  |
| S\_IWGRP | 拥有者所在组的成员能够写这个文件  |
| S\_IXGRP | 拥有者所在组的成员能够执行这个文件 |
| S\_IROTH | 其他人（任何人）能够读这个文件   |
| S\_IWOTH | 其他人（任何人）能够写这个文件   |
| S\_IXOTH | 其他人（任何人）能够执行这个文件  |

> 图 10-2 访问权限位。在 sys/stat.h 中定义

作为上下文的一部分，每个进程都有一个 umask，它是通过调用 umask 函数来设置的。当进程通过带某个 mode 参数的 open 函数调用来创建一个新文件时，文件的访问权限位被设置为 **mode & \~umask**。例如，假设我们给定下面的 mode 和 umask 默认值：

```c
#define DEF_MODE   S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH
#define DEF_UMASK  S_IWGRP|S_IWOTH
```

接下来，下面的代码片段创建一个新文件，文件的拥有者有读写权限，而所有其他的用户都有读权限：

```c
umask(DEF_UMASK);
fd = Open("foo.txt", O_CREAT|O_TRUNC|O_WRONLY, DEF_MODE);
```

最后，进程通过调用 close 函数关闭一个打开的文件。

```c
#include <unistd.h>

int close(int fd);

// 返回：若成功则为 0，若出错则为 -1。
```

关闭一个已关闭的描述符会出错。

### 练习题 10.1

{% tabs %}
{% tab title="练习题 10.1" %}
下面程序的输出是什么？

```c
#include "csapp.h"

int main()
{
    int fd1, fd2;

    fd1 = Open("foo.txt", O_RDONLY, 0);
    Close(fd1);
    fd2 = Open("baz.txt", O_RDONLY, 0);
    printf("fd2 = %d\n", fd2);
    exit(0);
}
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="答案" %}
Unix 进程生命周期开始时，打开的描述符赋给了 stdin（描述符 0）、stdout（描述符 1）和 stderr（描述符 2）。open 函数总是返回最低的未打开的描述符，所以第一次调用。pen 会返回描述符 3。调用 close 函数会释放描述符 3。最后对 open 的调用会返回描述符 3，因此程序的输出是 “fd2=3”。
{% endtab %}
{% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://hansimov.gitbook.io/csapp/part3/ch10-system-level-io/10.3-opening-and-closing-files.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
