1.文件描述符
文件描述符(File Descriptor)是操作系统中用于访问和操作文件或输入输出资源的一个抽象指针。它是一个非负整数,标识一个已经打开的文件或输入输出资源(如管道、网络连接等)。在UNIX和类UNIX系统(如Linux)中,文件描述符是非常重要的概念,用于文件操作、进程间通信、网络编程等多个方面。
文件描述符的基本概念
-
文件描述符的类型:
- 标准输入(Standard Input): 文件描述符为0,通常与键盘关联,用于从用户处读取输入。
- 标准输出(Standard Output): 文件描述符为1,通常与屏幕关联,用于向用户显示输出。
- 标准错误(Standard Error): 文件描述符为2,通常也与屏幕关联,用于显示错误信息。
-
文件描述符的作用:
- 文件操作: 文件描述符用于读取、写入文件和控制文件状态。常用的系统调用如
open()
,read()
,write()
,close()
等都使用文件描述符。 - 进程间通信: 通过管道(pipe)或命名管道(FIFO)等机制,文件描述符用于在进程间传递数据。
- 网络编程: 套接字(socket)也被视为文件,文件描述符用于处理网络连接。
- 文件操作: 文件描述符用于读取、写入文件和控制文件状态。常用的系统调用如
2.对文件描述符的操作
1.打开和关闭文件
1.open()
open() 会打开一个文件 (可能创建文件),并返回一个新的文件描述符。原型1:
int open(const char *pathname, int flags);
pathname: 文件路径。
flags: 文件打开的标志,如 O_RDONLY(只读),O_WRONLY(只写),O_RDWR(读写),O_CREAT(如果文件不存在则创建),O_EXCL(与 O_CREAT 一起使用,如果文件已存在则失败)。
原型2:
int open(const char *pathname, int flags, mode_t mode);
mode: 文件权限(仅在创建文件时使用),如 S_IRUSR(用户读权限),S_IWUSR(用户写权限)。
2.close()
close()
系统调用用于关闭一个文件描述符,使其不再指向任何文件,并释放相关资源。
int close(int fd);
fd: 文件描述符。
示例代码:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
int fd;
const char *filepath = "example.txt";
// 尝试打开文件
fd = open(filepath, O_RDONLY);
if (fd == -1) {
// 打开文件失败,打印错误信息并退出
perror("Failed to open file");
return 1;
}
printf("File opened successfully.\n");
// 关闭文件
if (close(fd) == -1) {
// 关闭文件失败,打印错误信息
perror("Failed to close file");
return 1;
}
printf("File closed successfully.\n");
return 0;
}
2.读写文件
在Linux系统编程中,read()
和 write()
系统调用用于文件的读写操作。这些调用操作文件描述符,并在底层实现文件内容的读取和写入。
1.read()
原型
ssize_t read(int fd, void *buf, size_t count);
参数:
fd: 文件描述符,表示要读取的文件。
buf: 缓冲区指针,用于存储读取的数据。
count: 需要读取的字节数。
返回值:
成功时返回读取的字节数,返回值为0表示已到达文件末尾。
失败时返回-1,并设置 errno。
2.write()
原型:
ssize_t write(int fd, const void *buf, size_t count);
参数:
fd: 文件描述符,表示要写入的文件。
buf: 缓冲区指针,包含要写入的数据。
count: 需要写入的字节数。
返回值:
成功时返回写入的字节数。
失败时返回-1,并设置 errno。
示例代码:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#define BUFFER_SIZE 1024
int main() {
int source_fd, dest_fd;
ssize_t bytes_read, bytes_written;
char buffer[BUFFER_SIZE];
// 打开源文件 example.txt 以只读方式
source_fd = open("example.txt", O_RDONLY);
if (source_fd == -1) {
perror("Failed to open source file");
return 1;
}
printf("Source file opened successfully.\n");
// 创建并打开目标文件 copy.txt 以只写方式,如果不存在则创建
// 文件权限使用数字形式指定:0600 表示用户读写权限
dest_fd = open("copy.txt", O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (dest_fd == -1) {
perror("Failed to open destination file");
close(source_fd);
return 1;
}
printf("Destination file opened successfully.\n");
// 从源文件读取内容并写入目标文件
while ((bytes_read = read(source_fd, buffer, BUFFER_SIZE)) > 0) {
bytes_written = write(dest_fd, buffer, bytes_read);
if (bytes_written != bytes_read) {
perror("Failed to write to destination file");
close(source_fd);
close(dest_fd);
return 1;
}
}
if (bytes_read == -1) {
perror("Failed to read from source file");
} else {
printf("File copy completed successfully.\n");
}
// 关闭文件
if (close(source_fd) == -1) {
perror("Failed to close source file");
return 1;
}
printf("Source file closed successfully.\n");
if (close(dest_fd) == -1) {
perror("Failed to close destination file");
return 1;
}
printf("Destination file closed successfully.\n");
return 0;
}
3.文件定位
1.lseek()
lseek()
系统调用用于移动文件描述符的读写位置。它允许程序在文件中任意移动读写位置,而不仅仅是顺序读写。
原型:
off_t lseek(int fd, off_t offset, int whence);
参数:
fd: 文件描述符,表示要操作的文件。
offset: 偏移量,表示从 whence 开始的偏移字节数。
whence: 定位基准,决定偏移量的参考位置。可以是以下值之一:
SEEK_SET: 文件开头
SEEK_CUR: 当前位置
SEEK_END: 文件末尾
返回值:
成功时返回新的文件偏移量(以字节为单位)。
失败时返回 -1,并设置 errno。
示例代码:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main() {
int fd;
off_t offset;
const char *filepath = "example.txt";
char buffer[20];
// 打开文件 example.txt 以读写方式
fd = open(filepath, O_RDWR);
if (fd == -1) {
perror("Failed to open file");
return 1;
}
printf("File opened successfully.\n");
// 将文件偏移量移动到文件开头的第10个字节处
offset = lseek(fd, 10, SEEK_SET);
if (offset == -1) {
perror("Failed to lseek");
close(fd);
return 1;
}
printf("File offset moved to: %ld\n", (long)offset);
// 从新的偏移位置读取数据
ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
if (bytes_read == -1) {
perror("Failed to read file");
close(fd);
return 1;
}
// 确保缓冲区以null字符结束
buffer[bytes_read] = '\0';
printf("Read data: %s\n", buffer);
// 将文件偏移量移动到文件末尾再向前移动10个字节
offset = lseek(fd, -10, SEEK_END);
if (offset == -1) {
perror("Failed to lseek");
close(fd);
return 1;
}
printf("File offset moved to: %ld\n", (long)offset);
// 写入新数据到新的偏移位置
const char *new_data = "New Data";
ssize_t bytes_written = write(fd, new_data, strlen(new_data));
if (bytes_written == -1) {
perror("Failed to write file");
close(fd);
return 1;
}
printf("Wrote %ld bytes: %s\n", (long)bytes_written, new_data);
// 关闭文件
if (close(fd) == -1) {
perror("Failed to close file");
return 1;
}
printf("File closed successfully.\n");
return 0;
}
4.文件系统同步
1.fsync()
fsync()
用于将指定文件描述符的所有修改数据同步到存储设备上。它确保文件内容和元数据(如文件权限、修改时间等)被写入磁盘。
原型:
int fsync(int fd);
参数:
fd: 文件描述符。
返回值:
成功时返回0。
失败时返回-1,并设置 errno。
2.fdatasync()
fdatasync()
类似于 fsync()
,但只保证文件内容(数据)同步到存储设备上,不包括文件元数据的同步(除非元数据对文件内容的写入有影响)。
原型:
int fdatasync(int fd);
参数:
fd: 文件描述符。
返回值:
成功时返回0。
失败时返回-1,并设置 errno.
3.ftruncate()
ftruncate()
用于将指定文件描述符的文件大小截断为指定长度。如果文件比新长度短,则扩展文件;如果文件比新长度长,则截断文件。
原型:
int ftruncate(int fd, off_t length);
参数:
fd: 文件描述符。
length: 新的文件大小(字节数)。
返回值:
成功时返回0。
失败时返回-1,并设置 errno。
示例代码:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#define FILE_PATH "example.txt"
int main() {
int fd;
const char *data = "Hello, World!";
ssize_t bytes_written;
// 打开文件 example.txt 以读写方式,如果不存在则创建
fd = open(FILE_PATH, O_RDWR | O_CREAT, 0644);
if (fd == -1) {
perror("Failed to open file");
return 1;
}
printf("File opened successfully.\n");
// 写入数据到文件
bytes_written = write(fd, data, strlen(data));
if (bytes_written == -1) {
perror("Failed to write to file");
close(fd);
return 1;
}
printf("Wrote %ld bytes to the file.\n", (long)bytes_written);
// 使用 fsync() 同步文件数据和元数据
if (fsync(fd) == -1) {
perror("Failed to fsync file");
close(fd);
return 1;
}
printf("File data and metadata synchronized using fsync().\n");
// 截断文件到 5 个字节
if (ftruncate(fd, 5) == -1) {
perror("Failed to truncate file");
close(fd);
return 1;
}
printf("File truncated to 5 bytes.\n");
// 使用 fdatasync() 同步文件数据
if (fdatasync(fd) == -1) {
perror("Failed to fdatasync file");
close(fd);
return 1;
}
printf("File data synchronized using fdatasync().\n");
// 关闭文件
if (close(fd) == -1) {
perror("Failed to close file");
return 1;
}
printf("File closed successfully.\n");
return 0;
}
5.文件状态
1.fstat()
fstat()
系统调用用于获取与文件描述符关联的文件的状态信息。该系统调用返回文件的各种属性,包括文件大小、权限、最后访问时间等。
原型:
int fstat(int fd, struct stat *statbuf);
参数:
fd: 文件描述符,表示要获取状态信息的文件。
statbuf: 指向 stat 结构体的指针,该结构体用于存储文件的状态信息。
返回值:
成功时返回0。
失败时返回-1,并设置 errno。
stat
结构体
stat
结构体定义在 <sys/stat.h>
头文件中,包含许多字段,以下是一些常用字段:
struct stat {
dev_t st_dev; // 文件所在设备的ID
ino_t st_ino; // inode号
mode_t st_mode; // 文件类型和权限
nlink_t st_nlink; // 硬链接数
uid_t st_uid; // 所有者的用户ID
gid_t st_gid; // 所有者的组ID
dev_t st_rdev; // 设备ID(如果是特殊文件)
off_t st_size; // 文件大小(以字节为单位)
blksize_t st_blksize; // 文件系统I/O块大小
blkcnt_t st_blocks; // 分配的块数
time_t st_atime; // 最后访问时间
time_t st_mtime; // 最后修改时间
time_t st_ctime; // 最后状态改变时间
};
示例代码:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include <time.h>
int main() {
int fd;
struct stat statbuf;
const char *filepath = "example.txt";
// 打开文件 example.txt 以只读方式
fd = open(filepath, O_RDONLY);
if (fd == -1) {
perror("Failed to open file");
return 1;
}
printf("File opened successfully.\n");
// 获取文件状态
if (fstat(fd, &statbuf) == -1) {
perror("Failed to get file status");
close(fd);
return 1;
}
// 打印文件状态信息
printf("File size: %ld bytes\n", statbuf.st_size);
printf("Number of blocks allocated: %ld\n", statbuf.st_blocks);
printf("Block size: %ld bytes\n", statbuf.st_blksize);
printf("File type and mode: %o\n", statbuf.st_mode);
printf("Number of hard links: %ld\n", statbuf.st_nlink);
printf("Owner UID: %d\n", statbuf.st_uid);
printf("Owner GID: %d\n", statbuf.st_gid);
printf("Last access time: %s", ctime(&statbuf.st_atime));
printf("Last modification time: %s", ctime(&statbuf.st_mtime));
printf("Last status change time: %s", ctime(&statbuf.st_ctime));
// 关闭文件
if (close(fd) == -1) {
perror("Failed to close file");
return 1;
}
printf("File closed successfully.\n");
return 0;
}
6.文件描述符复制
dup()
和 dup2()
是用于复制文件描述符的系统调用。在Unix和类Unix操作系统中,这些调用用于创建一个新的文件描述符,该描述符指向与原始文件描述符相同的文件表项。
1.dup()
原型:
int dup(int oldfd);
参数:
oldfd: 要复制的文件描述符。
返回值:
成功时返回新的文件描述符。
失败时返回 -1,并设置 errno。
2.dup2()
原型:
int dup2(int oldfd, int newfd);
参数:
oldfd: 要复制的文件描述符。
newfd: 新的文件描述符。如果 newfd 已经被打开,它将首先被关闭。如果 oldfd 和 newfd 相同,则 dup2() 什么也不做,直接返回 newfd。
返回值:
成功时返回 newfd。
失败时返回 -1,并设置 errno。
示例代码:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main() {
int fd, new_fd, new_fd2;
const char *filepath = "example.txt";
const char *data = "Hello, World!";
ssize_t bytes_written;
// 打开文件 example.txt 以读写方式,如果不存在则创建
fd = open(filepath, O_RDWR | O_CREAT, 0644);
if (fd == -1) {
perror("Failed to open file");
return 1;
}
printf("File opened successfully with file descriptor %d.\n", fd);
// 使用 dup() 复制文件描述符
new_fd = dup(fd);
if (new_fd == -1) {
perror("Failed to duplicate file descriptor");
close(fd);
return 1;
}
printf("File descriptor %d duplicated to %d using dup().\n", fd, new_fd);
// 使用 dup2() 复制文件描述符到指定的描述符 5
new_fd2 = dup2(fd, 5);
if (new_fd2 == -1) {
perror("Failed to duplicate file descriptor to 5");
close(fd);
close(new_fd);
return 1;
}
printf("File descriptor %d duplicated to %d using dup2().\n", fd, new_fd2);
// 使用新的文件描述符写入数据到文件
bytes_written = write(new_fd, data, strlen(data));
if (bytes_written == -1) {
perror("Failed to write to file using new file descriptor");
close(fd);
close(new_fd);
close(new_fd2);
return 1;
}
printf("Wrote %ld bytes to the file using duplicated file descriptor %d.\n", (long)bytes_written, new_fd);
// 关闭文件描述符
if (close(fd) == -1) {
perror("Failed to close original file descriptor");
return 1;
}
printf("Original file descriptor %d closed successfully.\n", fd);
if (close(new_fd) == -1) {
perror("Failed to close duplicated file descriptor");
return 1;
}
printf("Duplicated file descriptor %d closed successfully.\n", new_fd);
if (close(new_fd2) == -1) {
perror("Failed to close second duplicated file descriptor");
return 1;
}
printf("Second duplicated file descriptor %d closed successfully.\n", new_fd2);
return 0;
}
7.文件控制
1.fcntl()
fcntl()
系统调用用于控制文件描述符的一些属性和行为。它提供了多种操作,包括复制文件描述符、获取/设置文件描述符标志、文件状态标志等。
原型:
int fcntl(int fd, int cmd, ... /* arg */ );
参数:
fd: 文件描述符。
cmd: 控制命令,不同的命令要求不同的第三个参数(arg)。
F_DUPFD: 复制文件描述符。
F_GETFD: 获取文件描述符标志。
F_SETFD: 设置文件描述符标志。
F_GETFL: 获取文件状态标志。
F_SETFL: 设置文件状态标志。
F_GETLK, F_SETLK, F_SETLKW: 文件加锁操作。
示例代码:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main() {
int fd, new_fd, flags;
const char *filepath = "example.txt";
const char *data = "Hello, World!";
ssize_t bytes_written;
// 打开文件 example.txt 以读写方式,如果不存在则创建
fd = open(filepath, O_RDWR | O_CREAT, 0644);
if (fd == -1) {
perror("Failed to open file");
return 1;
}
printf("File opened successfully with file descriptor %d.\n", fd);
// 使用 fcntl() 复制文件描述符
new_fd = fcntl(fd, F_DUPFD, 0);
if (new_fd == -1) {
perror("Failed to duplicate file descriptor");
close(fd);
return 1;
}
printf("File descriptor %d duplicated to %d using fcntl().\n", fd, new_fd);
// 使用 fcntl() 获取文件状态标志
flags = fcntl(fd, F_GETFL);
if (flags == -1) {
perror("Failed to get file status flags");
close(fd);
close(new_fd);
return 1;
}
printf("File status flags: %d\n", flags);
// 添加非阻塞标志并设置回文件状态标志
flags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) == -1) {
perror("Failed to set file status flags");
close(fd);
close(new_fd);
return 1;
}
printf("File status flags set to: %d\n", flags);
// 使用新的文件描述符写入数据到文件
bytes_written = write(new_fd, data, strlen(data));
if (bytes_written == -1) {
perror("Failed to write to file using new file descriptor");
close(fd);
close(new_fd);
return 1;
}
printf("Wrote %ld bytes to the file using duplicated file descriptor %d.\n", (long)bytes_written, new_fd);
// 关闭文件描述符
if (close(fd) == -1) {
perror("Failed to close original file descriptor");
return 1;
}
printf("Original file descriptor %d closed successfully.\n", fd);
if (close(new_fd) == -1) {
perror("Failed to close duplicated file descriptor");
return 1;
}
printf("Duplicated file descriptor %d closed successfully.\n", new_fd);
return 0;
}
标签:printf,文件,编程,描述符,详解,fd,file,Linux,close
From: https://blog.csdn.net/qq_42037383/article/details/140305938