`stat` 系统调用详解
stat
是 Unix/Linux 系统中用于获取文件状态信息的核心系统调用。它能够提供关于文件的元数据,而无需打开文件本身。
1. 函数的概念与用途
stat
函数用于获取指定路径名对应文件的信息,包括文件大小、权限、所有者、时间戳等元数据。这些信息存储在 struct stat
结构中。
主要用途:
- 检查文件是否存在及其类型(普通文件、目录、符号链接等)
- 获取文件大小、权限和所有权信息
- 查看文件的访问、修改和状态变更时间
- 实现类似
ls -l
命令的功能 - 在文件操作前验证文件属性
2. 函数的声明与出处
stat
函数及其相关变体定义在 <sys/stat.h>
头文件中,是 POSIX 标准的一部分。
#include <sys/stat.h>int stat(const char *pathname, struct stat *statbuf);
int fstat(int fd, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);
三种变体的区别:
stat()
: 获取路径名指向的文件信息(会跟随符号链接)lstat()
: 类似stat()
,但不跟随符号链接(获取链接本身的信息)fstat()
: 通过文件描述符获取已打开文件的信息
3. 返回值的含义与取值范围
- 成功时:返回 0
- 失败时:返回 -1,并设置
errno
来指示错误原因
常见错误码 (errno):
ENOENT
: 路径名的一部分不存在EACCES
: 对路径名的某部分无搜索权限ENOTDIR
: 路径名的某部分不是目录ELOOP
: 解析路径名时遇到太多符号链接EFAULT
: 错误的地址ENAMETOOLONG
: 路径名太长
4. 参数的含义与取值范围
-
const char *pathname
(对于stat
和lstat
)- 含义:要查询的文件路径
- 取值范围:任何有效的文件系统路径(绝对或相对路径)
-
int fd
(对于fstat
)- 含义:已打开文件的文件描述符
- 取值范围:有效的文件描述符(通常由
open()
返回)
-
struct stat *statbuf
(所有变体)- 含义:指向
struct stat
的指针,用于存储获取到的文件信息 - 取值范围:必须指向有效的内存位置,大小至少为
sizeof(struct stat)
- 含义:指向
5. struct stat
结构体详解
struct stat
包含丰富的文件元数据,其具体字段可能因系统而异,但通常包括:
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; /* 分配的 512B 块数 *//* 时间戳(具体精度和字段可能因系统而异) */time_t st_atime; /* 最后访问时间 */time_t st_mtime; /* 最后修改时间 */time_t st_ctime; /* 最后状态变更时间 */
};
6. 函数使用案例
下面是一个完整的示例,展示如何使用 stat
获取文件信息:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <string.h>void print_file_type(mode_t mode) {if (S_ISREG(mode)) printf("普通文件");else if (S_ISDIR(mode)) printf("目录");else if (S_ISCHR(mode)) printf("字符设备");else if (S_ISBLK(mode)) printf("块设备");else if (S_ISFIFO(mode)) printf("FIFO/命名管道");else if (S_ISLNK(mode)) printf("符号链接");else if (S_ISSOCK(mode)) printf("套接字");else printf("未知类型");
}void print_permissions(mode_t mode) {printf("%c%c%c%c%c%c%c%c%c",(mode & S_IRUSR) ? 'r' : '-',(mode & S_IWUSR) ? 'w' : '-',(mode & S_IXUSR) ? 'x' : '-',(mode & S_IRGRP) ? 'r' : '-',(mode & S_IWGRP) ? 'w' : '-',(mode & S_IXGRP) ? 'x' : '-',(mode & S_IROTH) ? 'r' : '-',(mode & S_IWOTH) ? 'w' : '-',(mode & S_IXOTH) ? 'x' : '-');
}int main(int argc, char *argv[]) {if (argc != 2) {fprintf(stderr, "用法: %s <文件名>\n", argv[0]);exit(EXIT_FAILURE);}struct stat sb;if (stat(argv[1], &sb) == -1) {fprintf(stderr, "stat() 失败: %s\n", strerror(errno));exit(EXIT_FAILURE);}printf("文件信息: %s\n", argv[1]);printf("========================\n");printf("文件类型: ");print_file_type(sb.st_mode);printf("\n");printf("权限: ");print_permissions(sb.st_mode);printf("\n");printf("硬链接数: %ld\n", (long) sb.st_nlink);printf("所有者UID: %d\n", sb.st_uid);printf("组GID: %d\n", sb.st_gid);printf("文件大小: %lld 字节\n", (long long) sb.st_size);printf("块大小: %ld 字节\n", (long) sb.st_blksize);printf("分配块数: %lld\n", (long long) sb.st_blocks);printf("最后访问: %s", ctime(&sb.st_atime));printf("最后修改: %s", ctime(&sb.st_mtime));printf("最后状态变更: %s", ctime(&sb.st_ctime));return 0;
}
7. 编译方式与注意事项
编译命令:
gcc -o stat_demo stat_demo.c
注意事项:
- 权限问题:需要对目标文件路径有执行权限才能成功调用
stat()
- 符号链接:使用
stat()
会跟随符号链接,如需获取链接本身信息,使用lstat()
- 时间精度:不同系统可能支持不同精度的时间戳(秒、纳秒等)
- 可移植性:
struct stat
的具体字段可能因系统和架构而异 - 文件描述符:使用
fstat()
时,确保文件描述符有效且已打开 - 错误处理:始终检查返回值并处理可能的错误情况
8. 执行结果说明
假设有一个名为 test.txt
的文件,运行程序:
$ ./stat_demo test.txt
可能的输出:
文件信息: test.txt
========================
文件类型: 普通文件
权限: rw-r--r--
硬链接数: 1
所有者UID: 1000
组GID: 1000
文件大小: 1024 字节
块大小: 4096 字节
分配块数: 8
最后访问: Wed Jun 15 10:30:45 2023
最后修改: Wed Jun 14 16:20:30 2023
最后状态变更: Wed Jun 14 16:20:30 2023
结果解释:
- 这是一个普通文件,权限为所有者可读写,组和其他用户只读
- 文件大小为1024字节,但实际占用8个512字节的块(即4096字节,由于文件系统块大小)
- 时间戳显示文件的最后访问、修改和状态变更时间
9. 图文总结
以下是 stat
系统调用的工作流程:
关键点总结:
stat
提供了一种无需打开文件即可获取文件元数据的方法- 三种变体 (
stat
,fstat
,lstat
) 适用于不同场景 - 返回的
struct stat
包含丰富的文件信息,如类型、权限、大小和时间戳 - 正确使用
stat
需要了解文件系统权限和路径解析规则 - 始终检查返回值并处理错误情况是良好编程实践
stat
系统调用是文件操作和系统编程的基础,深入理解其工作原理和用法对于开发可靠的系统软件至关重要。