fstatfs系统调用及示例

fstatfs 函数详解

1. 函数介绍

fstatfs 是 Linux 系统中用于获取文件系统统计信息的系统调用。可以把这个函数想象成一个”文件系统体检工具”——当你给它一个已打开的文件描述符时,它会告诉你这个文件所在文件系统的详细信息,比如总空间多大、已用多少、还剩多少、支持什么特性等等。

就像你去体检时医生会检查你的身高、体重、血压等指标一样,fstatfs 会检查文件系统的”健康状况”和”基本信息”。

2. 函数原型

#include <sys/vfs.h>    /* 或者 #include <sys/statfs.h> */

int fstatfs(int fd, struct statfs *buf);

3. 功能

fstatfs 函数用于获取与指定文件描述符相关的文件系统的统计信息。它填充一个 statfs 结构体,其中包含了文件系统的各种参数和状态信息。

4. 参数

  • fd: 已打开的文件描述符(可以是任何类型的文件:普通文件、目录、设备等)
  • buf: 指向 struct statfs 结构体的指针,用于存储返回的文件系统信息

5. 返回值

  • 成功: 返回 0
  • 失败: 返回 -1,并设置相应的 errno 错误码

常见错误码:

  • EBADF: fd 不是有效的文件描述符
  • EFAULT: buf 指针无效
  • EIO: I/O 错误
  • ENOSYS: 系统不支持此操作

6. 相似函数或关联函数

  • statfs: 通过文件路径获取文件系统信息(需要文件路径而非文件描述符)
  • statvfs: POSIX 标准的文件系统信息获取函数(提供更标准化的接口)
  • fstatvfs: 通过文件描述符获取 POSIX 标准的文件系统信息
  • stat: 获取文件本身的详细信息(不是文件系统信息)

7. struct statfs 结构体详解

struct statfs {
    long    f_type;     /* 文件系统类型 */
    long    f_bsize;    /* 文件系统块大小 */
    long    f_blocks;   /* 总块数 */
    long    f_bfree;    /* 空闲块数 */
    long    f_bavail;   /* 对非超级用户可用的块数 */
    long    f_files;    /* 总 inode 数 */
    long    f_ffree;    /* 空闲 inode 数 */
    fsid_t  f_fsid;     /* 文件系统 ID */
    long    f_namelen;  /* 最大文件名长度 */
    long    f_frsize;   /* 片段大小 */
    long    f_spare[5]; /* 保留字段 */
};

8. 示例代码

示例1:基础用法 – 查看文件系统空间信息

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/vfs.h>

int main() {
    int fd;
    struct statfs fs_info;
    const char *filename = "/etc/passwd";  // 使用系统文件作为示例
    
    // 打开文件
    fd = open(filename, O_RDONLY);
    if (fd == -1) {
        perror("open");
        return 1;
    }
    
    // 获取文件系统信息
    if (fstatfs(fd, &fs_info) == -1) {
        perror("fstatfs");
        close(fd);
        return 1;
    }
    
    // 显示文件系统信息
    printf("=== 文件系统信息 ===\n");
    printf("文件: %s\n", filename);
    printf("文件系统类型: 0x%lx\n", fs_info.f_type);
    printf("块大小: %ld 字节\n", fs_info.f_bsize);
    printf("总块数: %ld\n", fs_info.f_blocks);
    printf("空闲块数: %ld\n", fs_info.f_bfree);
    printf("可用块数: %ld\n", fs_info.f_bavail);
    printf("总 inode 数: %ld\n", fs_info.f_files);
    printf("空闲 inode 数: %ld\n", fs_info.f_ffree);
    
    // 计算空间使用情况(以 GB 为单位)
    double total_space = (double)(fs_info.f_blocks * fs_info.f_bsize) / (1024 * 1024 * 1024);
    double free_space = (double)(fs_info.f_bfree * fs_info.f_bsize) / (1024 * 1024 * 1024);
    double avail_space = (double)(fs_info.f_bavail * fs_info.f_bsize) / (1024 * 1024 * 1024);
    
    printf("\n=== 空间使用情况 ===\n");
    printf("总空间: %.2f GB\n", total_space);
    printf("空闲空间: %.2f GB\n", free_space);
    printf("可用空间: %.2f GB\n", avail_space);
    printf("使用率: %.2f%%\n", 
           (total_space - free_space) / total_space * 100);
    
    close(fd);
    return 0;
}

示例2:文件系统类型识别

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/vfs.h>

// 常见文件系统类型的宏定义
#define EXT2_SUPER_MAGIC    0xEF53
#define EXT3_SUPER_MAGIC    0xEF53
#define EXT4_SUPER_MAGIC    0xEF53
#define XFS_SUPER_MAGIC     0x58465342
#define NTFS_SB_MAGIC       0x5346544E
#define TMPFS_MAGIC         0x01021994
#define PROC_SUPER_MAGIC    0x9FA0
#define SYSFS_MAGIC         0x62656572

// 根据文件系统类型返回字符串描述
const char* get_fs_type_name(long fs_type) {
    switch (fs_type) {
        case EXT2_SUPER_MAGIC:
            return "ext2/ext3/ext4";
        case XFS_SUPER_MAGIC:
            return "XFS";
        case NTFS_SB_MAGIC:
            return "NTFS";
        case TMPFS_MAGIC:
            return "tmpfs";
        case PROC_SUPER_MAGIC:
            return "proc";
        case SYSFS_MAGIC:
            return "sysfs";
        default:
            return "未知文件系统";
    }
}

int main() {
    int fd;
    struct statfs fs_info;
    const char *test_files[] = {"/etc/passwd", "/proc/version", "/sys/kernel"};
    int num_files = sizeof(test_files) / sizeof(test_files[0]);
    
    for (int i = 0; i < num_files; i++) {
        printf("\n=== 检查文件: %s ===\n", test_files[i]);
        
        // 打开文件
        fd = open(test_files[i], O_RDONLY);
        if (fd == -1) {
            perror("open");
            printf("跳过此文件\n");
            continue;
        }
        
        // 获取文件系统信息
        if (fstatfs(fd, &fs_info) == -1) {
            perror("fstatfs");
            close(fd);
            continue;
        }
        
        // 显示详细信息
        printf("文件系统类型代码: 0x%lx\n", fs_info.f_type);
        printf("文件系统类型名称: %s\n", get_fs_type_name(fs_info.f_type));
        printf("最大文件名长度: %ld 字符\n", fs_info.f_namelen);
        printf("片段大小: %ld 字节\n", fs_info.f_frsize);
        
        close(fd);
    }
    
    return 0;
}

示例3:磁盘空间监控工具

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/vfs.h>
#include <string.h>

// 磁盘使用情况结构体
struct disk_usage {
    char path[256];
    double total_gb;
    double used_gb;
    double free_gb;
    double usage_percent;
};

// 获取磁盘使用情况
int get_disk_usage(const char *filepath, struct disk_usage *usage) {
    int fd;
    struct statfs fs_info;
    
    // 打开文件
    fd = open(filepath, O_RDONLY);
    if (fd == -1) {
        perror("open");
        return -1;
    }
    
    // 获取文件系统信息
    if (fstatfs(fd, &fs_info) == -1) {
        perror("fstatfs");
        close(fd);
        return -1;
    }
    
    // 填充使用情况结构体
    strncpy(usage->path, filepath, sizeof(usage->path) - 1);
    usage->path[sizeof(usage->path) - 1] = '\0';
    
    // 计算空间大小(GB)
    double block_size_gb = (double)fs_info.f_bsize / (1024 * 1024 * 1024);
    usage->total_gb = fs_info.f_blocks * block_size_gb;
    usage->free_gb = fs_info.f_bavail * block_size_gb;  // 使用 f_bavail 而不是 f_bfree
    usage->used_gb = usage->total_gb - usage->free_gb;
    usage->usage_percent = (usage->used_gb / usage->total_gb) * 100;
    
    close(fd);
    return 0;
}

// 打印磁盘使用情况(带进度条效果)
void print_disk_usage(const struct disk_usage *usage) {
    printf("路径: %s\n", usage->path);
    printf("总空间: %.2f GB\n", usage->total_gb);
    printf("已用空间: %.2f GB\n", usage->used_gb);
    printf("可用空间: %.2f GB\n", usage->free_gb);
    
    // 显示进度条
    int bar_width = 50;
    int filled_width = (int)((usage->usage_percent / 100) * bar_width);
    
    printf("使用率: %.1f%% [", usage->usage_percent);
    for (int i = 0; i < bar_width; i++) {
        if (i < filled_width) {
            printf("#");
        } else {
            printf("-");
        }
    }
    printf("]\n\n");
}

int main() {
    // 测试不同的文件路径
    const char *test_paths[] = {
        "/",
        "/home",
        "/tmp",
        "/etc/passwd"
    };
    
    int num_paths = sizeof(test_paths) / sizeof(test_paths[0]);
    
    printf("=== 磁盘空间监控工具 ===\n\n");
    
    for (int i = 0; i < num_paths; i++) {
        struct disk_usage usage;
        
        if (get_disk_usage(test_paths[i], &usage) == 0) {
            print_disk_usage(&usage);
        } else {
            printf("无法获取 %s 的磁盘信息\n\n", test_paths[i]);
        }
    }
    
    return 0;
}

编译和运行说明

gcc -o fstatfs_example fstatfs_example.c
./fstatfs_example

注意事项

  1. 文件系统类型: 不同的文件系统类型有不同的魔数(magic number),可以通过 f_type 字段识别
  2. 权限要求: 通常只需要对文件有读权限即可调用此函数
  3. 空间计算:
    • f_bfree: 所有用户都可用的空闲块数
    • f_bavail: 非超级用户可用的块数(通常更小)
  4. 移植性statfs 结构体在不同系统上可能有差异,如需高移植性可考虑使用 statvfs
  5. 性能: 频繁调用此函数可能影响性能,建议缓存结果

这些示例展示了 fstatfs 函数的多种应用场景,从简单的空间信息获取到文件系统类型识别,再到实用的磁盘监控工具,帮助你全面理解这个函数的使用方法。

fstatfs系统调用及示例-CSDN博客

此条目发表在linux文章分类目录。将固定链接加入收藏夹。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注