我们来学习一下 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. 参数
cmd
:int
类型。这是一个命令参数,指定了要执行的具体操作以及配额的类型(用户配额或组配额) 。通常使用QCMD(subcmd, type)
宏来构造这个命令。subcmd
是具体的操作(如Q_QUOTAON
,Q_QUOTAOFF
,Q_GETQUOTA
,Q_SETQUOTA
等),type
是配额类型(通常是USRQUOTA
表示用户配额,GRPQUOTA
表示组配额) 。special
:const char *
类型。指向一个以 null 结尾的字符串,该字符串表示要操作的文件系统设备的路径名(例如 “/dev/sda1”)或挂载点(例如 “/home”) 。id
:int
类型。指定要操作的用户 ID (UID) 或组 ID (GID),具体取决于cmd
中指定的类型 。addr
:caddr_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)"a_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
命令初始化和启用配额。