【Linux】基础IO导论——从C文件接口到系统调用初识
目录
1. 引言
文件I/O是操作系统中最基础、最重要的功能之一。无论是读写普通文件,还是与键盘、显示器等设备交互,都离不开I/O操作。本文将从“文件”的本质出发,复习C语言标准库中的文件操作接口,并引出系统调用接口,为后续深入理解文件描述符、重定向、缓冲区等概念打下基础。
2. 理解“文件”
2.1 狭义理解
-
文件存储在磁盘上,磁盘是永久性存储介质,因此文件具有持久性。
-
磁盘属于外设(既是输入设备也是输出设备),对文件的所有操作本质是对外设的输入和输出,简称 I/O。
2.2 广义理解
Linux 下“一切皆文件”:键盘、显示器、网卡、磁盘等设备都被抽象为文件。这种抽象使得用户可以使用统一的接口(如 open、read、write)来访问不同类型的资源。
2.3 文件操作的归类认知
-
即使是0KB的空文件,也占用磁盘空间(至少存储元数据)。
-
文件 = 属性(元数据)+ 内容。文件操作分为内容操作(读写数据)和属性操作(修改权限、大小等)。
2.4 系统角度
-
对文件的操作本质是进程对文件的操作。
-
磁盘的管理者是操作系统,因此任何对文件的访问最终都必须通过操作系统提供的系统调用完成。C库函数只是对系统调用的封装,方便用户使用。
3. 回顾C文件接口
3.1 打开文件:fopen
c
#include <stdio.h>
FILE *fp = fopen("myfile", "w");
-
若文件不存在,
"w"模式会创建新文件。 -
打开的文件位于当前工作目录。可以通过
/proc/[pid]/cwd查看进程的当前路径。
3.2 写文件示例
c
const char *msg = "hello bit!\n";
int count = 5;
while (count--) {
fwrite(msg, strlen(msg), 1, fp);
}
3.3 读文件示例
c
char buf[1024];
ssize_t s = fread(buf, 1, strlen(msg), fp);
if (s > 0) {
buf[s] = 0;
printf("%s", buf);
}
利用此模式可以实现简单的 cat 命令:通过命令行参数传入文件名,打开并读取内容输出到标准输出。
3.4 输出到显示器的多种方法
c
fwrite(msg, strlen(msg), 1, stdout);
printf("hello printf\n");
fprintf(stdout, "hello fprintf\n");
-
stdin、stdout、stderr是C语言默认打开的三个流,类型均为FILE*。
3.5 文件打开方式(mode)
| 模式 | 含义 |
|---|---|
"r" |
只读,流位于文件开头 |
"r+" |
读写,流位于文件开头 |
"w" |
只写,文件不存在则创建,存在则截断为0 |
"w+" |
读写,创建或截断 |
"a" |
追加,文件不存在则创建,流位于文件末尾 |
"a+" |
读+追加,读从开头,写总是追加到末尾 |
补充:
"b"可用于二进制模式(如"wb"),在Linux中文本与二进制无区别,但为了可移植性建议明确指定。
4. 从C接口到系统调用
C文件接口(fopen、fread、fwrite、fclose)是库函数,它们底层依赖系统调用(open、read、write、close)。理解系统调用是掌握文件I/O的关键。
在进入系统调用之前,我们先了解一种常见的标志位传递技术,这在后续的 open 函数中会用到:
c
#define ONE 0001 // 二进制 0001
#define TWO 0002 // 0010
#define THREE 0004 // 0100
void func(int flags) {
if (flags & ONE) printf("ONE ");
if (flags & TWO) printf("TWO ");
if (flags & THREE) printf("THREE");
}
// 调用示例:
func(ONE | TWO); // 输出 "ONE TWO"
通过按位或组合多个标志,可以在一个整型参数中传递多个选项,这是Linux系统调用中非常常见的设计模式。
5. 小结
本文回顾了C语言中的文件操作接口,并引出文件的基本概念和系统调用的重要性。下一篇将正式介绍系统级I/O接口:open、read、write、close,并深入探讨文件描述符及其分配规则。
更多推荐




所有评论(0)