warning
本文章为博主原创文章。遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
在 Linux 系统中,文件描述符(File Descriptor,简称 FD)是一个核心概念,它是一个非负整数,用于唯一标识一个已打开的文件。这个文件可以是普通文件、目录、网络套接字(socket),甚至是设备(如键盘、显示器)。
文件描述符的设计主要有以下几个优点:
简化了 I/O 接口:无论是读写本地文件,还是进行网络通信,开发者都可以使用一套统一的系统调用(read
, write
, close
等),而无需关心底层的具体类型。这种“一切皆文件”的设计哲学是 Linux 简洁和强大的体现。
隔离了应用程序与内核:应用程序只知道一个简单的整数,而不需要知道文件在磁盘上的物理位置,也不用关心内核是如何管理文件的。所有复杂的操作都由内核在幕后完成。
管理系统资源:内核通过文件描述符表来管理每个进程打开的文件。当一个进程结束时,内核可以根据这个表自动关闭所有打开的文件,避免资源泄露。
文件描述符的几个重要特点:
唯一性:在一个进程中,每个打开的文件都有一个独一无二的文件描述符。
非负整数:文件描述符的值通常从 3 开始分配,因为 0、1、2 这三个描述符被系统预留给标准输入、标准输出和标准错误。
0:标准输入(stdin
),通常是键盘。
1:标准输出(stdout
),通常是显示器。
2:标准错误(stderr
),也通常是显示器。
进程私有:文件描述符是在进程内部使用的,不同进程的文件描述符互不影响。例如,进程 A 的文件描述符 3 和进程 B 的文件描述符 3 可能指向完全不同的文件。
Linux I/O 编程中常见的系统调用函数,这些函数位于 unistd.h
(以及 fcntl.h
等)中,是用户态和内核态交互的基础接口。
open
1 |
|
打开或创建一个文件,返回一个 文件描述符(fd,整数),后续 I/O 都依赖它。
pathname
:文件路径。
flags
:打开方式,常用值:
O_RDONLY
:只读
O_WRONLY
:只写
O_RDWR
:读写
O_CREAT
:若文件不存在则创建,需要配合 mode
参数
O_TRUNC
:打开时清空文件
O_APPEND
:写时追加到文件尾部
O_NOBLOCK
:非阻塞模式
mode
:创建文件时的权限(如 0666
,受 umask 影响)。
成功:文件描述符(非负整数)
失败:-1
,并设置 errno
write
1 |
|
向文件(或设备)写入数据。
fd
:文件描述符
buf
:要写入的数据缓冲区指针
count
:要写入的字节数
成功:实际写入的字节数(可能小于 count
)
失败:-1
对于普通文件,通常会写入全部数据。
对于管道、socket 等,可能分多次写入,需要循环写。
read
1 |
|
从文件(或设备)读取数据。
fd
:文件描述符
buf
:存放读出数据的缓冲区
count
:最大读取字节数
成功:实际读取的字节数
0
表示到达文件结尾 (EOF)失败:-1
实际读取字节数可能小于 count
。
对于阻塞 I/O,如果没有数据,可能会挂起等待。
lseek
1 |
|
移动文件读写位置(文件指针)。
fd
:文件描述符
offset
:偏移量(可正可负)
whence
:偏移起始点
SEEK_SET
:从文件开头
SEEK_CUR
:从当前位置
SEEK_END
:从文件末尾
成功:新的文件偏移位置
失败:-1
常用来实现随机读写。
对某些设备文件可能不支持(如管道)。
close
1 |
|
关闭一个打开的文件描述符,释放内核资源。
fd
:文件描述符成功:0
失败:-1
函数 | 头文件 | 功能 | 主要参数 | 返回值 | 常见用途 |
---|---|---|---|---|---|
open |
<fcntl.h> , <unistd.h> |
打开或创建文件,返回文件描述符 | pathname :路径flags :打开方式(如 O_RDONLY )mode :权限(如 0644 ,仅在 O_CREAT 时有效) |
成功:文件描述符 (≥0)失败:-1 |
打开文件/设备,获得 fd |
write |
<unistd.h> |
向文件写入数据 | fd :文件描述符buf :数据缓冲区count :要写入字节数 |
成功:实际写入字节数失败:-1 |
向文件、管道、socket 写数据 |
read |
<unistd.h> |
从文件读取数据 | fd :文件描述符buf :存放数据的缓冲区count :最大读取字节数 |
成功:实际读取字节数0 :文件结尾失败:-1 |
读取文件、键盘输入、网络数据 |
lseek |
<unistd.h> |
改变文件读写位置(文件指针) | fd :文件描述符offset :偏移量whence :参考位置(SEEK_SET /SEEK_CUR /SEEK_END ) |
成功:新的偏移位置失败:-1 |
随机读写文件、获取文件大小 |
close |
<unistd.h> |
关闭文件描述符,释放资源 | fd :文件描述符 |
成功:0 失败:-1 |
程序结束或文件不再使用时关闭 |
sync |
<unistd.h> |
将内核缓冲区中所有修改过的数据(脏页)写入磁盘 | 无 | 无返回值 | 全局刷新,影响所有文件,效率较低 |
fsync |
<unistd.h> |
将指定文件的缓存数据强制写入磁盘 | fd :文件描述符 |
成功:0 ,失败:-1 |
精确到单个文件,常用于数据库、日志写入 |
pipe |
<unistd.h> |
创建匿名管道,用于进程间通信 | int pipefd[2] |
成功:0,失败:-1 | pipefd[0] 读端,pipefd[1] 写端 |
unlink |
<unistd.h> |
删除一个文件(目录用 rmdir ) |
pathname :文件路径 |
成功:0,失败:-1 | 实际删除在引用计数归零时发生 |
access |
<unistd.h> |
检查文件是否存在及权限 | pathname, mode |
成功:0,失败:-1 | mode 可取 R_OK, W_OK, X_OK, F_OK |
使用c标准库的IO操作,小量频繁读写的效率更高,因为其内部自带有缓冲区。
这可以理解为在系统IO上有封装了一层,进行文件操作时写入C自带缓冲区,满足一定条件再调用系统IO,将缓冲区的内容写入IO缓存区,再到内核的页缓存区,最后到物理的磁盘。
特性 | Linux 系统调用 I/O | C 标准库文件操作 |
---|---|---|
头文件 | <unistd.h> , <fcntl.h> |
<stdio.h> |
函数示例 | open , read , write , lseek , close |
fopen , fread , fwrite , fseek , fclose , fprintf , fscanf |
返回值 | 直接返回字节数、文件描述符等,或 -1 |
返回 FILE* 指针,或 EOF 等错误码 |
数据单位 | 以 字节(byte) 为单位 | 以 缓冲区 / 结构化数据 为单位(有缓冲机制) |
缓冲 | 无用户态缓冲,直接在用户空间和内核空间间传递数据 | 带缓冲区(stdio 库内部维护缓存,加速小块读写) |
层次 | 操作系统内核提供的 底层接口 | 基于系统调用的 封装库函数 |
灵活性 | 可操作普通文件、设备文件、socket、管道 | 主要操作普通文件和标准输入输出 |
可移植性 | 偏向 Unix/Linux 系统 | 跨平台(符合 ANSI C 标准,Windows/Linux 通用) |
典型用途 | 驱动开发、系统编程、精确控制 I/O | 应用层文件读写、文本处理、快速开发 |
创建时间:9月 09, 2025
最后更新:9月 09, 2025
字数统计:2.1k字
预计阅读:7min