目录
一. 系统调用 I/O
1. 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: 代表文件打开方式.
mode: 创建文件时, 创建文件的权限. -
返回值:
若打开或者创建成功, 返回一个整型值 文件描述符 (file descriptor); 若失败, 返回 -1 并设置错误码.
flags
flags 也被称作标记位, 和 waitpid() 的 status 参数一样, 都是通过位图传参, 按照比特位来区分打开方式.
参数 | 含义 |
---|---|
O_RDONLY | 以只读的方式打开文件 |
O_WRNOLY | 以只写的方式打开文件 |
O_APPEND | 以追加的方式打开文件 |
O_RDWR | 以读写的方式打开文件 |
O_CREAT | 当目标文件不存在时,创建文件 |
例:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fd = open("test.c", O_RDONLY); //以只读方式打开 test.c 文件
if (fd < 0)
return 1;
char s[1024];
ssize_t n = read(fd, s, 1024); //读取内容
write(1, s, n); //打印内容
return 0;
}
mode
使用 open() 函数创建文件时, 必须指明文件权限. 所以当 文件打开方式包含创建文件时, 需要设置mode参数.
例:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
umask(0); //权限掩码置0
int fd = open("log.txt", O_WRONLY | O_CREAT, 0666); //以只写方式打开 test.c 文件, 若文件不存在则创建文件
if (fd < 0)
return 1;
return 0;
}
2. close()
- 关闭一个文件描述符
#include <unistd.h>
int close(int fd);
- 参数 fd: open() 函数的返回值, 文件描述符.
例:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
umask(0); //权限掩码置0
int fd = open("log.txt", O_WRONLY | O_CREAT, 0666); //以只写方式打开 test.c 文件, 若文件不存在则创建文件
if (fd < 0)
return 1;
close(fd); //关闭文件
return 0;
}
3. write()
向文件描述符指向的文件中写入.
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
-
参数
fd: open() 函数的返回值, 文件描述符.
buf: 要写入的缓冲区.
count: 需要写入的字节数. -
返回值:
若写入成功, 返回写入数据的字节个数; 若失败, 返回 -1 并设置错误码.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
umask(0); //权限掩码置0
int fd = open("log.txt", O_WRONLY | O_CREAT, 0666); //以只写方式打开 test.c 文件, 若文件不存在则创建文件
if (fd < 0)
return 1;
char s[] = "11111\n";
write(fd, s, strlen(s));
close(fd);
return 0;
}
4. read()
向文件描述符指向的文件中读取.
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
-
参数
fd: open() 函数的返回值, 文件描述符.
buf: 保存读取内容的缓冲区.
count: 请求读取的字节数. -
返回值
若读取成功, 返回读取数据的字节个数; 若失败, 返回 -1 并设置错误码; 若在调 read() 之前已到达文件末尾, 则这次 read() 返回 0.
二. 文件描述符(fd)
1. 内存中的文件
在操作系统看来, 无论是在磁盘中存储的程序, 软件, 还是磁盘或键盘等硬件, 实际上都是文件, 不过文件的内容和属性, 操作方式不同.
当操作系统将文件加载至内存时, 文件就被描述为了包含类型, 状态等属性的 struct file 结构体.
//描述文件
struct file
{
//文件的各种属性
int types; //文件的类型
int status; //文件的状态
int (*writep)(...); //函数指针,指向读函数
int (*readp)(...); //函数指针,指向写函数
struct file* next; //下一个file对象的地址
...
}
Linux 这种使用方法集屏蔽底层差异的 struct file 结构体这一层称作 虚拟文件系统 vfs, 使得文件在上层使用统一的文件接口的方式进行操作.
2. 文件描述符(fd)
为了使一个进程可以打开多个文件, 会在 task_struct 结构体中包含 struct files_struct *file 的指针, 指向当前进程的 struct files_struct 结构体, 在 files_struct 中的 file* fd_array 指针数组会包含当前进程打开或从父进程继承下来的文件的地址.
文件描述符(fd)正是 struct file *fd_array[] 的结构体指针数组的下标.
是 open() 函数调用成功的返回值, 所以使用系统的 I/O 接口都需要 fd 参数.
浅显理解为:
每当 open() 函数新打开文件时, 操作系统会创建新的 struct file 结构, 进行文件管理, 会在 struct file *fd_array[] 的数组中保存一份地址, 最后返回数组下标.
文件描述符(fd)默认是从 3 开始. 0, 1, 2下标的地址分别指向标准输入(键盘), 标准输出(显示器), 标准错误(显示器), 是系统默认打开的.
但若将关闭 0 后, 那么文件描述符(fd)的就会从 0 位置开始, 实际上文件描述符(fd)是从最小的, 没有存储信息的一位下标开始.
3. dup2()
#include <unistd.h>
int dup2(int oldfd, int newfd);
将 oldfd 拷贝给 newfd, 必要时会先关闭 newfd. 函数只会保留 oldfd 的值.
4. 重定向
进程在进行 I/O 的时, 默认只会根据标准输入(stdin), 标准输出(stdout), 标准错误(stderr)对应的文件描述符(fd)进行操作;
也就是进程只会寻找在 struct file *fd_array[] 指针数组中 0,1,2 下标位置存放的文件指针, 对该指针指向的文件进行输入, 输出操作; 上层所使用的 printf(), scanf() 等函数中也封装了文件描述符(fd).
重定向指 将 struct file *fd_array[] 指针数组中原有的文件指针替换, 在内核更改了数组下标指向的内容, 而上层依然使用原有的下标, 改变所操作的文件.
可以在文件打开之前将需要重定向的下标关闭, 再打开文件, 可以达到重定向的效果.
也可以使用 dup2() 函数实现重定向.