文章目录
- 前言
-
- [1. I/O 的分类与基础概念](#1. I/O 的分类与基础概念)
-
- [文件与 I/O 的基本概念](#文件与 I/O 的基本概念)
- [2. 标准库 I/O 接口](#2. 标准库 I/O 接口)
-
- [2.1 文件操作](#2.1 文件操作)
- [2.2 数据读写](#2.2 数据读写)
- [2.3 文件定位](#2.3 文件定位)
- [3. 系统调用 I/O 接口](#3. 系统调用 I/O 接口)
-
- [3.1 打开与关闭文件](#3.1 打开与关闭文件)
- [3.2 读写数据](#3.2 读写数据)
- [3.3 文件定位](#3.3 文件定位)
- [4. 文件描述符](#4. 文件描述符)
- [5. 重定向](#5. 重定向)
-
- [5.1 标准重定向](#5.1 标准重定向)
- [5.2 文件描述符重定向](#5.2 文件描述符重定向)
- [5.3 使用 dup2 实现重定向](#5.3 使用 dup2 实现重定向)
- [6. 文件描述符与流的关系](#6. 文件描述符与流的关系)
- [7. 文件系统与缓冲区](#7. 文件系统与缓冲区)
-
- [7.1 文件系统](#7.1 文件系统)
- [7.2 缓冲区](#7.2 缓冲区)
- [8. 软链接 与 硬链接](#8. 软链接 与 硬链接)
-
- [8.1 软链接(Symbolic Link)](#8.1 软链接(Symbolic Link))
- [8.2 硬链接(Hard Link)](#8.2 硬链接(Hard Link))
- [8.3 软链接与硬链接的区别](#8.3 软链接与硬链接的区别)
前言
Linux 操作系统以其高效、灵活的 I/O 机制闻名,它不仅为开发者提供了丰富的接口,还在底层设计上注重性能与可靠性。下面我们将从多角度解析 Linux 的基础 I/O,涵盖从文件读写到文件描述符、重定向、缓冲区管理以及异步 I/O 的方方面面,理清基础 I/O 在 Linux 中的全貌。
1. I/O 的分类与基础概念
在 Linux 中,I/O 操作分为两大类:
- 标准库 I/O:基于标准 C 库(libc),如
fopen
、fread
等,提供高层次接口,支持缓冲操作。 - 系统调用 I/O:直接调用内核的接口,如
open
、read
等,提供底层控制。
文件与 I/O 的基本概念
- 文件描述符(File Descriptor):内核用来标识文件的一个整数值,是 Linux 一切 I/O 操作的核心。
- 流(Stream):标准库 I/O 操作使用流的抽象,与文件描述符有对应关系。
- 缓冲区:用于提高性能的数据缓存区。
2. 标准库 I/O 接口
标准库提供了一组简单易用的文件操作接口,这些接口基于流(FILE*
)的抽象。
FILE*
是 C 标准库中定义的一个数据类型,用于表示文件流(file stream
)
2.1 文件操作
-
fopen
和fclose
:用于打开和关闭文件。cFILE *file = fopen("example.txt", "r"); if (file) fclose(file);
-
模式选项:
"r"
:只读打开。"w"
:写入打开,清空文件。"a"
:追加模式打开。"b"
:二进制模式(可组合)。
2.2 数据读写
-
文本读写
fgets
和fputs
:按行操作文本。
cchar line[256]; fgets(line, sizeof(line), file); // 从文件读取一行 fputs(line, stdout); // 输出到标准输出
-
格式化读写
fprintf
和fscanf
:支持格式化操作。
cfprintf(file, "Name: %s, Age: %d\n", name, age); fscanf(file, "%s %d", name, &age);
-
二进制读写
fread
和fwrite
:适用于结构体或块数据操作。
cchar buffer[512]; size_t bytesRead = fread(buffer, sizeof(char), 512, file); fwrite(buffer, sizeof(char), bytesRead, anotherFile);
2.3 文件定位
-
fseek
:调整文件指针位置。cfseek(file, 0, SEEK_END); // 移动到文件末尾
-
ftell
:获取当前位置。clong pos = ftell(file);
-
rewind
:快速返回文件开头。crewind(file);
3. 系统调用 I/O 接口
系统调用是 Linux 文件 I/O 的基础,提供底层控制,灵活性更强。
3.1 打开与关闭文件
-
open
和close
:cint fd = open("example.txt", O_RDWR | O_CREAT, 0644); if (fd != -1) close(fd);
-
模式选项:
O_RDONLY
:只读。O_WRONLY
:只写。O_RDWR
:读写。O_CREAT
:文件不存在时创建。
3.2 读写数据
-
read
和write
:cchar buffer[256]; ssize_t n = read(fd, buffer, sizeof(buffer)); write(fd, buffer, n);
3.3 文件定位
-
lseek
:调整文件偏移。coff_t offset = lseek(fd, 0, SEEK_CUR); // 获取当前位置
4. 文件描述符
文件描述符是 Linux I/O 操作的核心抽象,每个打开的文件、管道、网络连接都有一个唯一的描述符。
- 标准文件描述符
0
:标准输入(stdin)。1
:标准输出(stdout)。2
:标准错误(stderr)。
5. 重定向
在 Linux 中,重定向是一种将命令的输入或输出从默认位置(如终端)重定向到其他目标(如文件或设备)的功能。常见的重定向操作包括将输出写入文件或从文件读取输入。
除了在 shell 中使用符号(如 >
和 <
)实现重定向,开发者还可以通过系统调用 dup2
在程序中动态重定向文件描述符。
5.1 标准重定向
-
输出重定向:
bash$ echo "Hello" > output.txt
-
输入重定向:
bash$ wc -l < input.txt
5.2 文件描述符重定向
-
重定向错误输出:
bash$ command 2> error.log
-
同时重定向标准输出和错误输出:
bash$ command > output.log 2>&1
5.3 使用 dup2 实现重定向
dup2
是一个系统调用,用于将一个文件描述符复制到另一个文件描述符,从而实现重定向。
c
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
int main() {
int fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1) {
perror("open");
return 1;
}
// 重定向标准输出到文件
dup2(fd, STDOUT_FILENO);
close(fd);
// 输出内容将被写入 output.txt 而不是终端
printf("Hello, this text is redirected to a file!\n");
return 0;
}
在上面的例子中,dup2
将标准输出(文件描述符 STDOUT_FILENO
)重定向到 output.txt
。此后的所有标准输出操作都会写入该文件,而不是显示在终端中。
6. 文件描述符与流的关系
文件描述符是底层的整数标识符,由内核分配,用于标识打开的文件、管道或设备;而流(FILE*
)是标准库(如 stdio.h
)提供的高级抽象,用于缓冲和格式化的 I/O 操作。
-
文件流指针结构中包含了文件描述符成员
-
流(
FILE*
)通过fopen
创建。 -
描述符由
open
返回。
二者转换:
-
文件流转描述符:
cint fd = fileno(file);
-
描述符转文件流:
cFILE *file = fdopen(fd, "r");
7. 文件系统与缓冲区
7.1 文件系统
Linux 文件系统支持多种类型(如 ext4、XFS、NTFS 等),提供对存储设备的抽象。
- 文件类型
- 普通文件:存储数据。
- 目录文件:组织文件。
- 设备文件:如
/dev/sda
。 - 套接字:如
/var/run/docker.sock
。
7.2 缓冲区
标准 I/O 默认启用缓冲区:
- 全缓冲:如文件流。
- 行缓冲:如标准输出。
- 无缓冲:如标准错误。
缓冲区模式可通过 setvbuf
调整:
c
setvbuf(file, NULL, _IONBF, 0); // 设置无缓冲模式
在 Linux 文件系统中,软链接(symbolic link) 和 硬链接(hard link) 是两种实现文件共享或别名的方法。它们有不同的特性和用途。
8. 软链接 与 硬链接
8.1 软链接(Symbolic Link)
软链接是一个独立的文件,它包含指向另一个文件或目录的路径。软链接类似于 Windows 中的快捷方式。
特点</font>
- 独立存在:软链接是一个实际的文件,与被链接的目标文件分开存储。
- 路径指向:软链接存储的是目标文件的路径(绝对路径或相对路径)。
- 可以跨文件系统:软链接可以指向不同的分区或文件系统上的文件。
- 链断后无效:如果目标文件被删除或移动,软链接会变成无效的"断链"。
创建软链接
使用 ln -s
命令:
bash
ln -s target_file symbolic_link
示例代码
bash
$ echo "Hello" > original.txt
$ ln -s original.txt link.txt
$ cat link.txt
Hello
$ rm original.txt
$ cat link.txt
cat: link.txt: No such file or directory
8.2 硬链接(Hard Link)
硬链接是一个直接引用目标文件的文件系统对象。它们指向文件的同一个物理数据块。
特点
- 共享文件数据:硬链接与目标文件共享相同的 inode,多个硬链接是文件的多个别名。
- 不可跨文件系统:硬链接只能在同一个分区或文件系统内创建。
- 目标文件不可删除:目标文件的实际数据只有当所有硬链接都被删除后才会被释放。
- 链不会断:即使删除目标文件,其数据仍然可以通过硬链接访问。
创建硬链接
使用 ln
命令(不带 -s
选项):
bash
ln target_file hard_link
示例代码
bash
$ echo "Hello" > original.txt
$ ln original.txt hardlink.txt
$ rm original.txt
$ cat hardlink.txt
Hello
8.3 软链接与硬链接的区别
特性 | 软链接 | 硬链接 |
---|---|---|
指向 | 指向目标文件的路径 | 指向目标文件的 inode |
文件系统 | 可以跨文件系统 | 只能在同一个文件系统内 |
是否独立 | 是一个独立文件 | 是文件的额外别名 |
目录支持 | 可以链接目录(需要特权) | 不支持 |
目标文件删除影响 | 链接失效 | 链接仍然有效 |
总结
- 软链接:更灵活,支持跨文件系统,链断后失效,适用于快捷方式场景。
- 硬链接:更稳固,多个硬链接共享同一数据块,适用于需要冗余访问或保护文件内容的场景。