曲径通幽论坛

标题: 资源与限制(Resource and limits) [打印本页]

作者: beyes    时间: 2009-2-27 17:42
标题: 资源与限制(Resource and limits)
运行在 linux系统上的程序受到(be subject to)资源的限制.这可能是硬件方面的无理性限制(如内存),或者是系统策略方面的限制(例如所分配到的CPU时间),还可以是软件实现(implementation)方面的限制(如一个整数的长度或者文就名所允许的最大字符长度).UNIX 技术规范(specification)定义这些限制,可以由一个应用程序测定出来。

头文件 limits.h 定义了许多一目了然(manifest)的常数,它们是一些操作系统方面的限制值:
Limit Constant                         Purpose
NAME_MAX                          文件名所允许用的最大字符数
CHAR_BIT                           一个char类型值的最大位数
CHAR_MAX                          char类型的最大值
INT_MAX                            int 类型的最大值

注意NAME_MAX 是与文件系统特定的。基于代码的可移植性考虑,应该使用 pathconf() 函数,详见参考手测。

sys/resource.h 头文件提供了资源操作方面的定义。其中包括对程序的最大长度,执行优先级以及文件资源进行核查和设置限制的函数:
#include <sys/resource.h>
int getpriority(int which, id_t who);
int setpriority(int which, id_t who, int priority);
int getrlimit(int resource, struct rlimit *r_limit);
int setrlimit(int resource, const struct rlimit *r_limit);
int getrusage(int who, struct rusage *r_usage);

id_t 是一个用在用户标识符和组标识符方面的整数类型。rusage 结构体,定义在 sys/resource.h 中,它用在确定当前程序使用了多少的 CPU 时间。它必须至少包含以下2个成员:

rusage Member                Description
struct timeval ru_utime        用户占用的时间
struct timeval ru_stime        系统占用的时间

timeval 结构体定义在 sys/time.h 中并且包含 tv_sectv_usec 两个域,分别代表着秒和微秒。

被一个程序消耗的 CPU 时间分为用户时间(user time)和系统时间(system time).用户时间是用户执行自身指令所消耗的时间;系统时间由操作系统为了这个程序而消耗的时间--在系统调用中完成输入输出或者执行其他系统函数所花费的时间。

getrusage() 函数把 CPU 时间信息写到由 r_usage 参数指向的 rusage 结构体中。who 参数可以为以下常数中的一个:

who Constant                               Description
RUSAGE_SELF                            只返回当前程序的CPU时间占用信息
RUSAGE_CHILDREN                       还包含子进程的 CPU 占用时间

简单的说,每一个运行着的程序都有一个与之相关联的优先级,高优先级的程序会被分配到更多的 CPU 时间。普通用户只能降低他们程序的优先级,而不能增加它们。

程序可以通过 getpriority()setpriority() 函数确定和改变他们的优先级或者其他人的优先级。被优先级处理函数检查或改变的进程可以由进程标识符,组标识符,或用户名来得到区分。which 参数说明了 who 参数被对待的方式:

which 参数                                     描述
PRIO_PROCESS                         who 是一个进程标识符
PRIO_PGRP                            who 是一个进程分组标识符
PRIO_USER                            who 是一个用户标识符

所以,确定一个当前进程的优先级,可以如下调用:
priority = getpriority(PRIO_PROCESS, getpid());

setpriority() 函数允许设置一个新的优先级,如果可能的话。

默认的优先级是 0. 正优先级用用于后台任务,它们当没有其他更高优先级准备运行时才会运行。负优先级会使程序运行的更加频繁,且分配了更大一块的 CPU 时间。可用的优先级范围从 -20 到 +20。这经常使人迷惑,因为更高的优先级数字竟然是对应着更低的执行优先权。

getpriority() 函数在成功时返回一个有效的优先级,否则返回 -1 并设置 errno 变量(with errno set on error 并以 errno 确定错误类型)。因为 -1 它自己也是一个有效的优先级,所以 errno 变量应该在调用 getpriority() 前设置为 0 ,并在返回时检查它是否仍为 0。setpriority() 如果成功则返回0,否则返回 -1.

通过 getrlimit() 和 setrlimit() 函数可以读取与设置系统的限制性资源。这两个函数都要用 rlimit 结构体来描述资源方面的限制。这个结构体定义在 sys/resource.h 里,它有以下成员:
rlimit Member                       Description
rlim_t rlim_cur                        当前的软限制
rlim_t rlim_max                       硬限制

定义类型 rlim_t 是一个用来描述资源容量(resource levels)的整数类型。典型地,软限制是一个带有忠告性质的限制值,最好不要超过它,否则可能会引起库函数返回错误。硬限制,如果超出,就会引起系统终止程序的运行,办法是给程序发出一个信号。例如,超出 CPU 时间限制时发送的是 SIGXCPU 信号;超出某个数据容量限制时发出 SIGSEGV 信号。一个程序可以设置它自己的软限制值为小于硬限制值的任意值。它可以减少自己的硬限制。只有以超级用户权限运行的程序才可以增加硬限制。

一些系统资源可以被限制。这些由 rlimit() 函数的 resource 参数所指定并且定义在 sys/resource.h 头文件中,如下所示:

resource Parameter                                  Description
RLIMIT_CORE                                      core 文件的最大字节数,若其值为0则阻止创建core文件
RLIMIT_CPU                                        CPU 时间的最大量值(秒),当超过此软限制时,向该进程发送SIGXCPU信号
RLIMIT_DATA                                      数据段的最大字节长度,是初始化数据,非初始化数据以堆的综合
RLIMIT_FSIZE                                     可以创建的文件的最大字节长度,当超过此软限制时,则向该进程发送 SIGXFSZ 信号
RLIMIT_NOFILE                                  每个进程能打开的最大文件数,更改此限制将影响sysconf函数在参数_SC_OPEN_MAX中的返回值。
RLIMIT_STACK                                   栈的最大字节长度    
RLIMIT_AS                                         进程可用存储区的最大总长度(字节),这会影响 sbrk()和mmap()函数
RLIMIT_NPROC                                 每个实际用户ID可拥有的最大子进程数,更改此限制会影响sysconf函数在参数_SC_CHILD_MAX中的返回值。
RLIMIT_RSS                                       最大驻内存集的字节长度(resident set size in bytes,RSS)
RLIMIT_PTPRIO                                  最大的实时优先级
RLIMIT_NICE                                       非实时进程的优先级(nice level)
RLIMIT_MEMLOCK                             一个进程使用 mlock 函数能够锁定在存储器中的最大字节长度
RLIMIT_MSGQUEUE                          信息队列的最大数目
RLIMIT_SIGPENDING                         待决信号的最大数目
RLIMIT_LOCKS                                  文件锁的最大数目

测试代码
01 #include <sys/types.h>
02 #include <sys/resource.h>
03 #include <sys/time.h>
04 #include <unistd.h>
05 #include <stdio.h>
06 #include <stdlib.h>
07 #include <math.h>
08
09 void work()
10 {
11          FILE *f;
12          int i;
13          double x = 4.5;
14
15          f = tmpfile();
16
17          for(i = 0; i < 10000; i++) {
18                  fprintf(f, "Do some output\n");
19                  if(ferror(f)) {
20                          fprintf(stderr, "Error writing to temporary file\n");
21                          exit(1);
22                  }
23          }
24
25          for(i = 0; i < 1000000; i++)
26                  x = log(x*x + 3.21);
27 }
28
29 int main()
30 {
31          struct rusage r_usage;
32          struct rlimit r_limit;
33          int priority;
34
35          work();
36          getrusage(RUSAGE_SELF, &r_usage);
37
38          printf("CPU usage: User = %ld.%06ld, System = %ld.%06ld\n",
39                  r_usage.ru_utime.tv_sec, r_usage.ru_utime.tv_usec,
40                  r_usage.ru_stime.tv_sec, r_usage.ru_stime.tv_usec);
41
42          priority = getpriority(PRIO_PROCESS, getpid());
43          printf("Current priority = %d\n", priority);
44
45          getrlimit(RLIMIT_FSIZE, &r_limit);
46          printf("Current FSIZE limit: soft = %ld, hard = %ld\n",
47                  r_limit.rlim_cur, r_limit.rlim_max);
48
49          r_limit.rlim_cur = 2048;
50          r_limit.rlim_max = 4096;
51          printf("Setting a 2K fil size limit\n");
52          setrlimit(RLIMIT_FSIZE, &r_limit);
53
54          work();
55          exit(0);
56 }
运行输出 1
[root@localhost C]# ./resource_limits.exe
CPU usage: User = 0.108006, System = 0.000000
Current priority = 0
Current FSIZE limit: soft = -1, hard = -1
Setting a 2K fil size limit
File size limit exceeded
运行输出 2
[root@localhost C]# nice ./resource_limits.exe
CPU usage: User = 0.124007, System = 0.000000
Current priority = 10
Current FSIZE limit: soft = -1, hard = -1
Setting a 2K fil size limit
File size limit exceeded
程序分析
15) tmpfile() 函数创建一个临时文件,并返回一个文件流到 f
18) 写一万句 Do some output
19-21)出错输出然后退出

36) 调用 getrusage(RUSAGE_SELF, &r_usage); 函数。参数 RUSAGE_SELF 表示只返回当前程序的CPU占用时间信息,&r_usage 参数传入 r_usage 结构体指针(自身地址),这样 getrusage() 函数得到的 CPU 时间信息就会写到 r_usage 中

38-40) 打印出用户的CPU时间和系统的CPU时间

42) 利用 getpriority(PRIO_PROCESS, getpid()) 函数得到进程标识符

45) 利用 getrlimit(RLIMIT_FSIZE, &r_limit) 函数得到文件长度限制信息。这里的文件长度信息没有设置,所以允许你创建一个你希望的任意大的文件(当然需要硬盘空间的允许)。所以这里返回的是 -1.

52) 设置可以操作的文件长度限制在 2K

54) 这样,再调用 work() 函数时,因为要创建 150k 的文件,所以发生失败。

注意的是,上面的程序中,'Error writing to temporary file' 这句话不会如你期望的打印出来。这是因为一些系统(如 linux2.2 及以后的版本)当超出资源限制时会终止程序---这通过发送一个 SIGXFSZ 的信号来实现。

更改资源限制时,必须遵循以下三条规则:
1. 任何一个进程都可将一个软限制值更改为更小或等于其硬限制值。
2. 任何一个进程都可以降低其硬限制值,但它必须大于或等于软限制值。这种降低对普通用户而言是不可逆的。
3. 只有超级用户进程可以提高硬限制值。

常量 RLIM_INFINITY 指定了一个无限量的限制。

下面代码打印当前资源限制:
[C++] 纯文本查看 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/resource.h>

#define doit(name)    pr_limits(#name, name)

static void pr_limits (char *, int);

int main(void)
{
    #ifdef RLIMIT_AS
        doit(RLIMIT_AS);
    #endif
   
        doit(RLIMIT_CORE);
        doit(RLIMIT_CPU);
        doit(RLIMIT_DATA);
        doit(RLIMIT_FSIZE);

    #ifdef RLIMIT_LOCKS
        doit(RLIMIT_LOCKS);
    #endif

    #ifdef RLIMIT_MEMLOCK
        doit(RLIMIT_MEMLOCK);
    #endif
   
        doit(RLIMIT_NOFILE);
   
    #ifdef RLIMIT_NPROC
        doit(RLIMIT_NPROC);
    #endif
    #ifdef RLIMIT_RSS
        doit(RLIMIT_RSS);
    #endif
    #ifdef RLIMIT_SBSIZE
        doit(RLIMIT_SBSIZE);
    #endif

        doit(RLIMIT_STACK);

    #ifdef RLIMIT_VMEM
        doit(RLIMIT_VMEM);
    #endif

    exit(0);
}

static void pr_limits(char *name, int resource)
{
    struct rlimit limit;
   
    if(getrlimit(resource, &limit) < 0) {
        perror ("getrlimit");
        exit (EXIT_FAILURE);
    }
    printf ("%-14s  ", name);
    if (limit.rlim_cur == RLIM_INFINITY)
        printf ("(infinite)  ");
    else
        printf ("%10lld  ", limit.rlim_cur);
    if (limit.rlim_max == RLIM_INFINITY)
        printf ("%10lld  ", limit.rlim_max);

    putchar ((int)'\n');
}

运行输出:
[beyes@localhost libfunc]$ ./printlimit
RLIMIT_AS       (infinite)  8589934591  
RLIMIT_CORE     4294967296  8589934591  
RLIMIT_CPU      (infinite)  8589934591  
RLIMIT_DATA     (infinite)  8589934591  
RLIMIT_FSIZE    (infinite)  8589934591  
RLIMIT_LOCKS    (infinite)  8589934591  
RLIMIT_MEMLOCK  4295032832  
RLIMIT_NOFILE   4294968320  
RLIMIT_NPROC    4294968320  
RLIMIT_RSS      (infinite)  8589934591  
RLIMIT_STACK    4303355904  8589934591  
在代码中,由于资源是用 long long 类型表示的,所以在 printf 出来时,要使用 %lld 。




欢迎光临 曲径通幽论坛 (http://www.groad.net/bbs/) Powered by Discuz! X3.2