quotactl系统调用及示例

我们来学习一下 quotactl 这个系统调用。它主要用于管理 Linux 系统上的磁盘配额,帮助系统管理员控制用户或组可以使用的磁盘空间量 。

1. 函数介绍

quotactl 是一个 Linux 系统调用(System Call),它提供了一个底层接口来操作 Unix 和 Linux 操作系统中的磁盘配额 。简单来说,它允许你设置和查询特定用户(UID)或组(GID)在某个文件系统上可以使用的磁盘空间上限 。这对于防止某个用户或组占用过多磁盘资源,确保系统资源公平分配非常有用。

2. 函数原型

#include <sys/quota.h>

int quotactl(int cmd, const char *special, int id, caddr_t addr);

3. 功能

这个函数的主要功能是操作磁盘配额 。根据传入的 cmd 参数,它可以执行多种操作,比如设置配额限制、获取当前配额使用情况、开启或关闭文件系统的配额检查等 。

4. 参数

  • cmdint 类型。这是一个命令参数,指定了要执行的具体操作以及配额的类型(用户配额或组配额) 。通常使用 QCMD(subcmd, type) 宏来构造这个命令。subcmd 是具体的操作(如 Q_QUOTAONQ_QUOTAOFFQ_GETQUOTAQ_SETQUOTA 等),type 是配额类型(通常是 USRQUOTA 表示用户配额,GRPQUOTA 表示组配额) 。
  • specialconst char * 类型。指向一个以 null 结尾的字符串,该字符串表示要操作的文件系统设备的路径名(例如 “/dev/sda1”)或挂载点(例如 “/home”) 。
  • idint 类型。指定要操作的用户 ID (UID) 或组 ID (GID),具体取决于 cmd 中指定的类型 。
  • addrcaddr_t 类型(通常可以看作 void *)。一个地址指针,指向包含操作所需数据的缓冲区,或者用于存放函数返回的数据。具体指向的数据结构取决于 cmd 指定的操作 。例如,如果是获取配额信息 (Q_GETQUOTA),它指向一个 struct dqblk 结构体变量,函数会将结果填入其中;如果是设置配额信息 (Q_SETQUOTA),它指向一个包含新配额设置的 struct dqblk 结构体变量。

5. 返回值

  • 成功: 返回 0。
  • 失败: 返回 -1,并设置全局变量 errno 来指示具体的错误类型 。

6. 相似函数或关联函数

  • quota 命令: 这是一个用户空间的命令行工具,用于显示用户或组的磁盘配额信息。它内部可能会使用 quotactl 系统调用来获取信息。
  • quotacheck 命令: 用于扫描文件系统并创建或更新配额文件(如 aquota.user 和 aquota.group),这些文件存储了配额信息 。
  • edquota 命令: 用于编辑用户或组的磁盘配额限制,它也依赖于 quotactl 来应用更改。
  • QCMD 宏: 用于构造 quotactl 的 cmd 参数 。
  • struct dqblk: 这是与 quotactl 配合使用的一个重要结构体,用于存储或传递配额限制和使用情况的数据。

7. 示例代码

前提: 运行这些示例代码需要 root 权限,因为管理配额是系统管理员的任务。同时,目标文件系统需要支持并已启用配额功能。

示例 1: 获取用户磁盘配额信息

#include <stdio.h>
#include <unistd.h>
#include <sys/quota.h>
#include <errno.h>
#include <string.h>

int main() {
    // 假设我们要查询 /home 文件系统的用户配额
    const char *mount_point = "/home";
    // 假设我们要查询 UID 为 1001 的用户的配额
    int user_id = 1001;
    struct dqblk quota_info;
    int result;

    // 使用 QCMD 宏构造 cmd 参数:操作是 Q_GETQUOTA,类型是 USRQUOTA (用户配额)
    result = quotactl(QCMD(Q_GETQUOTA, USRQUOTA), mount_point, user_id, (caddr_t)&quota_info);

    if (result == -1) {
        perror("quotactl"); // 打印错误信息
        fprintf(stderr, "Failed to get quota info for user %d on %s\n", user_id, mount_point);
        return 1;
    }

    // 打印获取到的配额信息
    // 注意: 数值通常以 1KB 块为单位
    printf("User ID: %d\n", user_id);
    printf("Block Hard Limit: %llu KB\n", (unsigned long long)quota_info.dqb_bhardlimit);
    printf("Block Soft Limit: %llu KB\n", (unsigned long long)quota_info.dqb_bsoftlimit);
    printf("Current Blocks Used: %llu KB\n", (unsigned long long)quota_info.dqb_curblocks);
    printf("Inode Hard Limit: %llu\n", (unsigned long long)quota_info.dqb_ihardlimit);
    printf("Inode Soft Limit: %llu\n", (unsigned long long)quota_info.dqb_isoftlimit);
    printf("Current Inodes Used: %llu\n", (unsigned long long)quota_info.dqb_curinodes);
    // dqb_btime 和 dqb_itime 是宽限时间,当超过软限制时才相关
    // printf("Block Grace Time Expiry: %lu\n", quota_info.dqb_btime);
    // printf("Inode Grace Time Expiry: %lu\n", quota_info.dqb_itime);

    return 0;
}

示例 2: 设置用户磁盘配额限制

#include <stdio.h>
#include <unistd.h>
#include <sys/quota.h>
#include <errno.h>
#include <string.h>
#include <time.h> // 需要设置宽限时间

int main() {
    // 假设我们要设置 /home 文件系统的用户配额
    const char *mount_point = "/home";
    // 假设我们要设置 UID 为 1002 的用户的配额
    int user_id = 1002;
    struct dqblk new_quota_limits;
    int result;

    // 初始化结构体
    memset(&new_quota_limits, 0, sizeof(new_quota_limits));

    // 设置块(磁盘空间)限制 (单位通常是 1KB 块)
    new_quota_limits.dqb_bhardlimit = 500000; // 硬限制 500MB
    new_quota_limits.dqb_bsoftlimit = 400000; // 软限制 400MB

    // 设置 inode(文件数量)限制
    new_quota_limits.dqb_ihardlimit = 5000;  // 硬限制 5000 个文件
    new_quota_limits.dqb_isoftlimit = 4000;  // 软限制 4000 个文件

    // 设置宽限时间 (秒) - 当超过软限制时,用户还有这段时间可以清理,之后硬限制生效
    // 如果不设置或设置为0,可能会使用默认值或导致软限制行为异常
    new_quota_limits.dqb_btime = time(NULL) + 7 * 24 * 60 * 60; // 7天后
    new_quota_limits.dqb_itime = time(NULL) + 7 * 24 * 60 * 60; // 7天后
    // 设置有效的字段标志,告诉内核我们要设置哪些字段
    new_quota_limits.dqb_valid = QIF_LIMITS | QIF_TIMES; // 设置限制和时间

    // 使用 QCMD 宏构造 cmd 参数:操作是 Q_SETQUOTA,类型是 USRQUOTA (用户配额)
    result = quotactl(QCMD(Q_SETQUOTA, USRQUOTA), mount_point, user_id, (caddr_t)&new_quota_limits);

    if (result == -1) {
        perror("quotactl"); // 打印错误信息
        fprintf(stderr, "Failed to set quota limits for user %d on %s\n", user_id, mount_point);
        return 1;
    }

    printf("Successfully set quota limits for user %d on %s\n", user_id, mount_point);
    printf("Block Hard Limit: %llu KB, Soft Limit: %llu KB\n",
           (unsigned long long)new_quota_limits.dqb_bhardlimit,
           (unsigned long long)new_quota_limits.dqb_bsoftlimit);
    printf("Inode Hard Limit: %llu, Soft Limit: %llu\n",
           (unsigned long long)new_quota_limits.dqb_ihardlimit,
           (unsigned long long)new_quota_limits.dqb_isoftlimit);

    return 0;
}

编译和运行:

# 假设代码保存在 get_quota.c 和 set_quota.c 中
# 需要 root 权限编译和运行
sudo gcc -o get_quota get_quota.c
sudo gcc -o set_quota set_quota.c

# 运行前确保 /home 文件系统启用了用户配额
# 运行示例 (需要 root 权限)
sudo ./get_quota
sudo ./set_quota

请注意,实际使用中需要确保文件系统已经正确配置并启用了配额功能,这通常涉及在 /etc/fstab 中添加 usrquota 或 grpquota 选项,并使用 quotacheck 和 quotaon 命令初始化和启用配额。

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

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

发表回复

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