Solaris 10 资源管理器开发者指南

第 5 章 资源控制

本章介绍资源控制及其属性。

资源控制概述

可使用扩展的记帐功能确定系统上的工作负荷所占用的资源。确定占用的资源后,请使用资源控制功能为资源使用情况设置限制。 对资源设置的限制可以防止工作负荷过度占用资源。

有关资源控制以及用于管理资源控制的示例命令的概述,请参见《系统管理指南:Solaris Containers-资源管理和 Solaris Zones》中的第 6  章 “资源控制(概述)”《系统管理指南:Solaris Containers-资源管理和 Solaris Zones》中的第 7  章 “管理资源控制(任务)”

资源控制功能可带来以下益处。

资源控制标志和操作

本节介绍了与资源控制关联的标志、操作和信号。

rlimit,资源限制

rlimit 是基于进程的。rlimit 可确立对某个进程占用各种系统资源的限制。由该进程创建的每个进程均从其原始进程继承此资源限制。资源限制由一对值来定义。这些值指定了当前(软)限制和最大(硬)限制。

进程可能会不可逆转地将其硬限制降为大于或等于软限制的任何值。只有具有超级用户 ID 的进程可以提高硬限制。请参见 setrlimit()getrlimit()

rlimit 结构包含用于定义软限制和硬限制的两个成员。

rlim_t     rlim_cur;       /* current (soft) limit */

rlim_t     rlim_max        /* hard limit */

rctl,资源控制

rctl 通过控制由项目数据库中定义的进程、任务和项目占用的资源来扩大基于进程的 rlimit 限制。


注 –

对于设置资源限制,rctl 机制优先于使用 rlimit。只有需要跨 UNIX 平台的可移植性时才应使用 rlimit 功能。


根据应用程序处理资源控制的方式,应用程序可分为以下几大类。根据执行的操作,可以进一步对资源控制进行分类。大多数资源控制会报告错误并终止操作。其他资源控制允许应用程序继续运行并适应资源占用减少的情况。可以为每种资源控制指定以递增的值表示的渐进操作链。

资源控制的属性列表由权限级别、阈值和超过阈值时执行的操作组成。

资源控制值和权限级别

有关资源控制的每个阈值都必须与以下某种权限级别关联:

RCPRIV_BASIC

权限级别可由调用进程的属主进行修改。RCPRIV_BASIC 与资源的软限制关联。

RCPRIV_PRIVILEGED

权限级别只能由具有权限的(超级用户)调用方进行修改。RCPRIV_PRIVILEGED 与资源的硬限制关联。

RCPRIV_SYSTEM

权限级别在操作系统实例的持续时间内固定不变。

图 5–2 显示用于设置信号权限级别(由 /etc/project 文件的 process.max-cpu-time 资源控制定义)的时间线。

局部操作和局部标志

局部操作和局部标志适用于由此资源控制块表示的当前资源控制值。局部操作和局部标志特定于值。对于为资源控制设置的每个阈值,以下局部操作和局部标志是可用的:

RCTL_LOCAL_NOACTION

超过此资源控制值时,不执行任何局部操作。

RCTL_LOCAL_SIGNAL

指定的信号(由 rctlblk_set_local_action() 设置)将被发送到在值序列中设置此资源控制值的进程。

RCTL_LOCAL_DENY

遇到此资源控制值时,将拒绝对资源的请求。如果为此控制设置了 RCTL_GLOBAL_DENY_ALWAYS,则会对所有值设置该项。如果为此控制设置了 RCTL_GLOBAL_DENY_NEVER,则会对所有值清除该项。

RCTL_LOCAL_MAXIMAL

此资源控制值表示对此控制的最大资源量的请求。如果为此资源控制设置了 RCTL_GLOBAL_INFINITE,则 RCTL_LOCAL_MAXIMAL 将指示资源控制值没有限制,永远不会超出。

全局操作和全局标志

全局标志适用于由此资源控制块表示的所有当前资源控制值。全局操作和全局标志是通过 rctladm(1M) 设置的。不能使用 setrctl() 设置全局操作和全局标志。全局标志适用于所有的资源控制。对于为资源控制设置的每个阈值,以下全局操作和全局标志是可用的:

RCTL_GLOBAL_NOACTION

超过有关此控制的资源控制值时,不执行任何全局操作。

RCTL_GLOBAL_SYSLOG

超过序列中与此控制关联的任何资源控制值时,可通过 syslog() 功能记录标准消息。

RCTL_GLOBAL_NOBASIC

此控制不允许具有 RCPRIV_BASIC 权限的任何值。

RCTL_GLOBAL_LOWERABLE

没有权限的调用方可以降低具有权限的资源控制值中有关此控制的值。

RCTL_GLOBAL_DENY_ALWAYS

超过有关此控制的控制值时执行的操作始终包括拒绝资源。

RCTL_GLOBAL_DENY_NEVER

超过有关此控制的控制值时执行的操作始终排除拒绝资源。尽管也可能会执行其他操作,但会始终授权使用该资源。

RCTL_GLOBAL_FILE_SIZE

局部操作的有效信号包括 SIGXFSZ 信号。

RCTL_GLOBAL_CPU_TIME

局部操作的有效信号包括 SIGXCPU 信号。

RCTL_GLOBAL_SIGNAL_NEVER

此控制不允许局部操作。始终授权使用该资源。

RCTL_GLOBAL_INFINITE

此资源控制支持没有限制的值这一概念。通常,没有限制的值只适用于逐渐累积型的资源(如 CPU 时间)。

RCTL_GLOBAL_UNOBSERVABLE

通常,与任务或项目相关的资源控制不支持观测的控制值。为任务或进程设置的具有 RCPRIV_BASIC 权限的控制值只有在被该进程超过时才生成操作。

与项目、进程和任务关联的资源控制集

下图显示了与任务、进程和项目关联的资源控制集。

图 5–1 任务、项目和进程的资源控制集

表示任务、项目和进程的资源控制集。

一种资源可以存在多个资源控制,每个资源控制都处在进程模型的包含级别上。资源控制可以在进程以及集合任务或集合项目的同一种资源中处于活动状态。在这种情况下,对进程的操作优先。例如,如果同时遇到 process.max-cpu-timetask.max-cpu-time 这两个控制,则首先对第一个控制执行操作。

与项目关联的资源控制

与项目关联的资源控制包括以下各项:

project.cpu-shares

授予此项目的 CPU 份额,用于公平共享调度程序 FSS(7)。

project.max-msg-ids

项目所允许的最大 System V 消息队列数。

project.max-sem-ids

项目所允许的最大 System V 信号数。

project.max-port-ids

允许的最大事件端口数。

与任务关联的资源控制

与任务关联的资源控制包括以下各项:

task.max-cpu-time

此任务进程可用的最长 CPU 时间(秒)。

task.max-lwps

此任务的进程可同时使用的最大 LWP 数。

与进程关联的资源控制

与进程关联的资源控制包括以下各项:

process.max-address-space

此进程可用的最大地址空间量(字节),即段大小的总和。

process.max-core-size

此进程创建的最大核心转储文件大小(字节)。

process.max-cpu-time

此进程可用的最长 CPU 时间(秒)。

process.max-file-descriptor

此进程可用的最大文件描述符索引。

process.max-file-size

此进程可写入的最大文件偏移(字节)。

process.max-msg-messages

消息队列中的最大消息数。该值是在 msgget() 时间从资源控制中复制的。

process.max-msg-qbytes

消息队列中的最大消息数(字节)。该值是在 msgget() 时间从资源控制中复制的。如果设置了新的 project.max-msg-qbytes 值,则仅对后续创建的值进行初始化。新的 project.max-msg-qbytes 值不影响现有值。

process.max-sem-nsems

信号集允许的最大信号数。

process.max-sem-ops

semop() 调用允许的最大信号操作数。该值是在 msgget() 时间从资源控制中复制的。新的 project.max-sem-ops 值仅影响对后续创建值的初始化,对现有值没有任何影响。

process.max-port-events

每个事件端口允许的最大事件数。

用于资源控制的信号

对于为资源控制设置的每个阈值,以下一组受限制的信号是可用的:

SIGBART

终止进程。

SIGXRES

超过资源控制限制时由资源控制功能生成的信号。

SIGHUP

当载波在断开的线路上停止时,将挂起信号发送到用于控制终端的进程组,此挂起信号即 SIGHUP。

SIGSTOP

作业控制信号。停止进程。停止信号不是来自终端。

SIGTERM

终止进程。由软件发送的终止信号。

SIGKILL

终止进程。中止程序。

SIGXFSX

终止进程。超过了文件大小限制。仅可用于具有 RCTL_GLOBAL_FILE_SIZE 属性的资源控制。

SIGXCPU

终止进程。超过了 CPU 时间限制。仅可用于具有 RCTL_GLOBAL_CPUTIME 属性的资源控制。

由于特定控制的全局属性,可能允许其他信号。


注 –

对包含非法信号的 setrctl() 的调用将失败。


图 5–2 为信号设置权限级别

为信号设置权限级别

资源控制 API 函数

资源控制 API 包含的函数可以:

对资源控制的操作-值对执行操作

以下列表包含用于设置或获取资源控制块的函数。

setrctl(2)

getrctl(2)

对局部可修改值执行操作

以下列表包含与局部可修改资源控制块关联的函数。

rctlblk_set_privilege(3C)

rctlblk_get_privilege(3C)

rctlblk_set_value(3C)

rctlblk_get_value(3C)

rctlblk_set_local_action(3C)

rctlblk_get_local_action(3C)

rctlblk_set_local_flags(3C)

rctlblk_get_local_flags(3C)

检索局部只读值

以下列表包含与局部只读资源控制块关联的函数。

rctlblk_get_recipient_pid(3C)

rctlblk_get_firing_time(3C)

rctlblk_get_enforced_value(3C)

检索全局只读操作

以下列表包含与全局只读资源控制块关联的函数。

rctlblk_get_global_action(3C)

rctlblk_get_global_flags(3C)

资源控制代码示例

资源控制的主观察进程

以下示例是主观察者进程。图 5–3 显示了主观察进程的资源控制。


注 –

换行符在 /etc/project 文件中是无效的。此处显示的换行符仅允许示例显示在列显页或显示页上。/etc/project 文件中的每一项都必须占用单独的一行。


图 5–3 主观察进程

显示主观察进程的资源控制的图

该示例的要点包括以下内容:

rctlblk_t *mlwprcb;

sigset_t smask;



/* Omit return value checking/error processing to keep code sample short */

/* First, install a RCPRIV_BASIC, v=1000, signal=SIGXRES rctl */

mlwprcb = calloc(1, rctlblk_size());	 /* rctl blocks are opaque: */

       rctlblk_set_value(mlwprcb, 1000);

       rctlblk_set_privilege(mlwprcb, RCPRIV_BASIC);

       rctlblk_set_local_action(mlwprcb, RCTL_LOCAL_SIGNAL, SIGXRES);

       if (setrctl("task.max-lwps", NULL, mlwprcb, RCTL_INSERT) == -1) {

           perror("setrctl");

           exit (1);

       }



/* Now, create the thread which waits for the signal */

        sigemptyset(&smask);

        sigaddset(&smask, SIGXRES);

        thr_sigsetmask(SIG_BLOCK, &smask, NULL);

thr_create(NULL, 0, sigthread, (void *)SIGXRES, THR_DETACHED, NULL));



/* Omit return value checking/error processing to keep code sample short */



void *sigthread(void *a)

{

        int sig = (int)a;

        int rsig;

        sigset_t sset;



        sigemptyset(&sset);

        sigaddset(&sset, sig);



        while (1) {

                 rsig = sigwait(&sset);

          if (rsig == SIGXRES) {

              notify_all_children();

              /* e.g. sigsend(P_PID, child_pid, SIGXRES); */

		     }

        }

}

列出特定资源控制的所有值-操作对

以下示例列出了特定资源控制 task.max-lwps 的所有值-操作对。该示例的要点是 getrctl(2) 使用两个资源控制块,并返回 RCTL_NEXT 标志的资源控制块。要在所有的资源控制块中迭代,请使用 rcb_tmp rctl 块反复交换此处显示的资源控制块值。

rctlblk_t *rcb1, *rcb2, *rcb_tmp;

	...

/* Omit return value checking/error processing to keep code sample short */

rcb1 = calloc(1, rctlblk_size()); /* rctl blocks are opaque: */

                               /* "rctlblk_t rcb" does not work */

rcb2 = calloc(1, rctlblk_size());

getrctl("task.max-lwps", NULL, rcb1, RCTL_FIRST);

while (1) {

     print_rctl(rcb1);

     rcb_tmp = rcb2;

     rcb2 = rcb1;

     rcb1 = rcb_tmp;        /* swap rcb1 with rcb2 */

     if (getrctl("task.max-lwps", rcb2,  rcb1, RCTL_NEXT) == -1) {

          if (errno == ENOENT) {

               break;

     } else {

          perror("getrctl");

          exit (1);

     }

     }

}

设置 project.cpu-shares 并添加新值

该示例的要点包括以下内容:

该示例将获取项目的 CPU 份额分配 project.cpu-shares,并将其值更改为 nshares

/* Omit return value checking/error processing to keep code sample short */

blk1 = malloc(rctlblk_size());

getrctl("project.cpu-shares", NULL, blk1, RCTL_FIRST);

my_shares = rctlblk_get_value(blk1);

printout_my_shares(my_shares);

/* if privileged, do the following to */

/* change project.cpu-shares to "nshares" */

blk1 = malloc(rctlblk_size());

blk2 = malloc(rctlblk_size());

if (getrctl("project.cpu-shares", NULL, blk1, RCTL_FIRST) != 0) {

     perror("getrctl failed");

     exit(1);

}

bcopy(blk1, blk2, rctlblk_size());

rctlblk_set_value(blk1, nshares);

if (setrctl("project.cpu-shares", blk2, blk1, RCTL_REPLACE) != 0) {

     perror("setrctl failed");

     exit(1);

}		     

为资源控制块设置 LWP 限制

在以下示例中,应用程序设置了具有权限的限制 3000 LWP(可能没有超过)。 此外,应用程序还设置了基本限制 2000 LWP。 超过此限制时,系统会将 SIGXRES 发送到应用程序。 收到 SIGXRES 时,应用程序可能会将通知发送给其子进程,而该进程可能会减少进程使用或需要的 LWP 数目。

/* Omit return value and error checking */



#include <rctl.h>



rctlblk_t *rcb1, *rcb2;



/*

         * Resource control blocks are opaque

         * and must be explicitly allocated.

         */

rcb1 = calloc(rctlblk_size());	



rcb2 = calloc(rctlblk_size());	





/* Install an RCPRIV_PRIVILEGED, v=3000: do not allow more than 3000 LWPs */

rctlblk_set_value(rcb1, 3000);

rctlblk_set_privilege(rcb1, RCPRIV_PRIVILEGED);

rctlblk_set_local_action(rcb1, RCTL_LOCAL_DENY);

setrctl("task.max-lwps", NULL, rcb1, RCTL_INSERT);





/* Install an RCPRIV_BASIC, v=2000 to send SIGXRES when LWPs exceeds 2000 */

rctlblk_set_value(rcb2, 2000);

rctlblk_set_privilege(rcb2, RCPRIV_BASIC);

rctlblk_set_local_action(rcb2, RCTL_LOCAL_SIGNAL, SIGXRES);

setrctl("task.max-lwps", NULL, rcb2, RCTL_INSERT);

与资源控制关联的编程问题

编写应用程序时,请考虑以下问题: