持续更新中
持续更新中
持续更新中
一、接口介绍
以下仅介绍常用部分,更详细的,见 man 2 手册
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:打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags
O_RDONLY:只读打开
O_WRONLY:只写打开
O_RDWR:读写打开
O_CREAT:
若文件不存在,先创建(通过参数 mode 设置文件的访问权限,例如 0x664)再打开,需要注意的是最终文件的访问权限是 mode & ~umask,其中,umask 值可通过 umask() 进行设置,
若文件存在,分两种情况:
1、配合 O_EXCL,打开文件报错 File exists
2、没有 O_EXCL,直接打开文件
O_APPEND:追加写,每次调用 write() 写数据前,文件偏移量都会被定位到文件末尾,效果等同于调用了 lseek(),而且文件偏移量的调整和写入操作会作为一个原子步骤执行
O_TRUNC:若文件已存在且为普通文件,并且允许写入(即参数 flags 包含 O_RDWR 或 O_WRONLY),则该文件长度会被截断为 0
注意,O_RDONLY | O_TRUNC 是未定义的,测试 Linux(3.10.0-1160.el7.x86_64) 会被截断
O_CLOEXEC(Since Linux 2.6.23):替代 open + fcntl(fd, F_SETFD, FD_CLOEXEC),保证 “创建 fd + 设置 FD_CLOEXEC” 为原子操作
O_LARGEFILE:64 位的 Linux 用不到
返回值:
成功返回文件描述符
失败返回 -1,并设置错误码
2、write
功能:往文件写数据
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
输入型参数:
fd:open() 返回值
buf:数据缓冲区
count:写入的字节数
返回值:
成功返回写入的字节数
失败返回 -1,并设置错误码
注意:
1、如果文件是以 O_APPEND 标志打开的,则在执行写入操作前,文件偏移量会先被设置到文件末尾,而且文件偏移量的调整和写入操作会作为一个原子步骤执行
2、POSIX 标准要求如果 read() 发生在 write() 返回之后,则该 read() 应读取到最新写入的数据。但并非所有的文件系统都遵守 POSIX 标准
3、write() 成功返回并不保证数据已写入磁盘,要确保数据安全写入,唯一的方法是在写完所有数据后调用 fsync()
3、read
功能:从文件读数据
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
输入型参数:
fd:open() 返回值
buf:数据缓冲区
count:读出的字节数
返回值:
成功返回读出的字节数,返回 0 表示已到达文件末尾
失败返回 -1,并设置错误码
4、close
功能:关闭文件描述符
#include <unistd.h>
int close(int fd);
输入型参数:
fd:open() 返回值
返回值:
成功返回 0
失败返回 -1,并设置错误码
注意:
close() 成功返回也不保证数据已写入磁盘,要确保数据安全写入,唯一的方法是在写完所有数据后调用 fsync()
二、文件描述符

当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件,于是就有了 file 结构体,表示一个已经打开的文件对象
每个进程都有一个 files 指针,指向一张 files_struct 表,该表最重要的部分是 fd_array 指针数组,其中的每个元素都是一个指向打开文件的指针,所以文件描述符本质上就是该数组的下标,只要拿着文件描述符就可以找到对应的文件
注意
Linux 进程默认情况下会有 3 个缺省打开的文件描述符,分别是标准输入(0)、标准输出(1) 和标准错误(2),分别对应的物理设备一般是键盘、显示器和显示器,所以输入/出还可以采用如下方式:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main()
{
char buf[1024];
int rlen;
rlen = read(0, buf, sizeof(buf));
if(rlen > 0)
{
buf[rlen] = 0;
write(1, buf, strlen(buf));
write(2, buf, strlen(buf));
}
return 0;
}
三、文件描述符的分配规则
从 files_struct 数组中选取当前未被使用的最小下标作为新的文件描述符
四、重定向
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
int fd;
close(1);
fd = open("./myfile", O_WRONLY | O_CREAT, 0x644);
if(fd < 0)
{
perror("open");
return 1;
}
printf("fd: %d\n", fd);
fflush(stdout);
close(fd);
return 0;
}
测试发现,本该输出到显示器上的内容,结果输出到了 myfile 文件中,这种现象叫做输出重定向
常见的重定向见下:
- >:输出重定向,将命令的标准输出重定向到指定文件,若文件存在,直接覆盖文件原内容;若文件不存在,自动创建该文件
- command > log 2>&1:将标准输出和标准错误都覆盖写入 log 文件
- >>:输出追加重定向,将命令的标准输出重定向到指定文件,若文件存在,在文件末尾追加新内容;若文件不存在,自动创建该文件
- command >> file 2>&1:将标准输出和标准错误都追加写入 log 文件
- <:输入重定向,将命令的标准输入重定向到指定文件,即命令直接从文件中读取数据,而非等待用户键盘输入
重定向本质

printf 是 C 库中的 IO 函数,一般往 stdout 中输出,而 stdout 是对 fd:1 的二次封装,此时 fd:1 已经指向了 myfile 文件,所以本该输出到显示器上的内容,结果输出到了 myfile 文件中
/usr/include/stdio.h
extern struct _IO_FILE *stdout; /* Standard output stream. */
/usr/include/libio.h
struct _IO_FILE {
...
int _fileno; /* 封装的的文件描述符 */
...
};
五、文件系统
详见理解文件系统
1210

被折叠的 条评论
为什么被折叠?



