3

Android C++系列:Linux文件IO操作(一) - InfoQ 写作平台

 2 years ago
source link: https://xie.infoq.cn/article/5a00aaeae9ea3e3202d1a9282
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

1.1 C 标准函数与系统函数的区别

81d15d83c01fe9d1bfa7f92bfcee432e.png

1.1.1 I/O 缓冲区

每一个 FILE 文件流都有一个缓冲区 buffer,默认大小 8192Byte。

1.1.2 效率

1.1.3 程序的跨平台性

事实上 Unbuffered I/O 这个名词是有些误导的,虽然 write 系统调用位于 C 标准库 I/O 缓 冲区的底层,但在 write 的底层也可以分配一个内核 I/O 缓冲区,所以 write 也不一定是直接 写到文件的,也可能写到内核 I/O 缓冲区中,至于究竟写到了文件中还是内核缓冲区中对于 进程来说是没有差别的,如果进程 A 和进程 B 打开同一文件,进程 A 写到内核 I/O 缓冲区中的数 据从进程 B 也能读到,而 C 标准库的 I/O 缓冲区则不具有这一特性(想一想为什么)。

1.2 PCB 概念

1.2.1 task_struct 结构体

    /usr/src/linux-headers/include/linux/sched.h

1.2.2 files_struct 结构体

1.3 open/close

49b84d0a87075e40de1383c71a503c89.png

1.3.1 文件描述符

一个进程默认打开 3 个文件描述符

STDIN_FILENO 0 STDOUT_FILENO 1 STDERR_FILENO 2

新打开文件返回文件描述符表中未使用的最小文件描述符。 open 函数可以打开或创建一个文件。

#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int open(const char *pathname, int flags);int open(const char *pathname, int flags, mode_t mode); 返回值:成功返回新分配的文件描述符,出错返回-1并设置errno

在 Man Page 中 open 函数有两种形式,一种带两个参数,一种带三个参数,其实在 C 代码 中 open 函数是这样声明的:

 int open(const char *pathname, int flags, ...);

最后的可变参数可以是 0 个或 1 个,由 flags 参数中的标志位决定,见下面的详细说明。

pathname 参数是要打开或创建的文件名,和 fopen 一样,pathname 既可以是相对路径也 可以是绝对路径。flags 参数有一系列常数值可供选择,可以同时选择多个常数用按位或运 算符连接起来,所以这些常数的宏定义都以 O_开头,表示 or。

  必选项:以下三个常数中必须指定一个,且仅允许指定一个。

* O_RDONLY 只读打开* O_WRONLY 只写打开* O_RDWR 可读可写打开

以下可选项可以同时指定 0 个或多个,和必选项按位或起来作为 flags 参数。可选项有很多, 这里只介绍一部分,其它选项可参考 open(2)的 Man Page:

  • * O_APPEND 表示追加。如果文件已有内容,这次打开文件所写的数据附加到文件的末尾 而不覆盖原来的内容。

  • * O_CREAT 若此文件不存在则创建它。使用此选项时需要提供第三个参数 mode,表示该 文件的访问权限。

  • * O_EXCL 如果同时指定了 O_CREAT,并且文件已存在,则出错返回。

  • * O_TRUNC 如果文件已存在,并且以只写或可读可写方式打开,则将其长度截断(Trun-

  • cate)为 0 字节。

  • * O_NONBLOCK 对于设备文件,以 O_NONBLOCK 方式打开可以做非阻塞 I/O(Nonblock I/

  • O),非阻塞 I/O 在下一节详细讲解。

  • 注意 open 函数与 C 标准 I/O 库的 fopen 函数有些细微的区别: 以可写的方式 fopen 一个文件时,如果文件不存在会自动创建,而 open 一个文件时必须

  • 明确指定 O_CREAT 才会创建文件,否则文件不存在就出错返回。 以 w 或 w+方式 fopen 一个文件时,如果文件已存在就截断为 0 字节,而 open 一个文件时必

  • 须明确指定 O_TRUNC 才会截断文件,否则直接在原来的数据上改写。 第三个参数 mode 指定文件权限,可以用八进制数表示,比如 0644 表示-rw-r-r–,也可

  • 以用 S_IRUSR、S_IWUSR 等宏定义按位或起来表示,详见 open(2)的 Man Page。要注意的是, 文件权限由 open 的 mode 参数和当前进程的 umask 掩码共同决定。

  • 补充说明一下 Shell 的 umask 命令。Shell 进程的 umask 掩码可以用 umask 命令查看:

  $ umask   0002

用 touch 命令创建一个文件时,创建权限是 0666,而 touch 进程继承了 Shell 进程的 umask 掩码,所以最终的文件权限是 0666&∼022=0644。

  $ touch file123  $ ls -l file123  -rw-rw-r-- 1 qingkouwei qingkouwei 0 9月 11 23:48 file123

同样道理,用 gcc 编译生成一个可执行文件时,创建权限是 0777,而最终的文件权限是

0777 & ∼022 = 0755。

  ubuntu:~$ umask  0002  ubuntu:~$ gcc main.c  ubuntu:~$ ls -l a.out  -rwxrwxr-x 1 qingkouwei qingkouwei 7158 9月 11 23:51 a.out

我们看到的都是被 umask 掩码修改之后的权限,那么如何证明 touch 或 gcc 创建文件的权 限本来应该是 0666 和 0777 呢?我们可以把 Shell 进程的 umask 改成 0,再重复上述实验:

  $ rm file123 a.out  $ umask 0  $ touch file123  $ ls -l file123  -rw-rw-rw- 1 qingkouwei qingkouwei $ gcc main.c  0 9月 11 23:52 file123  $ ls -l a.out  -rwxrwxr-x 1 qingkouwei qingkouwei 7158 9月 11 23:52 a.out

现在我们自己写一个程序,在其中调用 open(“somefile”, O_WRONLY | O_CREAT, 0664);创建文件,然后在 Shell 中运行并查看结果:

close 函数关闭一个已打开的文件:

  #include <unistd.h>  int close(int fd); 返回值:成功返回0,出错返回-1并设置errno

参数 fd 是要关闭的文件描述符。需要说明的是,当一个进程终止时,内核对该进程所有 尚未关闭的文件描述符调用 close 关闭,所以即使用户程序不调用 close,在终止时内核也会 自动关闭它打开的所有文件。但是对于一个长年累月运行的程序(比如网络服务器),打开 的文件描述符一定要记得关闭,否则随着打开的文件越来越多,会占用大量文件描述符和系 统资源。

由 open 返回的文件描述符一定是该进程尚未使用的最小描述符。由于程序启动时自动打 开文件描述符 0、1、2,因此第一次调用 open 打开文件通常会返回描述符 3,再调用 open 就会 返回 4。可以利用这一点在标准输入、标准输出或标准错误输出上打开一个新文件,实现重 定向的功能。例如,首先调用 close 关闭文件描述符 1,然后调用 open 打开一个常规文件, 则一定会返回文件描述符 1,这时候标准输出就不再是终端,而是一个常规文件了,再调用 printf 就不会打印到屏幕上,而是写到这个文件中了。后面要讲的 dup2 函数提供了另外一种 办法在指定的文件描述符上打开文件。

1.3.2 最大打开文件个数

查看当前系统允许打开最大文件个数

 cat /proc/sys/fs/file-max

当前默认设置最大打开文件个数 1024

 ulimit -a

修改默认设置最大打开文件个数为 4096

ulimit -n 4096

1.4 总结

文本介绍了 Linux 下的文件操作命令、系统调用、API 接口等。并介绍了 C 标准函数与系统函数的区别,PCB 概念等。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK