接触过 Linux 的同学都知道,在 Linux 中一切皆文件。所有的文件都是通过"文件 IO"来操作的,所以这很重要。
那么 Linux 的文件都是从哪里来的呢?
访问文件
通用 IO 模型:
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);
参数含义:
- pathname:
指向想要打开的文件路径名或者文件名。文件路径名是文件的绝对位置,文件名是当前路径下的文件。 - flags:
表示打开文件要进行的操作,必须指定一下三种中的一种,且只允许指定一个。
- O_RDONLY: 只读模式
- O_WRONLY:只写模式
- O_RDWR:可读可写
可选参数: - O_APPEND:表示追加,如果原来文件里面有内容,则这次写入会写在文件的最末尾。
- O_CREAT:表示如果指定文件不存在,则创建这个文件
- O_EXCL:表示如果要创建的文件已存在,则出错,同时返回 -1,并且修改 errno 的值。
- O_TRUNC:表示截断,如果文件存在,并且以只写、读写方式打开,则将其长度截断为0。
- O_NOCTTY:如果路径名指向终端设备,不要把这个设备用作控制终端。
- O_NONBLOCK:如果路径名指向 FIFO/块文件/字符文件,则把文件的打开和后继 I/O 设置为非阻塞模式。
- O_DSYNC 等待物理 I/O 结束后再 write。在不影响读取新写入的数据的前提下,不等待文件属性更新。
- O_RSYNC read 等待所有写入同一区域的写操作完成后再进行
- O_SYNC 等待物理 I/O 结束后再 write,包括更新文件属性的 I/O
- mode:
当只有第二个参数 O_CREAT 时才有作用,表示创建的文件的权限初始值。和用户掩码 umask 有关。 - 返回值:
返回文件描述符。
read
从文件读取 n 字节数据到数据区中。
头文件:
#include <unistd.h>
函数原型:
ssize_t read(int fd, void *buf, size_t count);
参数含义:
- fd:
要读取的文件的文件描述符。 - buf:
读取到的字符串存入的地方。 - count:
要读取的字节数。 - 返回值:
返回成功读取到的字节数。
write
从文件写入 n 直接数据。
头文件:
#include <unistd.h>
函数原型:
ssize_t write(int fd, const void *buf, size_t count);
参数含义:
- fd:
要写入的文件的文件描述符。 - buf:
要写入的数据。 - count:
要写入的字节数。 - 返回值:
成功写入的字节数。
lseek
移动文件中光标的位置。
头文件:
#include <sys/types.h>
#include <unistd.h>
函数原型:
off_t lseek(int fd, off_t offset, int whence);
参数含义:
- fd:
文件描述符。 - offset:
位移量。 - whence:
配合 offset 设置
- SEEK_SET:参数 offset 即为新的位置。
- SEEK_CUR:在目前的位置向前后移动 offset 个位移量,可以为负数。
- SEEK_END:在文件末尾的位置向前或移动 offset 个位移量,可以为负数。
- 返回值:
文件中光标的位置距离文件开头的字节数。
close
关闭文件。
头文件:
#include <unistd.h>
函数原型:
int close(int fd);
关闭文件描述符为 fd 的文件,成功返回 0,失败返回-1。
系统调用函数进入内核过程
内核的 sys_open 、sys_read 会做什么?
从图中可以看出,进入内核后,sys_read/open 会首先根据参数判断文件的类型,然后根据不同的文件类型去找不同的设备驱动,继而进行读写或者输入输出控制。