文件描述符
文件描述符(File Descriptor,简称 FD)是 UNIX 和 UNIX-like 系统中用于代表和识别打开的文件或其他I/O资源的一种抽象标识。它是一个非负整数,内部由操作系统进行管理和分配。文件描述符可以代表文件、套接字、管道等各种类型的I/O资源。
核心概念:
-
标准文件描述符:当一个进程启动时,它默认会拥有三个已经打开的文件描述符。
0
- 标准输入(STDIN)1
- 标准输出(STDOUT)2
- 标准错误输出(STDERR)
-
分配:当新的文件或其他I/O资源被打开时(例如使用
open()
或socket()
),操作系统会为它分配最小的可用文件描述符。 -
生命周期:文件描述符在资源打开时被创建,当资源被关闭时(例如使用
close()
)被回收。 -
重定向:在 shell 编程中,可以重定向文件描述符,将输出写入文件或从文件中读取输入。
-
表:内核维护一个文件描述符表,每个进程都有其自己的文件描述符表。表中的每个条目都指向一个文件、套接字或其他类型的I/O资源。
-
限制:每个进程都有一个文件描述符的上限,即它可以同时打开的最大文件数量。这个上限可以通过
ulimit
命令查看和修改(在某些系统中)。
使用文件描述符的例子:
-
使用
open()
打开文件:int fd = open("example.txt", O_RDONLY); if (fd == -1) { perror("open"); }
-
使用
read()
从文件描述符读取数据:char buffer[256]; ssize_t bytes_read = read(fd, buffer, sizeof(buffer));
-
使用
write()
向文件描述符写入数据:const char *message = "Hello, world!"; ssize_t bytes_written = write(fd, message, strlen(message));
-
使用
close()
关闭文件描述符:close(fd);
文件描述符是 UNIX 和 UNIX-like 系统中低级I/O操作的核心。然而,在许多应用编程场景中,高级I/O函数(如标准C库中的 fopen()
, fread()
, fwrite()
等)提供了更简洁和可移植的接口,它们在内部使用文件描述符,但为开发者提供了更高级和更便于使用的抽象。
open()
open()
是在 UNIX 和 Linux 系统编程中常用的一个系统调用,用于打开或创建文件。一旦文件被打开,open()
会返回一个文件描述符,这是一个非负整数,可以用来引用该文件进行进一步的操作(如读、写或关闭文件)。
函数原型
#include <sys/types.h>
#include <sys/stat.h>
#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_EXCL
: 与O_CREAT
一起使用,确保文件在被创建时是新的,即如果文件已存在则调用失败。O_TRUNC
: 如果文件已存在且成功以写方式打开,则截断文件的长度为0。O_APPEND
: 打开文件进行追加(每次写都写在文件末尾)。
- mode: 当使用
O_CREAT
标志时,该参数用于指定新文件的权限。它是一个八进制数,如0644
,表示文件所有者有读写权限,而组成员和其他用户只有读取权限。
返回值
- 成功时返回一个非负整数,即文件描述符。
- 失败时返回
-1
,并设置errno
以指示错误。
示例
- 只读方式打开一个已存在的文件:
int fd = open("test.txt", O_RDONLY);
- 读写方式打开一个文件,如果文件不存在则创建它,并设置权限为 0644:
int fd = open("test.txt", O_RDWR | O_CREAT, 0644);
- 只写方式打开一个文件以追加内容:
int fd = open("test.txt", O_WRONLY | O_APPEND);
使用 open()
打开的文件应当在完成操作后使用 close()
函数关闭。这是良好的编程实践,可以避免资源泄漏。
lseek()
lseek()
是一个 UNIX 和 Linux 系统调用,用于改变文件描述符所指示的文件的当前读/写位置。这个系统调用允许应用程序随机访问文件中的任何位置,而不仅仅是连续地读取或写入文件。
原型
其函数原型如下:
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
参数
- fd: 文件描述符,通常是之前由
open()
函数返回的。 - offset: 相对于基准点(由
whence
参数指定)的字节偏移量。 - whence: 基准点,可以是以下之一:
SEEK_SET
: 文件的开始位置。SEEK_CUR
: 文件的当前位置。SEEK_END
: 文件的结束位置。
返回值
- 如果成功,
lseek()
返回新的文件偏移量(相对于文件开始的位置)。 - 如果失败,返回
-1
并设置errno
。
示例
下面是一个简单的示例,展示了如何使用 lseek()
:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main() {
int fd;
off_t position;
fd = open("test.txt", O_RDWR);
if (fd == -1) {
perror("Error opening the file");
return 1;
}
// Move file pointer 10 bytes from the start
position = lseek(fd, 10, SEEK_SET);
if (position == (off_t) -1) {
perror("lseek error");
close(fd);
return 1;
}
printf("Current file position: %ld\n", position);
close(fd);
return 0;
}
在这个示例中,我们首先打开一个名为 “test.txt” 的文件。然后,我们使用 lseek()
将文件指针从文件的开始位置向前移动10个字节。最后,我们输出了文件的新位置并关闭了文件。
需要注意的是,并不是所有的文件类型都支持 lseek()
。例如,尝试在某些类型的设备文件或管道上使用 lseek()
可能会失败。