文件描述符(File Descriptor)

在 Linux 中,所有打开的文件都通过文件描述符(非负整数)来标识。默认的三个文件描述符:

  • 0:标准输入(STDIN)
  • 1:标准输出(STDOUT)
  • 2:标准错误(STDERR)

类型:int

范围:0 ~ 1023,每个进程最多允许打开 1024 个文件/资源。

打开文件:open()

功能:打开或创建一个文件,并返回文件描述符。

函数原型

1
2
3
#include <fcntl.h>
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 时,指定新文件的权限(如0644

返回值

  • 成功:文件描述符
  • -1:出错

关闭文件:close()

功能:关闭已打开的文件描述符,释放资源。

函数原型

1
2
#include <unistd.h>
int close(int fd);

返回值

  • 0:表示文件描述符已成功关闭,相关资源被释放。
  • -1:出错

读取文件:read()

功能:从文件描述符中读取数据。

函数原型

1
2
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);

参数说明

  • fd:文件描述符
  • buf:存储读取数据的缓冲区
  • count:要读取的字节数

返回值

  • 成功:实际读取的字节数(可能小于count
  • 0:已到达文件末尾
  • -1:出错

写入文件:write()

功能:向文件描述符写入数据。

函数原型

1
2
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);

参数说明

  • fd:文件描述符
  • buf:要写入的数据缓冲区
  • count:要写入的字节数

返回值

  • 成功:实际写入的字节数
  • -1:出错

文件定位:lseek()

功能:修改文件的当前读写位置(类似标准库的fseek)。

函数原型

1
2
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);

参数说明

  • offset:偏移量
  • whence:基准位置
    • SEEK_SET:从文件开头开始计算
    • SEEK_CUR:从当前位置开始计算
    • SEEK_END:从文件末尾开始计算

返回值

  • 成功:新的文件偏移量
  • -1:出错
1
2
3
4
5
// 移动到文件开头
off_t pos = lseek(fd, 0, SEEK_SET);

// 移动到文件末尾(获取文件大小)
off_t file_size = lseek(fd, 0, SEEK_END);

创建文件:creat()

功能:创建一个新文件(等价于open(path, O_WRONLY | O_CREAT | O_TRUNC, mode))。

函数原型

1
2
#include <fcntl.h>
int creat(const char *pathname, mode_t mode);

示例

1
int fd = creat("newfile.txt", 0644); // 创建权限为644的新文件

复制文件描述符:dup() 和 dup2()

功能:复制已有的文件描述符,常用于重定向标准输入 / 输出。

函数原型

1
2
3
#include <unistd.h>
int dup(int oldfd); // 复制为最小可用的新描述符
int dup2(int oldfd, int newfd); // 复制到指定的newfd

示例

1
2
3
// 将标准输出重定向到文件
int fd = open("output.txt", O_WRONLY | O_CREAT, 0644);
dup2(fd, STDOUT_FILENO); // 后续printf会写入文件而不是终端

同步写入:fsync()

功能:将文件缓冲区的数据强制写入磁盘(确保数据持久化)。

函数原型

1
2
#include <unistd.h>
int fsync(int fd);

示例

1
2
write(fd, data, len);
fsync(fd); // 确保数据写入磁盘,防止断电丢失

示例程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <stdio.h>
#include <fcntl.h> // open
#include <unistd.h> // close/read/close/lseek

int main(int argc, char *argv[])
{
char buff[128] = {};
ssize_t read_ret;
ssize_t write_ret;

if (argc != 2)
{
printf("用法:%s 文件名\n", argv[0]);
return -1;
}

int fd = open(argv[1], O_RDWR | O_CREAT, 0666);
if (fd < 0)
{
perror("open failed");
return -2;
}

write_ret = write(fd, "hello world", 12);
if (write_ret < 0)
{
perror("写入失败");
return -3;
}
printf("写入的数据长度:%ld\n", write_ret);

if (lseek(fd, 0, SEEK_SET) < 0)
{
perror("文件偏移错误");
return -4;
}

read_ret = read(fd, buff, 32);
if (read_ret < 0)
{
perror("读取失败");
close(fd);
return -5;
}
printf("读取到的数据长度:%ld\n", read_ret);
printf("读取到的数据:%s\n", buff);

close(fd);
return 0;
}

注意事项:

  1. 所有 I/O 函数出错时返回 - 1,并设置全局变量errno,可通过perror()打印错误信息。
  2. 这些是无缓冲 I/O(系统调用),与 C 标准库的fopen/fread/fwrite(带缓冲)不同。
  3. 文件操作完成后必须调用close()释放资源,否则可能导致文件描述符泄漏。

通过这些基础操作,可以组合实现复杂的文件处理功能,如文件复制、数据解析、日志记录等。