「Linux 系统编程」目录项与 Inode、stat 与 lstat 函数

1. 目录项与 Inode

1.1 Inode

在 Linux 文件系统中,每个文件(或目录)都有一个对应的 inode(index node),即索引节点。inode 不包含文件的名称,而是存储文件的元数据(metadata),例如:

  • 文件大小
  • 设备 ID
  • 文件所有者及所属组的 ID
  • 文件权限(读、写、执行)
  • 时间戳(创建时间、最后访问时间、最后修改时间等)
  • 指向文件数据块的指针

inode 通过一个唯一的编号(inode number)来标识。可以使用 ls -i 命令查看文件的 inode 号:

1
ls -i test.txt

也可以使用 stat 命令直接查看文件的元数据:

1
stat test.txt


1.2 目录项

目录项(dentry) 是内核用于维护文件系统目录结构的数据结构。每个目录项包含 一个文件名对应的 inode 号。目录项 将可读的文件名映射到内核使用的 inode 号

当用户打开一个文件(如 /home/user/test.txt)时,内核会遍历路径中的每个目录项,逐步解析直到找到目标文件的 inode,进而访问文件内容。

目录项和 inode 的设计使得文件系统 支持硬链接。多个目录项可以指向同一个 inode,即同一个文件可以有多个不同的路径名称。



2. stat 函数

2.1 stat 函数原型

stat 函数是一个系统调用,用于 获取指定路径文件的元数据信息。其原型如下:

1
2
3
4
5
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int stat(const char *pathname, struct stat *statbuf);
  • pathname:目标文件的路径。
  • statbuf:指向 struct stat 结构的指针,用于存储获取到的文件信息。

2.2 struct stat 结构

struct stat 包含了文件的多种元数据,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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; // 最后状态变更时间
};

<sys/stat.h> 头文件中定义了一系列宏,用于检测 struct stat 结构中的 st_mode 字段,从而 确定文件类型。这些宏接受一个 mode_t 类型的参数(通常是 st_mode 字段),并返回一个布尔值。

文件类型相关的宏

  • S_ISREG(m) - 检测是否为普通文件
  • S_ISDIR(m) - 检测是否为目录
  • S_ISCHR(m) - 检测是否为字符设备文件
  • S_ISBLK(m) - 检测是否为块设备文件
  • S_ISFIFO(m) - 检测是否为 FIFO(命名管道)
  • S_ISLNK(m) - 检测是否为符号链接
  • S_ISSOCK(m) - 检测是否为套接字文件

2.3 代码示例

例如,可以使用 stat 获取文件大小、文件类型(文件类型需要用到相关的宏)等:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>

int main(int argc, char* argv[]){
struct stat stat_buf;

if(stat(argv[1], &stat_buf) == -1){
perror("stat error");
return 1;
}

printf("File size: %lld bytes\n", (long long)stat_buf.st_size);

if(S_ISLNK(stat_buf.st_mode)){
printf("%s is a sym link\n", argv[1]);
}

return 0;
}

注意:如果 pathname 是一个符号链接,stat跟随链接指向最终的目标文件(链接穿透),并返回目标文件的元数据。



3. lstat 函数

3.1 lstat 函数原型

lstat 函数与 stat 类似,在处理符号链接时有所不同。函数原型如下:

1
2
3
4
5
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int lstat(const char *pathname, struct stat *statbuf);

3.2 与 stat 的区别

当路径指向一个符号链接时:

  • stat 返回 符号链接所指向的目标文件 的元数据。
  • lstat 返回 符号链接文件本身 的元数据。

3.3 代码示例

以下示例比较 statlstat 对符号链接的处理:

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
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>

int main(int argc, char* argv[]){
struct stat stat_buf;
struct stat lstat_buf;

if(stat(argv[1], &stat_buf) == -1){
perror("stat error");
return 1;
}

if(lstat(argv[1], &lstat_buf) == -1){
perror("lstat error");
return 1;
}

printf("stat - inode number of target: %ld\n", (long)stat_buf.st_ino);
if(S_ISLNK(stat_buf.st_mode)){
printf("stat: %s is a sym link\n", argv[1]);
}

printf("lstat - inode number of link itself: %ld\n", (long)lstat_buf.st_ino);
if(S_ISLNK(lstat_buf.st_mode)){
printf("lstat: %s is a sym link\n", argv[1]);
}

return 0;
}


「Linux 系统编程」目录项与 Inode、stat 与 lstat 函数
https://marisamagic.github.io/2025/08/26/20250826/
作者
MarisaMagic
发布于
2025年8月26日
许可协议