本章介绍资源控制及其属性。
可使用扩展的记帐功能确定系统上的工作负荷所占用的资源。确定占用的资源后,请使用资源控制功能为资源使用情况设置限制。 对资源设置的限制可以防止工作负荷过度占用资源。
有关资源控制以及用于管理资源控制的示例命令的概述,请参见《系统管理指南:Solaris Containers-资源管理和 Solaris Zones》中的第 6 章 “资源控制(概述)”和《系统管理指南:Solaris Containers-资源管理和 Solaris Zones》中的第 7 章 “管理资源控制(任务)”。
资源控制功能可带来以下益处。
动态设置
可以在系统运行的同时调整资源控制。
包含级别粒度
资源控制安排在项目、任务或进程的包含级别中。包含级别简化了配置,并使收集到的值更接近于特定的项目、任务或进程。
阈值保留
如果尝试将最大值设置为小于实际的资源占用额,则不会对最大值进行任何更改。
本节介绍了与资源控制关联的标志、操作和信号。
rlimit 是基于进程的。rlimit 可确立对某个进程占用各种系统资源的限制。由该进程创建的每个进程均从其原始进程继承此资源限制。资源限制由一对值来定义。这些值指定了当前(软)限制和最大(硬)限制。
进程可能会不可逆转地将其硬限制降为大于或等于软限制的任何值。只有具有超级用户 ID 的进程可以提高硬限制。请参见 setrlimit() 和 getrlimit()。
rlimit 结构包含用于定义软限制和硬限制的两个成员。
rlim_t rlim_cur; /* current (soft) limit */ rlim_t rlim_max /* hard limit */
rctl 通过控制由项目数据库中定义的进程、任务和项目占用的资源来扩大基于进程的 rlimit 限制。
对于设置资源限制,rctl 机制优先于使用 rlimit。只有需要跨 UNIX 平台的可移植性时才应使用 rlimit 功能。
根据应用程序处理资源控制的方式,应用程序可分为以下几大类。根据执行的操作,可以进一步对资源控制进行分类。大多数资源控制会报告错误并终止操作。其他资源控制允许应用程序继续运行并适应资源占用减少的情况。可以为每种资源控制指定以递增的值表示的渐进操作链。
资源控制的属性列表由权限级别、阈值和超过阈值时执行的操作组成。
权限级别可由调用进程的属主进行修改。RCPRIV_BASIC 与资源的软限制关联。
权限级别只能由具有权限的(超级用户)调用方进行修改。RCPRIV_PRIVILEGED 与资源的硬限制关联。
权限级别在操作系统实例的持续时间内固定不变。
图 5–2 显示用于设置信号权限级别(由 /etc/project 文件的 process.max-cpu-time 资源控制定义)的时间线。
局部操作和局部标志适用于由此资源控制块表示的当前资源控制值。局部操作和局部标志特定于值。对于为资源控制设置的每个阈值,以下局部操作和局部标志是可用的:
超过此资源控制值时,不执行任何局部操作。
指定的信号(由 rctlblk_set_local_action() 设置)将被发送到在值序列中设置此资源控制值的进程。
遇到此资源控制值时,将拒绝对资源的请求。如果为此控制设置了 RCTL_GLOBAL_DENY_ALWAYS,则会对所有值设置该项。如果为此控制设置了 RCTL_GLOBAL_DENY_NEVER,则会对所有值清除该项。
此资源控制值表示对此控制的最大资源量的请求。如果为此资源控制设置了 RCTL_GLOBAL_INFINITE,则 RCTL_LOCAL_MAXIMAL 将指示资源控制值没有限制,永远不会超出。
全局标志适用于由此资源控制块表示的所有当前资源控制值。全局操作和全局标志是通过 rctladm(1M) 设置的。不能使用 setrctl() 设置全局操作和全局标志。全局标志适用于所有的资源控制。对于为资源控制设置的每个阈值,以下全局操作和全局标志是可用的:
超过有关此控制的资源控制值时,不执行任何全局操作。
超过序列中与此控制关联的任何资源控制值时,可通过 syslog() 功能记录标准消息。
此控制不允许具有 RCPRIV_BASIC 权限的任何值。
没有权限的调用方可以降低具有权限的资源控制值中有关此控制的值。
超过有关此控制的控制值时执行的操作始终包括拒绝资源。
超过有关此控制的控制值时执行的操作始终排除拒绝资源。尽管也可能会执行其他操作,但会始终授权使用该资源。
局部操作的有效信号包括 SIGXFSZ 信号。
局部操作的有效信号包括 SIGXCPU 信号。
此控制不允许局部操作。始终授权使用该资源。
此资源控制支持没有限制的值这一概念。通常,没有限制的值只适用于逐渐累积型的资源(如 CPU 时间)。
通常,与任务或项目相关的资源控制不支持观测的控制值。为任务或进程设置的具有 RCPRIV_BASIC 权限的控制值只有在被该进程超过时才生成操作。
下图显示了与任务、进程和项目关联的资源控制集。
一种资源可以存在多个资源控制,每个资源控制都处在进程模型的包含级别上。资源控制可以在进程以及集合任务或集合项目的同一种资源中处于活动状态。在这种情况下,对进程的操作优先。例如,如果同时遇到 process.max-cpu-time 和 task.max-cpu-time 这两个控制,则首先对第一个控制执行操作。
与项目关联的资源控制包括以下各项:
授予此项目的 CPU 份额,用于公平共享调度程序 FSS(7)。
项目所允许的最大 System V 消息队列数。
项目所允许的最大 System V 信号数。
允许的最大事件端口数。
与任务关联的资源控制包括以下各项:
此任务进程可用的最长 CPU 时间(秒)。
此任务的进程可同时使用的最大 LWP 数。
与进程关联的资源控制包括以下各项:
此进程可用的最大地址空间量(字节),即段大小的总和。
此进程创建的最大核心转储文件大小(字节)。
此进程可用的最长 CPU 时间(秒)。
此进程可用的最大文件描述符索引。
此进程可写入的最大文件偏移(字节)。
消息队列中的最大消息数。该值是在 msgget() 时间从资源控制中复制的。
消息队列中的最大消息数(字节)。该值是在 msgget() 时间从资源控制中复制的。如果设置了新的 project.max-msg-qbytes 值,则仅对后续创建的值进行初始化。新的 project.max-msg-qbytes 值不影响现有值。
信号集允许的最大信号数。
semop() 调用允许的最大信号操作数。该值是在 msgget() 时间从资源控制中复制的。新的 project.max-sem-ops 值仅影响对后续创建值的初始化,对现有值没有任何影响。
每个事件端口允许的最大事件数。
对于为资源控制设置的每个阈值,以下一组受限制的信号是可用的:
终止进程。
超过资源控制限制时由资源控制功能生成的信号。
当载波在断开的线路上停止时,将挂起信号发送到用于控制终端的进程组,此挂起信号即 SIGHUP。
作业控制信号。停止进程。停止信号不是来自终端。
终止进程。由软件发送的终止信号。
终止进程。中止程序。
终止进程。超过了文件大小限制。仅可用于具有 RCTL_GLOBAL_FILE_SIZE 属性的资源控制。
终止进程。超过了 CPU 时间限制。仅可用于具有 RCTL_GLOBAL_CPUTIME 属性的资源控制。
由于特定控制的全局属性,可能允许其他信号。
对包含非法信号的 setrctl() 的调用将失败。
资源控制 API 包含的函数可以:
以下列表包含用于设置或获取资源控制块的函数。
以下列表包含与局部可修改资源控制块关联的函数。
以下列表包含与局部只读资源控制块关联的函数。
以下列表包含与全局只读资源控制块关联的函数。
以下示例是主观察者进程。图 5–3 显示了主观察进程的资源控制。
换行符在 /etc/project 文件中是无效的。此处显示的换行符仅允许示例显示在列显页或显示页上。/etc/project 文件中的每一项都必须占用单独的一行。
该示例的要点包括以下内容:
由于任务的限制具有权限,因此应用程序无法更改限制,也无法指定操作(如信号)。主进程通过为任务建立与基本资源控制相同的资源控制解决了此问题。主进程对资源使用相同的值或稍小的值,但执行不同的操作(信号 = XRES)。主进程将创建一个线程以等待此信号。
rctlblk 是不透明的。需要动态分配结构。
请注意,sigwait(2) 要求在创建线程之前阻塞所有的信号。
线程将调用 sigwait(2) 来阻塞信号。如果 sigwait() 返回 SIGXRES 信号,则线程将通知主进程的子进程适当减少正在使用的 LWP 的数目。此外,还应该使用每个子进程中的线程以类似方式为每个子进程建模,以等待此信号并适当调整其进程的 LWP 使用情况。
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); } } }
该示例类似于设置 pool.comment 属性并添加新属性中显示的示例。
请使用 bcopy(),而不要使用列出特定资源控制的所有值-操作对中的缓冲区交换。
要更改资源控制值,在调用 setrctl() 时请使用 RCTL_REPLACE 标志。新的资源控制块除了使用新的控制值以外,与原有的资源控制块相同。
rctlblk_set_value(blk1, nshares); if (setrctl("project.cpu-shares", blk2, blk1, RCTL_REPLACE) != 0)
该示例将获取项目的 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); }
在以下示例中,应用程序设置了具有权限的限制 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);
编写应用程序时,请考虑以下问题:
资源控制块是不透明的。 需要动态分配控制块。
如果在任务或项目中建立了基本资源控制,则建立此资源控制的进程将成为观察者。 对此资源控制块的操作将应用于该观察者。但是,不能使用此方式观察某些资源。
如果在任务或项目中设置具有权限的资源控制,则不存在观察者进程。但是,违反该限制的任何进程都将成为资源控制操作的适用对象。
只允许对每种类型执行一项操作:全局操作或局部操作。
只允许对每个进程、每个资源控制使用一个基本 rctl。