爆笑教程《看表情包学Linux》👈 猛戳订阅!
💭 写在前面:通过上一章节的讲解,想必大家已对文件系统基本的接口有一个简单的了解,本章我们将继续深入讲解,继续学习系统传递标志位,介绍 O_WRONLY, O_TRUNC, O_APPEND 和 O_RDONLY。之后我们就正是打开文件描述符 fd 的大门了,之前我们所探讨讲解的系统文件操作,都是为了给文件描述符做铺垫的,可见这这一块知识点是相当的重要。话不多说,让我们正式开始本章的学习!
📜 本章目录:
Ⅰ. 系统传递标记位
0x00 引入:O_WRONLY 没有像 w 那样完全覆盖?
0x01 O_TRUNC 截断清空(对标 w)
0x02 O_APPEND 追加(对标 a)
0x03 O_REONLY 读取
Ⅱ. 文件描述符(fd)
0x00 引入:open 参数的返回值
0x01 文件描述符的底层理解
0x02 理解:Linux 下一切皆文件
0x03 初识 VFS(虚拟文件系统)
0x04 回头看问题:fd 的 0,1,2,3...
未上榜
Ⅰ. 系统传递标记位
💬 代码演示:当前我们的 log.txt 内有 5 行数据,现在我们执行下面的代码:
int main(void)
{
umask(0);
// 当我们只有 O_WRONLY 和 O_CREAT 时
int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);
if (fd < 0) {
perror("open");
return 1;
}
printf("fd: %d\n", fd);
// 修改:向文件写入 2 行信息
int cnt = 0;
const char* str = "666\n"; // 修改:内容改成666(方便辨识)
while (cnt < 2) {
write(fd, str, strlen(str));
cnt++;
}
close(fd);
return 0;
}
🚩 运行结果如下:
0x01 O_TRUNC 截断清空(对标 w)
在我们打开文件时,如果带上 O_TRUNC
如果文件存在,并且打开是为了写入,O_TRUNC 会将该文件长度缩短 (truncated) 为 0。
也就是所谓的 截断清空 (Truncate Empty) ,我们默认情况下文件系统调用接口不会清空文件的,
但如果你想清空,就需要给 open() 接口 带上 O_TRUNC 选项:
💬 代码演示:让 open() 达到 fopen 中 "w"
int main(void)
{
umask(0);
int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (fd < 0) {
perror("open");
return 1;
}
printf("fd: %d\n", fd);
// 向文件写入 2 行信息
int cnt = 0;
const char* str = "666\n";
while (cnt < 2) {
write(fd, str, strlen(str));
cnt++;
}
close(fd);
return 0;
}
🚩 运行结果如下:
int fd = open("log.txt", O_WRONLY | O_CREATE | O_APPEND, 0666);
💬 代码演示:让 open() 达到 fopen 中 "a"
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(void)
{
umask(0);
int fd = open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);
if (fd < 0) {
perror("open");
return 1;
}
printf("fd: %d\n", fd);
// 向文件写入 2 行信息
int cnt = 0;
const char* str = "666\n";
while (cnt < 2) {
write(fd, str, strlen(str));
cnt++;
}
close(fd);
return 0;
}
🚩 运行结果如下:
我们再来对照
语言的 fopen,想做到这样的效果只需要一个 "a" :
open("log.txt", O_WRONLY | O_CREAT | O_APPEND, 0666);
fopen("log.txt", "a");
实际上,系统级别的接口本来就是被文件接口封装的,fopen 是系统级文件接口的底层实现。
我们的 a, w, r... 在底层上实际上就是这些 "O_" 组合而成的,使用系统接口麻烦吗?
当然麻烦!要记这么多东西,当然还是 C 语言用的更爽了,一个字母标明文件模式就行了。
0x03 O_REONLY 读取
如果我们想读取一个文件,那么这个文件肯定是存在的,我们传 O_RDONLY 选项:
int main()
{
umask(0);
int fd = open("log.txt", O_RDONLY);
if (fd < 0) {
perror("open");
return 1;
}
printf("fd: %d\n", fd);
char buffer[128];
ssize_t s = read(fd, buffer, sizeof(buffer) - 1);
if (s > 0) {
buffer[s] = '\0'; // 最后字符串序列设置为 '\0'
printf("%s", buffer);
}
close(fd);
return 0;
}
🚩 运行结果如下:
Ⅱ. 文件描述符(fd)
0x00 引入:open 参数的返回值
int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);
我们使用 open 函数举的例子中,一直是用一个叫做 fd
fopen 中我们习惯使用 fp / pf 接收返回值,那是因为是 fopen 的返回值 FILE*
file pointer 的缩写即是 fp,所以我们就习惯将这个接收 fopen 返回值的变量取名为 fp / pf。
那为什么接收 open 的返回值的变量要叫 fd 呢?
这个 fd 究竟是何方神圣?我们现在就揭开其神秘面纱,一睹芳容!它就是……
open 如果调用成功会返回一个新的 文件描述符 (file descriptor) ,如果失败会返回 -1 。
- :失败 (success)
- :成功 (failed)
💬 代码演示:我们现在多打开几个文件,观察 fd
int main(void)
{
int fd_1 = open("log1.txt", O_WRONLY | O_CREAT, 0666);
int fd_2 = open("log2.txt", O_WRONLY | O_CREAT, 0666);
int fd_3 = open("log3.txt", O_WRONLY | O_CREAT, 0666);
int fd_4 = open("log4.txt", O_WRONLY | O_CREAT, 0666);
int fd_5 = open("log5.txt", O_WRONLY | O_CREAT, 0666);
printf("fd_1: %d\n", fd_1);
printf("fd_2: %d\n", fd_2);
printf("fd_3: %d\n", fd_3);
printf("fd_4: %d\n", fd_4);
printf("fd_5: %d\n", fd_5);
close(fd_1);
close(fd_2);
close(fd_3);
close(fd_4);
close(fd_5);
return 0;
}
🚩 运行结果如下:
📌 [ 笔者 ] 王亦优
📃 [ 更新 ] 2023.3.24
❌ [ 勘误 ] /* 暂无 */
📜 [ 声明 ] 由于作者水平有限,本文有错误和不准确之处在所难免,
本人也很想知道这些错误,恳望读者批评指正!
📜 参考资料 C++reference[EB/OL]. []. http://www.cplusplus.com/reference/. Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. . 百度百科[EB/OL]. []. https://baike.baidu.com/. |