getrlimit 函数详解 链接到标题

1. 函数介绍 链接到标题

getrlimit 是 Linux 系统中用于获取进程资源限制的系统调用。可以把资源限制想象成"进程的使用配额"——就像你有手机流量套餐限制一样,每个进程在系统中使用各种资源(如内存、文件描述符、CPU时间等)都有相应的限制。

系统通过资源限制来防止单个进程消耗过多资源而影响整个系统的稳定性。getrlimit 允许你查询当前进程的各种资源限制,了解进程可以使用的资源上限。

2. 函数原型 链接到标题

#include <sys/resource.h>

int getrlimit(int resource, struct rlimit *rlim);

3. 功能 链接到标题

getrlimit 函数用于获取指定资源类型的当前限制。它返回软限制(soft limit)和硬限制(hard limit)的值。

4. 参数 链接到标题

  • resource: 资源类型(见下表)
  • rlim: 指向 rlimit 结构体的指针,用于存储返回的限制值

5. rlimit 结构体 链接到标题

struct rlimit {
    rlim_t rlim_cur;  /* 软限制 (soft limit) */
    rlim_t rlim_max;  /* 硬限制 (hard limit) */
};

6. 资源类型 链接到标题

资源类型 说明
RLIMIT_AS 进程虚拟地址空间的最大大小
RLIMIT_CORE core dump 文件的最大大小
RLIMIT_CPU CPU 时间限制(秒)
RLIMIT_DATA 数据段的最大大小
RLIMIT_FSIZE 文件大小限制(字节)
RLIMIT_LOCKS 文件锁的最大数量
RLIMIT_MEMLOCK 内存锁定的最大字节数
RLIMIT_MSGQUEUE POSIX 消息队列的最大字节数
RLIMIT_NICE 最大 nice 值(调度优先级)
RLIMIT_NOFILE 文件描述符的最大数量
RLIMIT_NPROC 用户可以创建的最大进程数
RLIMIT_RSS 物理内存的最大使用量
RLIMIT_RTPRIO 实时优先级的最大值
RLIMIT_RTTIME 实时调度下不被抢占的 CPU 时间
RLIMIT_SIGPENDING 最大待处理信号数
RLIMIT_STACK 栈的最大大小

7. 限制类型说明 链接到标题

软限制 (rlim_cur) 链接到标题

  • 进程当前实际使用的限制
  • 可以由进程自己降低(但不能超过硬限制)
  • 超过软限制时通常会收到信号(如 SIGXCPU)

硬限制 (rlim_max) 链接到标题

  • 软限制的上限
  • 只能由特权用户(root)提高
  • 普通用户只能降低硬限制

8. 返回值 链接到标题

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

常见错误码:

  • EFAULT: rlim 指针无效
  • EINVAL: resource 参数无效
  • EPERM: 权限不足

9. 相似函数或关联函数 链接到标题

  • setrlimit: 设置资源限制
  • ulimit: shell 内置命令,用于设置/查看资源限制
  • prlimit: 同时获取和设置资源限制(Linux 特有)
  • getrusage: 获取进程资源使用情况

10. 示例代码 链接到标题

示例1:基础用法 - 获取常见资源限制 链接到标题

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/resource.h>
#include <string.h>

// 将限制值转换为可读的字符串
const char* limit_to_string(rlim_t limit) {
    static char buffer[64];
    
    if (limit == RLIM_INFINITY) {
        return "无限制";
    } else if (limit >= 1024LL * 1024 * 1024) {
        snprintf(buffer, sizeof(buffer), "%.2f GB", 
                 (double)limit / (1024 * 1024 * 1024));
    } else if (limit >= 1024 * 1024) {
        snprintf(buffer, sizeof(buffer), "%.2f MB", 
                 (double)limit / (1024 * 1024));
    } else if (limit >= 1024) {
        snprintf(buffer, sizeof(buffer), "%.2f KB", 
                 (double)limit / 1024);
    } else {
        snprintf(buffer, sizeof(buffer), "%lu 字节", (unsigned long)limit);
    }
    
    return buffer;
}

// 获取并显示资源限制
int show_resource_limit(int resource, const char* name) {
    struct rlimit limit;
    
    if (getrlimit(resource, &limit) == -1) {
        perror(name);
        return -1;
    }
    
    printf("%-20s 软限制: %-15s 硬限制: %s\n",
           name,
           limit_to_string(limit.rlim_cur),
           limit_to_string(limit.rlim_max));
    
    return 0;
}

int main() {
    printf("=== 进程资源限制信息 ===\n\n");
    
    // 获取常见的资源限制
    show_resource_limit(RLIMIT_CPU, "CPU 时间");
    show_resource_limit(RLIMIT_FSIZE, "文件大小");
    show_resource_limit(RLIMIT_DATA, "数据段大小");
    show_resource_limit(RLIMIT_STACK, "栈大小");
    show_resource_limit(RLIMIT_CORE, "Core 文件大小");
    show_resource_limit(RLIMIT_RSS, "物理内存");
    show_resource_limit(RLIMIT_NOFILE, "文件描述符数");
    show_resource_limit(RLIMIT_AS, "虚拟地址空间");
    show_resource_limit(RLIMIT_NPROC, "进程数");
    show_resource_limit(RLIMIT_MEMLOCK, "内存锁定");
    show_resource_limit(RLIMIT_LOCKS, "文件锁数");
    
    printf("\n说明:\n");
    printf("1. 无限制表示没有上限\n");
    printf("2. 软限制可以由进程自己调整(不超过硬限制)\n");
    printf("3. 硬限制只能由 root 用户提高\n");
    printf("4. 超过软限制通常会收到相应信号\n");
    
    return 0;
}

示例2:资源限制监控和分析 链接到标题

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <string.h>

// 获取当前资源使用情况
void show_current_usage() {
    struct rusage usage;
    
    if (getrusage(RUSAGE_SELF, &usage) == 0) {
        printf("\n=== 当前资源使用情况 ===\n");
        printf("用户 CPU 时间: %ld.%06ld 秒\n", 
               usage.ru_utime.tv_sec, usage.ru_utime.tv_usec);
        printf("系统 CPU 时间: %ld.%06ld 秒\n", 
               usage.ru_stime.tv_sec, usage.ru_stime.tv_usec);
        printf("最大常驻内存: %ld KB\n", usage.ru_maxrss);
        printf("页面错误次数: %ld\n", usage.ru_majflt);
        printf("文件系统读取: %ld\n", usage.ru_inblock);
        printf("文件系统写入: %ld\n", usage.ru_oublock);
        printf("上下文切换: %ld\n", usage.ru_nvcsw + usage.ru_nivcsw);
    }
}

// 分析资源限制与使用情况
void analyze_limits() {
    struct rlimit limit;
    struct rusage usage;
    
    printf("\n=== 资源限制分析 ===\n");
    
    // 分析内存使用
    if (getrlimit(RLIMIT_AS, &limit) == 0 && getrusage(RUSAGE_SELF, &usage) == 0) {
        printf("虚拟内存使用分析:\n");
        printf("  当前限制: %s\n", 
               (limit.rlim_cur == RLIM_INFINITY) ? "无限制" : 
               (char*)limit_to_string_custom(limit.rlim_cur));
        printf("  当前使用: %.2f MB\n", 
               (double)usage.ru_maxrss / 1024.0);
        
        if (limit.rlim_cur != RLIM_INFINITY) {
            double usage_percent = (usage.ru_maxrss * 1024.0) / limit.rlim_cur * 100;
            printf("  使用比例: %.1f%%\n", usage_percent);
        }
    }
    
    // 分析文件描述符
    if (getrlimit(RLIMIT_NOFILE, &limit) == 0) {
        printf("\n文件描述符分析:\n");
        printf("  当前限制: %s\n", 
               (limit.rlim_cur == RLIM_INFINITY) ? "无限制" : 
               (char*)limit_to_string_custom(limit.rlim_cur));
        printf("  建议: 保持合理数量,避免资源浪费\n");
    }
    
    // 分析栈空间
    if (getrlimit(RLIMIT_STACK, &limit) == 0) {
        printf("\n栈空间分析:\n");
        printf("  当前限制: %s\n", 
               (limit.rlim_cur == RLIM_INFINITY) ? "无限制" : 
               (char*)limit_to_string_custom(limit.rlim_cur));
        printf("  建议: 递归深度过大可能导致栈溢出\n");
    }
}

// 自定义的限制值转换函数
const char* limit_to_string_custom(rlim_t limit) {
    static char buffer[64];
    
    if (limit == RLIM_INFINITY) {
        return "无限制";
    } else if (limit >= 1024LL * 1024 * 1024) {
        snprintf(buffer, sizeof(buffer), "%.2f GB", 
                 (double)limit / (1024 * 1024 * 1024));
    } else if (limit >= 1024 * 1024) {
        snprintf(buffer, sizeof(buffer), "%.2f MB", 
                 (double)limit / (1024 * 1024));
    } else if (limit >= 1024) {
        snprintf(buffer, sizeof(buffer), "%.2f KB", 
                 (double)limit / 1024);
    } else {
        snprintf(buffer, sizeof(buffer), "%lu", (unsigned long)limit);
    }
    
    return buffer;
}

// 模拟资源使用
void simulate_resource_usage() {
    printf("\n=== 模拟资源使用 ===\n");
    
    // 分配一些内存
    size_t alloc_size = 10 * 1024 * 1024;  // 10MB
    char *memory = malloc(alloc_size);
    if (memory) {
        memset(memory, 0xAA, alloc_size);
        printf("已分配 %zu MB 内存\n", alloc_size / (1024 * 1024));
        free(memory);
    }
    
    // 打开一些文件描述符
    int fd_count = 0;
    for (int i = 3; i < 10; i++) {
        int fd = dup(STDOUT_FILENO);
        if (fd != -1) {
            fd_count++;
        }
    }
    printf("已打开 %d 个额外文件描述符\n", fd_count);
    
    // 消耗一些 CPU 时间
    volatile long sum = 0;
    for (long i = 0; i < 1000000; i++) {
        sum += i;
    }
    printf("已完成 CPU 计算任务\n");
}

int main() {
    printf("=== 资源限制监控和分析工具 ===\n");
    
    // 显示当前用户和进程信息
    printf("用户 ID: %d\n", getuid());
    printf("进程 ID: %d\n", getpid());
    printf("父进程 ID: %d\n", getppid());
    
    // 显示资源限制
    printf("\n=== 当前资源限制 ===\n");
    struct rlimit limit;
    
    // CPU 时间限制
    if (getrlimit(RLIMIT_CPU, &limit) == 0) {
        printf("CPU 时间限制: %s\n", 
               (limit.rlim_cur == RLIM_INFINITY) ? "无限制" : 
               (limit.rlim_cur == 0) ? "已超限" : 
               (char*)limit_to_string_custom(limit.rlim_cur));
    }
    
    // 内存限制
    if (getrlimit(RLIMIT_AS, &limit) == 0) {
        printf("虚拟内存限制: %s\n", 
               (limit.rlim_cur == RLIM_INFINITY) ? "无限制" : 
               (char*)limit_to_string_custom(limit.rlim_cur));
    }
    
    // 文件描述符限制
    if (getrlimit(RLIMIT_NOFILE, &limit) == 0) {
        printf("文件描述符限制: 软=%s, 硬=%s\n",
               (limit.rlim_cur == RLIM_INFINITY) ? "无限制" : 
               (char*)limit_to_string_custom(limit.rlim_cur),
               (limit.rlim_max == RLIM_INFINITY) ? "无限制" : 
               (char*)limit_to_string_custom(limit.rlim_max));
    }
    
    // 模拟资源使用
    simulate_resource_usage();
    
    // 显示当前使用情况
    show_current_usage();
    
    // 分析限制
    analyze_limits();
    
    printf("\n=== 优化建议 ===\n");
    printf("1. 监控资源使用情况,避免超限\n");
    printf("2. 合理设置资源限制,平衡性能和稳定性\n");
    printf("3. 使用 ulimit 命令调整 shell 会话的限制\n");
    printf("4. 在程序中检查资源限制并优雅处理\n");
    
    return 0;
}

示例3:完整的资源限制管理工具 链接到标题

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/resource.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>

// 资源信息结构体
struct resource_info {
    int type;
    const char *name;
    const char *description;
    int has_time_unit;
};

// 资源列表
struct resource_info resources[] = {
    {RLIMIT_AS, "as", "虚拟地址空间", 0},
    {RLIMIT_CORE, "core", "Core dump 文件大小", 0},
    {RLIMIT_CPU, "cpu", "CPU 时间", 1},
    {RLIMIT_DATA, "data", "数据段大小", 0},
    {RLIMIT_FSIZE, "fsize", "文件大小", 0},
    {RLIMIT_LOCKS, "locks", "文件锁数量", 0},
    {RLIMIT_MEMLOCK, "memlock", "内存锁定大小", 0},
    {RLIMIT_MSGQUEUE, "msgqueue", "消息队列大小", 0},
    {RLIMIT_NICE, "nice", "最大 nice 值", 0},
    {RLIMIT_NOFILE, "nofile", "文件描述符数量", 0},
    {RLIMIT_NPROC, "nproc", "进程数量", 0},
    {RLIMIT_RSS, "rss", "物理内存使用", 0},
    {RLIMIT_RTPRIO, "rtprio", "实时优先级", 0},
    {RLIMIT_RTTIME, "rttime", "实时 CPU 时间", 1},
    {RLIMIT_SIGPENDING, "sigpending", "待处理信号数", 0},
    {RLIMIT_STACK, "stack", "栈大小", 0},
    {-1, NULL, NULL, 0}
};

// 将限制值转换为字符串
void format_limit(rlim_t limit, char *buffer, size_t size, int time_unit) {
    if (limit == RLIM_INFINITY) {
        snprintf(buffer, size, "unlimited");
    } else if (time_unit && limit >= 3600) {
        snprintf(buffer, size, "%luh%lum%lus", 
                 limit / 3600, (limit % 3600) / 60, limit % 60);
    } else if (time_unit && limit >= 60) {
        snprintf(buffer, size, "%lum%lus", limit / 60, limit % 60);
    } else if (limit >= 1024LL * 1024 * 1024) {
        snprintf(buffer, size, "%.2fG", (double)limit / (1024 * 1024 * 1024));
    } else if (limit >= 1024 * 1024) {
        snprintf(buffer, size, "%.2fM", (double)limit / (1024 * 1024));
    } else if (limit >= 1024) {
        snprintf(buffer, size, "%.2fK", (double)limit / 1024);
    } else {
        snprintf(buffer, size, "%lu", (unsigned long)limit);
    }
}

// 查找资源信息
struct resource_info* find_resource(const char *name) {
    for (int i = 0; resources[i].type != -1; i++) {
        if (strcmp(resources[i].name, name) == 0) {
            return &resources[i];
        }
    }
    return NULL;
}

// 显示所有资源限制
void show_all_limits() {
    printf("%-12s %-15s %-15s %s\n", "资源类型", "软限制", "硬限制", "描述");
    printf("%-12s %-15s %-15s %s\n", "--------", "------", "------", "----");
    
    for (int i = 0; resources[i].type != -1; i++) {
        struct rlimit limit;
        if (getrlimit(resources[i].type, &limit) == 0) {
            char soft_str[32], hard_str[32];
            format_limit(limit.rlim_cur, soft_str, sizeof(soft_str), 
                        resources[i].has_time_unit);
            format_limit(limit.rlim_max, hard_str, sizeof(hard_str), 
                        resources[i].has_time_unit);
            
            printf("%-12s %-15s %-15s %s\n",
                   resources[i].name, soft_str, hard_str, 
                   resources[i].description);
        }
    }
}

// 显示特定资源限制
void show_specific_limit(const char *resource_name) {
    struct resource_info *res = find_resource(resource_name);
    if (!res) {
        fprintf(stderr, "未知资源类型: %s\n", resource_name);
        fprintf(stderr, "可用资源类型: ");
        for (int i = 0; resources[i].type != -1; i++) {
            fprintf(stderr, "%s ", resources[i].name);
        }
        fprintf(stderr, "\n");
        return;
    }
    
    struct rlimit limit;
    if (getrlimit(res->type, &limit) == 0) {
        char soft_str[32], hard_str[32];
        format_limit(limit.rlim_cur, soft_str, sizeof(soft_str), 
                    res->has_time_unit);
        format_limit(limit.rlim_max, hard_str, sizeof(hard_str), 
                    res->has_time_unit);
        
        printf("资源类型: %s\n", res->name);
        printf("描述: %s\n", res->description);
        printf("软限制: %s\n", soft_str);
        printf("硬限制: %s\n", hard_str);
    } else {
        perror("getrlimit");
    }
}

// 解析限制值字符串
rlim_t parse_limit_value(const char *str) {
    if (strcmp(str, "unlimited") == 0 || strcmp(str, "infinity") == 0) {
        return RLIM_INFINITY;
    }
    
    char *endptr;
    unsigned long value = strtoul(str, &endptr, 10);
    
    // 处理单位后缀
    if (*endptr != '\0') {
        switch (*endptr) {
            case 'K': case 'k':
                value *= 1024;
                break;
            case 'M': case 'm':
                value *= 1024 * 1024;
                break;
            case 'G': case 'g':
                value *= 1024 * 1024 * 1024;
                break;
            case 'h':  // 小时
                value *= 3600;
                break;
            case 'm':  // 分钟(如果前面是数字)
                if (endptr > str && *(endptr-1) >= '0' && *(endptr-1) <= '9') {
                    value *= 60;
                }
                break;
        }
    }
    
    return (rlim_t)value;
}

// 显示帮助信息
void show_help(const char *program_name) {
    printf("用法: %s [选项]\n", program_name);
    printf("\n选项:\n");
    printf("  -a, --all           显示所有资源限制\n");
    printf("  -s, --show=RESOURCE 显示特定资源限制\n");
    printf("  -h, --help          显示此帮助信息\n");
    printf("\n资源类型:\n");
    for (int i = 0; resources[i].type != -1; i++) {
        printf("  %-10s %s\n", resources[i].name, resources[i].description);
    }
    printf("\n值格式:\n");
    printf("  数字           原始数值\n");
    printf("  数字K/M/G      带单位的数值 (KB, MB, GB)\n");
    printf("  unlimited      无限制\n");
    printf("  数字h/m/s      时间格式 (小时, 分钟, 秒)\n");
}

int main(int argc, char *argv[]) {
    int show_all = 0;
    char *show_resource = NULL;
    
    // 解析命令行参数
    static struct option long_options[] = {
        {"all",   no_argument,       0, 'a'},
        {"show",  required_argument, 0, 's'},
        {"help",  no_argument,       0, 'h'},
        {0, 0, 0, 0}
    };
    
    int c;
    while (1) {
        int option_index = 0;
        c = getopt_long(argc, argv, "as:h", long_options, &option_index);
        
        if (c == -1)
            break;
            
        switch (c) {
            case 'a':
                show_all = 1;
                break;
                
            case 's':
                show_resource = optarg;
                break;
                
            case 'h':
                show_help(argv[0]);
                return 0;
                
            case '?':
                return 1;
        }
    }
    
    // 执行相应操作
    if (show_all) {
        show_all_limits();
    } else if (show_resource) {
        show_specific_limit(show_resource);
    } else {
        // 默认显示基本限制信息
        printf("=== 进程资源限制信息 ===\n");
        printf("进程 ID: %d\n", getpid());
        printf("用户 ID: %d\n", getuid());
        printf("\n");
        show_all_limits();
    }
    
    return 0;
}

编译和运行说明 链接到标题

# 编译示例程序
gcc -o getrlimit_example1 example1.c
gcc -o getrlimit_example2 example2.c
gcc -o getrlimit_example3 example3.c

# 运行示例
./getrlimit_example1
./getrlimit_example2
./getrlimit_example3 --help
./getrlimit_example3 --all
./getrlimit_example3 --show nofile

命令行工具配合使用 链接到标题

# 使用 ulimit 命令查看和设置资源限制
ulimit -a          # 显示所有限制
ulimit -n          # 显示文件描述符限制
ulimit -n 2048     # 设置文件描述符限制为 2048
ulimit -s          # 显示栈大小限制
ulimit -s 8192     # 设置栈大小限制为 8MB

# 使用 prlimit 命令(Linux 特有)
prlimit --pid $$ --nofile          # 查看当前进程文件描述符限制
prlimit --pid $$ --as=unlimited    # 设置虚拟内存无限制

重要注意事项 链接到标题

  1. 权限限制: 普通用户只能降低硬限制,不能提高
  2. 进程继承: 子进程继承父进程的资源限制
  3. 系统限制: 系统可能有全局资源限制
  4. 信号处理: 超过软限制时可能收到相应信号
  5. 错误处理: 始终检查返回值和 errno

实际应用场景 链接到标题

  1. 系统监控: 监控进程资源使用情况
  2. 安全控制: 限制进程资源使用防止 DoS 攻击
  3. 性能调优: 根据应用需求调整资源限制
  4. 容器环境: Docker 等容器中的资源限制
  5. 服务器应用: Web 服务器、数据库等的资源管理

常见问题和解决方案 链接到标题

// 检查文件描述符限制并优雅处理
int safe_open_file(const char *filename) {
    struct rlimit limit;
    if (getrlimit(RLIMIT_NOFILE, &limit) == 0) {
        // 检查是否接近限制
        // 可以实现动态调整或清理不需要的文件描述符
    }
    return open(filename, O_RDONLY);
}

// 检查内存限制
int check_memory_limit(size_t requested_size) {
    struct rlimit limit;
    struct rusage usage;
    
    if (getrlimit(RLIMIT_AS, &limit) == 0 && 
        getrusage(RUSAGE_SELF, &usage) == 0) {
        
        if (limit.rlim_cur != RLIM_INFINITY) {
            size_t current_usage = usage.ru_maxrss * 1024;
            if (current_usage + requested_size > limit.rlim_cur) {
                return -1;  // 内存不足
            }
        }
    }
    return 0;  // 可以分配
}

这些示例展示了 getrlimit 函数的各种使用方法,从基础的限制查询到完整的资源管理工具,帮助你全面掌握 Linux 系统中的资源限制机制。