【Linux系统】从 C 语言文件操作到系统调用的核心原理
文章目录
- 前言
- lesson 15_基础IO
- 一、共识原理
- 二、回顾C语言接口
- 2.1 文件的打开操作
- 2.2 文件的读取与写入操作
- 2.3 三个标准输入输出流
- 三、过渡到系统,认识文件系统调用
- 3.1 `open` 系统调用
- 1. 比特位标志位示例
- 3.2 `write` 系统调用
- 1. 模拟实现 `w` 选项
- 2. 模拟实现 `a` 选项
- 3.3 `read` 系统调用
- 四、访问文件的本质
- 结语
前言
本文将从文件的基本概念出发,先回顾 C 语言中文件操作的常用接口,再逐步过渡到 Linux 系统调用,解析文件描述符、文件打开对象、进程与文件的关系等关键概念。通过代码示例和原理分析,带你揭开 Linux 基础 IO 的神秘面纱,理解操作系统如何管理文件、进程如何与文件交互的底层逻辑。
lesson 15_基础IO
一、共识原理
-
文件 = 内容 + 属性。
-
文件分为 打开的文件 和 没打开的文件。
-
打开的文件:谁打开的?进程!—— 本质是研究进程和文件的关系。
-
没打开的文件:在哪里放着呢?在磁盘上。我们最关注的问题?没有被打开的文件非常多,文件如何被分门别类的放置好(如何存储) —— 我们要快速的进行增删查改 —— 快速找到文件。
-
文件被打开,必须先被加载到内存!
-
进程:打开的文件 = 1:n。
小结:操作系统内部,一定存在大量的别打开的文件!—— OS 要不要管理这些被打开的文件呢? —— 怎么管理???—— 先描述,在组织。—— 在内核中,一个被打开的文件都必须有自己的文件打开对象,包含文件的很多属性。struct XXX{文件属性;struct XXX *next};
二、回顾C语言接口
2.1 文件的打开操作
-
fopen 函数用于打开文件,格式为
-
FILE *fopen(const char *path, const char *mode);
-
path: 文件路径或文件名。如果只有文件名,操作系统会在当前工作目录(cwd)下查找该文件。
-
mode: 文件打开模式。常见模式有:
- w: 如果文件已存在,先清空文件再写入。如果文件不存在,创建新文件。
- a: 以追加模式打开文件,在文件末尾添加内容。
-
当前路径 (cwd): 每个进程维护一个当前工作目录,操作系统会根据该目录来查找文件。如果路径没有指定,fopen 会使用进程的当前工作路径。
-
2.2 文件的读取与写入操作
-
fwrite 用于向文件写入数据。其函数声明为:
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
- ptr: 指向要写入数据的指针。
- size: 每个写入对象的大小。
- nmemb: 要写入的对象个数。
- stream:文件流指针
-
举例使用:
int main() { FILE *fp = fopen("log.txt", "w"); if (fp == NULL) { perror("fopen"); return errno; } char* str = "Hello Linux!"; fwrite(str, strlen(str), 1, fp); fclose(fp); return 0; }
-
fwrite的第二个参数是指每个写入对象的大小,strlen函数返回的值是不包含字符串结束标识符,那么我们传参是加一还是不加一呢?加一就代表把\0写入到文件中,那么我们是应该怎么选择呢?这里不妨试一试加一的结果:
注意 log.txt 文件中,字符串的末尾有一个^@,是什么意思呢?实际上这个字符组合是表示\0的ASCII码,所以写入字符串时,使用 strlen 计算字符串长度时,不包括结束符 \0。通常不需要将 \0 写入文件,因为它是 C 语言中的结束标志,而在其它语言中读取文件时,可能不希望看到这些无关的字符。
2.3 三个标准输入输出流
C 程序启动时,会自动打开以下三个标准流:
- stdin: 标准输入流(通常与键盘连接)。
- stdout: 标准输出流(通常与显示器连接)。
- stderr: 标准错误流(通常与显示器连接)。
这三个流都由操作系统和 C 标准库提供,并用于处理程序与外部交互的基本输入输出。
三、过渡到系统,认识文件系统调用
文件其实是在磁盘上的,磁盘是外部设备,访问磁盘文件其实是访问硬件!几乎所有的库只要是访问硬件设备,必定要封装系统调用。
3.1 open 系统调用
open 是一个用于打开文件或创建文件的系统调用,其原型为:
#include #include #include 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_CREAT:文件不存在时创建文件。
- O_TRUNC:打开文件时清空文件内容。
- O_APPEND:以追加模式打开文件。
- mode: 在使用 O_CREAT 时,需要指定新文件的访问权限。
- 返回值:成功返回文件描述符,失败返回 -1。
1. 比特位标志位示例
通过按位或(|)传递多个标志位,可以在同一次调用中同时指定多个选项。
代码示例:
#define ONE (1 if(flags & ONE) printf("function1\n"); if(flags & TWO) printf("function2\n"); if(flags & FOUR) printf("function3\n"); if(flags & EIGHT) printf("function4\n"); return; } int main() { printf("--------------------------------------\n"); show(ONE); printf("--------------------------------------\n"); show(ONE | TWO); printf("--------------------------------------\n"); show(ONE | TWO | FOUR ); printf("--------------------------------------\n"); show(ONE | TWO | FOUR | EIGHT); printf("--------------------------------------\n"); return 0; } umask(0); // 将权限掩码设置成0000 int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666); if(fd
- 参数说明:
-
-
-
-
-