使应用程序能够在区域 (Zone) 中工作


作者:Paul Lovvik, Joseph Balenzano, 发布时间:2005 年 5 月 27 日  
摘要:本文提供的过程有助于使应用程序能够在区 域中工作(使用 Solaris 10 OS 的 Solaris Zones 功能)。该过程包括软件安装和配置以及区域限制和解决方法。本文还讨论了应用程序供应商的最佳做法。

目录:

  1. 使软件在本 地区域中工作-概述
  2. 区域限 制
  3. 无 法在本地区域中正常工作的软件
  4. 本 地区域中的安装和配置
  5. QA
  6. 要点
  7. 资源
  8. 关于作者
 
 
 
 

“我的软件是否能够在区域中工作?”

我有不少合作伙伴对于将 Solaris 10 操作系统的某些新功能与其软件集成感兴趣,在与他们合作的过程中,这是他们向我提出的有关支持 Solaris Zones 功能的一个最常见的问题。尽管 Solaris 10 OS 支持的大多数软件都能够在非全局区域 (non-global zone) 中正常工作,但是,这个问题并不容易回答,因为有许多非常实际的问题会阻碍对非全局区域的支持。如果没有一个过程来帮助指导应用程序供应商完成对非全局区 域的支持,供应商可能会无法或者难以将对区域的支持带入其产品中。

本文旨在帮助您完成在区域中支持您的应用程序的过程。此过程包括软件安装和配置以及区域限制和可能的解决方 法。本文还讨论了希望支持区 域的应用程序供应商的最佳做法。

在开始讲述这些过程和做法之前,我们首先来理顺几个与“区域”有关的术语。区域是用于虚拟化操作系统服务的软 件分区技术,它为运行应用 程序提供安全的隔离环境。Solaris 10 OS 支持全局区域 (global zone) 和非全局区域这两个概念。全局区域是 Solaris 操作环境的全局视图,它适用于各种目的和用途。每个 Solaris 实例都有一个相应的、名为全局的全局区域。在 Solaris 实例中创建的每个软件分区称作非全局区域。一个 Solaris OS 实例中可以存在多个非全局区域,而且每个非全局区域都具有一个唯一的名称。在本文档的剩余部分,“区域”一词是指非全局区域。当需要明确区分非全局区域与 全局区域时,将使用全限定区域类型加以区分。

最简单的答案

如果您希望向软件中添加区域支持,那么,了解最快的入门方法将使您受益匪浅。您将遇到什么问题?如何估算所需 工作量?

区域提供标准的 Solaris 界面和应用程序环境,而不强加新的 ABI 或 API。在绝大多数情况下,应用程序无需重新编译即可在区域中工作。这是一个很好的消息,而且仅这一点就有可能使您的应用程序符合要求。向区域中运行的进 程强加少量限制,可以保证区域中的软件不会对其他区域或全局区域造成不利影响。稍后将详细讨论所有这些限制,不过,这些限制有一个共同特征,就是它们需要 的权限仅对以前的 Solaris 发行版中的超级用户 (root) 可用。

如果您的软件以非特权用户身份运行,则只需对“我的软件是否能够在区域中工作?”问题回答“是”。这是简化区 域支持问题的最简单方法。 例如,如果您的软件在 Solaris 8 或 Solaris 9 下以非超级用户身份运行,那么,应当知道,您所使用的系统调用或库调用并不需要那些在 Solaris 10 OS 下的区域中将会受到限制的权限。如果是这种情况,则会非常好,因为您不必执行完全限定过程即可验证您的软件是否能够在区域中正常运行。

这是相当简单的答案,而且涵盖了在 Solaris 上运行的大多数软件,但是,即使在这种简单的情况下也会出现问题。问题在于,在安装、配置和管理应用程序的过程中,某些操作可能需要由特权用户来执行。而 且,如果您的软件中包含设置了 SUID(set user ID,设置用户 ID)权限位的可执行文件,则必须更加仔细地考虑是否需要对该软件的这些部分进行进一步的限定。这些都是需要引起注意的地方。在声称支持本地区域配置之 前,至少必须确保实际上已安装并执行稳健测试 (sanity test)。您会很容易被缺省情况下未在区域中公开的设备或文件系统的相关性问题弄得晕头转向。即使您确信您的软件肯定能在区域中运行,也有必要了解正确 支持软件所必需的区域配置。

如果需要由特权用户来运行您的软件,那么,您的软件很可能可以在区域中工作,但如果想确定究竟是否可以在区域 中工作,还需要执行进一步 的调查。

更困难的任务

如果您的软件确实需要由特权用户来执行,则必须执行更多的调查才能确定您的应用程序是否能够在非全局区域中正 确运行。

问题在于,您必须确定所使用的 API 是否受到限制,以至于由于安全原因而无法在区域中按预期方式运行。下一节“区域限制”列出了所有已知的区域系统调用限制。

如果您确定所使用的 API 未在区域中受到限制,那么,您的软件将能够在区域中完全正确运行。可以保证,对于大多数软件而言,的确如此。如果您使用的是受限 API,则说明您的软件在区域中运行时将受到一定的限制。如果是这种情况,仍可能支持在非全局区域中执行您的软件。该软件多半具有已知限制。可以将应用程 序修改为“识别区域”,并使之与在区域中的行为略有不同。当在区域中运行时,可以关闭应用程序的某些功能以免遇到问题。

 
 
 

由于没有为区域定义新的 ABI 或 API,因此在 Solaris 10 OS 中运行的大多数软件都能够在区域中正常运行。本节专门介绍那些不符合此规则的系统调用及关联的库调用。

可通过多种方法在您的软件中查找此类限制。自动在源代码中搜索可能是经过证明的、最全面的搜索类型,但是,还 可以通过测试或在运行时使 用诸如权限调试、apptrace(1)truss(1)dtrace(1M) 之类的工具单独查找问题。

本节尽可能地按照实际情况对各种限制进行专门介绍。在随后的章节中将会介绍如何使用各种系统实用程序来查找这 些问题。

在介绍非全局区域中各个系统调用行为的具体情况之前,我们将按顺序逐一讨论区域安全性、Solaris 10 OS 中新引入的进程权利管理框架,以及区域资源和服务虚拟。

区域安全性

每个非全局区域都有一个安全边界。该安全边界通过以下各项进行维护:

  • 采用 Solaris 10 进程权利管理 (privileges(5))、
  • 名称空间(例如 /proc/dev)隔 离,以及
  • 允许区域仅通过诸如套接字之类的联网 API 在区域间通信
进程权利管理
 

进程权利管理允许在命令、用户、角色和系统四个级别上对进程加以限制。Solaris OS 通过权限实现进程权利管理。权限可以降低与(在系统中具有完全超级用户功能的)某个用户或某个进程相关的安全风险。

权限是进程执行某项操作所需的单项权利。权利是在内核中实施的。若程序在 Solaris 基本权限集边界内运行,则该程序也在系统安全策略边界内运行。Setuid 程序是在系统安全策略边界外部运行的程序的示例。通过使用权限,程序可以不必调用 setuid

权限以离散方式枚举可在系统上执行的操作种类。程序可以使用可使其成功执行的确切权限运行。例如,用来设置日 期并将日期写入管理文件的 程序可能需要 file_dac_writesys_time 权限。借助此功能,可以不必以超级用户身份运行任何程序。

以前,系统并未遵循权限模型,而是使用超级用户模型。在超级用户模型中,进程以超级用户或用户身份运行。用户 进程被限制在对用户的目录 和文件执行操作,根进程则可在系统中的任何位置创建目录和文件。需要在用户目录外部创建目录的进程将使用 UID=0(即作 为 root)运行。安全策略依靠 DAC(discretionary access control,自主访问控制)来保护系统文件。设备节点受到 DAC 的保护。例如,由 sys 组拥有的设备只能由 sys 组的成员打开。但是,setuid 程序、文件权限和管理帐户很容易被误用。setuid 进程所允许的操作数比该进程完成其运行过程所需的操作数多。setuid 程序可能会受到来自随后将以无所不能的超级用户身份运行的侵入者的威胁。同样,能够访问超级用户口令的任何用户都可能会危及整个系统的安全。与之相反,通 过权限实施策略的系统允许在用户功能和超级用户功能之间划分等级。可以授权用户执行超出普通用户权限的活动,并对超级用户加以限制,使其拥有的权限比目前 要少。

权限模型比超级用户模型具有更大的安全性。已从进程中删除的权限不会被利用。进程权限防止程序或管理帐户获取 对所有功能的访问权限。进 程权限可为敏感文件提供额外的保护措施,而 DAC 防护功能本身可被用来获取访问权限。之后,权限可将程序和进程限制为仅具备程序所需的功能。此功能被称作最低权限原则。在实现最低权限的系统上,捕获某个 进程的侵入者只能访问该进程所具有的那些权限,不会危及其余部分的系统安全。

privileges(5) 手册页中提供了每个权限的描述。ppriv -lv 命令会将对每个权限的描述显示在标准输出中。

对于大多数权限而言,缺少该权限就会导致失败(EPERM 错误)。在某些情况下,缺少权限可能会导致系统调用的行为有所不同。在其他情况下,删除某项权限可能会导致 set-uid 应用程序出现严重的故障。

区域进程权限
 

在区域中运行的所有进程都可以识别权限。这意味着区域中的所有进程都受创建进程时为其分配的权限集合的约束。 当系统创建非全局区域时, 会为其创建一个 init(1M) 进程,作为该区域的根进程。通常,非全局区域中的所有进程都是该 init(1M) 进程的后代。init 的可继承权限集决定了该区域中进程的有效权限集。

以前规定,“基本”权限始终对非特权进程可用,而且,在缺省情况下,进程仍具有基本权限。在非全局区域中执行 的非特权进程与在全局区域中运行的非特权进程 共享同一“基本”权限集。这就是为什么从权限的角度来看,能够保证非特权软件可以在区域中运行(前提是区域的配置正确无误)。在下面列出的权限中,file_link_anyproc_infoproc_sessionproc_forkproc_exec 权限构成“基本”权限集。

表 1 列出了 Solaris 10 OS 中的可用权限,以及它们在非全局区域中是否可用。非全局区域中的权限集是全局区域中可用权限的子集。非全局区域中不可用的这些权限(DTrace 权限除外,它们是 Solaris 10 OS 中的新增权限)所提供的功能仅对以前的 Solaris 发行版中的超级用户可用。

表 1:Solaris 10 权限及其在非全局区域中的可用性
权限
区域权限
contract_event
contract_observer
cpc_cpu
dtrace_kernel
dtrace_proc
dtrace_user
file_chown
file_chown_self
file_dac_execute
file_dac_read
file_dac_search
file_dac_write
file_link_any
file_owner
file_setid
ipc_dac_read
ipc_dac_write
ipc_owner
net_icmpaccess
net_privaddr
net_rawaccess
proc_audit
proc_chroot
proc_clock_highres
proc_exec
proc_fork
proc_info
proc_lock_memory
proc_owner
proc_priocntl
proc_session
proc_setid
proc_taskid
proc_zone
sys_acct
sys_admin
sys_audit
sys_config
sys_devices
sys_ipc_config
sys_linkdir
sys_mount
sys_net_config
sys_nfs
sys_res_config
sys_resource
sys_suser_compat
sys_time
 
 
系统调用

由于进程在非全局区域中的权限受到限制,因此在用某些参数调用某些系统调用时可能会返回错误。在大多数情况 下,没有权限的进程会返回 EPERM。所有这些发生失败的情况都需要以前的 Solaris 版本中的超级用户权限。

adjtime, stime, ntp_adjtime

adjtime(2)-更正时间以允许系统时钟同步
stime(2)-设置系统时间和日期
ntp_adjtime(2)-调整本地时钟参数

限制:不能在非全局区域中设置系统的时间概念。

所需的权限: sys_time

影响:需要调整系统当前时间概念的软件(例如,为了与另一台计算机同步)。

解决方法:N/A

相关命令: date(1), nptdate(1M), xntpd(1M)

creat, chmod, open

creat(2)-创建新文件或重写现有文件
chmod(2)-更改文件的权限模式
open(2)-打开文件

限制:创建或更改设置了 S_ISVTX 模式(sticky 位)的正规文件。

所需的权限: sys_config

影响:如果在未设置可执行文件模式的正规文件(而不是目录)上设置 sticky 位,则表示该文件是交换文件。因此,系统的页面高速缓存不会用来保存设置了 sticky 位的文件的内容。可以假设此限制带来的影响很小,因为只有少数应用程序会创建设置 sticky 位的文件。直接使用此模式或通过 mkfile(1M) 间接使用此模式的系统管理员能够更深切地感受到这种影响。请注意,用于保留此类模式以便日后恢复的备份和恢复实用程序可以读取和保留文件的 sticky 位,但是将无法在恢复时重新创建具有该模式的文件。

解决方法:sticky 位仅适用于全局区域中文件系统内的文件。目前,对于在区域中执行还没有已知的解决方法。尝试针对本地区域中的正规文件设置 sticky 位的操作将失败,而且不发出错误或警告。

相关命令: mkfile(1M), chmod(1), tar(1)

ioctl

ioctl(2) -设备控制

限制:如果在适当位置存在锚点,则无法弹出 streams 模块。

所需的权限: sys_net_config

影响:锚点 (I_ANCHOR) 是防止通过调用 I_POP ioctl 来删除 STREAMS 模块的锁。将锚点置于要锁定的模块的流中。位于该锚点或其下的所有模块都将被锁定,而且只能由具有足够权限的进程弹出。在区域中,此权限不可用。

解决方法:N/A

相关命令: autopush(1M)

link, unlink

link(2), unlink(2)-链接和 解除链接文件和目录

限制:不能在区域中创建目录链接或解除目录链接。

所需的权限: sys_linkdir

影响:这可能会影响用以创建目录链接的软件的安装/配置过程。此外还会对可以创建临时目录(稍 后可通过调用 unlink(2) 来删除这些临时目录)的软件产生影响。

解决方法:在区域中允许使用指向目录的符号链接 (symlink(2))。 可以用 rmdir(2) 系统调用来代替 unlink(2) 目录功能。

相关命令: link(1M), unlink(1M)

memcntl

memcntl(2)-内存管理控制

限制: MC_LOCKMC_LOCKASMC_UNLOCKMC_UNLOCKAS 不受支持,因此进程不能锁定内存,也不能解除对内存的锁定。

所需的权限: proc_lock_memory

影响:这可能会影响需要锁定内存的软件。例如,数据库程序可能需要锁定内存,使数据表缓冲区保 留在不可分页的内存中,以 便提高性能。

解决方法:如果要锁定共享内存段,请参考 shmctl(2) 的解决方法部分。

mknod

mknod(2)-生成特殊文件

限制:不能创建块 (S_IFBLK) 或字符 (S_IFCHR) 特殊文件。

所需的权限: sys_devices

影响:需要即时创建设备节点的软件(例如 Sun Ray Server Software)将会受到此限制的影响。备份和恢复实用程序(例如 tar(1))可以读取和保留特殊文件,但是将无法在 恢复时重新创建这些特殊文件。

解决方法:在该软件中,可以忽略对特殊文件的创建。而由 zonecfg(1M) 指定的区域配置中可以包括“设备”资源,该资源可指定在引导该区域时应当创建相应的设备文件。必须从全局区域中执行特殊文件的恢复操作。

相关命令: cpio(1), disks(1M), mknod(1M), tapes(1M), tar(1)

msgctl

msgctl(2)-消息控制操作

限制: IPC_SET 不能用来增加信息队列的字节数 (msg_qbytes)。

所需的权限: sys_ipc_config

影响:动态调整信息队列大小的软件将会受到此限制的影响。

解决方法:由系统定义的、用来初始化 msg_qbytes 的限制是调用进程的 process.max-msg-qbytes 资源控制的最小执行值。因此,在初始化信息队列时,可以将 msg_qbytes 初始化为您的应用程序所需的上限。

nice

nice(2)-更改进程的优先级

限制:如果增量参数为负或者大于 40,此调用将失败。

所需的权限: proc_priocntl

影响:根据应用程序需求的性质,您的软件可能需要设置调度优先级。调用 nice 函数不会对具有调度策略 SCHED_FIFOSCHED_RR 的进程或线程的优先级产生影响。

解决方法:如果您的软件确实需要使用 nice(2) 来调整(增加)其优先级,则全局区域中的另外一个进程需要代表非全局区域中的客户机来执行该操作。或者,将运行该应用程序的非全局区域绑定到某个池也可以 实现同样的效果(除非该进程与同一区域中的其他进程争用 CPU,在这种情况下,可以使用“公平共享调度程序”来指定哪些项目应当获得更多的 CPU)。

相关命令: nice(1)

p_online

p_online(2)-返回或更改处理器的运行状态

限制: P_ONLINEP_OFFLINEP_NOINTRP_FAULTEDP_SPAREP_FORCED 标志不受支持。

所需的权限: sys_res_config

影响:这将影响需要禁用/启用 CPU 的软件。

解决方法:N/A

相关命令: psradm(1M)

priocntl

priocntl(2)-进程调度程序控制

限制:不支持(通过使用 PC_SETPARMSPC_SETXPARMS) 更改 LWP 的调度参数。

所需的权限: proc_priocntl

影响:根据应用程序需求的性质,您的软件可能需要设置内核级的 LWP 调度优先级。

解决方法:N/A

相关命令: priocntl(1)

pset_create, pset_destroy, pset_assign, pset_bind, pset_setattr, processor_bind

pset_create(2), pset_destroy(2), pset_assign(2)- 管理处理器集
pset_bind(2)-将 LWP 绑定到处理器集
pset_setattr(2)-设置处理器集属性

限制:这些函数控制处理器集的创建和管理。由于处理器是系统范围的资源,因此不允许在区域中处 理它们。

所需的权限: sys_res_config

影响:为了提高性能以及实现并行或资源控制而利用 SMP 系统将 LWP 绑定到特定处理器集的软件。由于许可方面的原因,您的软件可能会对可运行的处理器数量进行限制。

解决方法:可以使用 poolcfg(1M)pooladm(1M) 设置一个资源池,然后使用 zonecfg(1M) 和“池”属性把将要运行该应用程序的区域绑定到此资源池。可以使用 processor_bind(2) 将 LWP 绑定到单个处理器。

相关命令: psrset(1M)

shmctl

shmctl(2)-共享内存控制操作

限制: SHM_LOCKSHM_UNLOCK 不受支持,因此进程不能锁定内存,也不能解除对内存的锁定。

所需的权限: proc_lock_memory

影响:这可能会影响需要锁定内存的软件。例如,数据库程序可能需要锁定内存,使数据表缓冲区保 留在不可分页的内存中,以 便提高性能。

解决方法:如果您是为了提高性能而锁定内存,则需要弄清楚 Solaris 的锁定共享内存 (Intimate Shared Memory, ISM) 功能 (shmat(2) SHM_SHARE_MMU) 的用法。使用 ISM 有很多优点,其中一项优点就是可以锁定 ISM 页面,这样会缩短内核代码路径并防止换出页面,从而大大提高了性能。应当注意的是,使用 ISM 可能会导致某些动态重新配置事件(例如,使用 cfgadm(1M) 命令调用的事件)失败。

socket

socket(2)-创建通信端点

限制:如果尝试在协议设置为 IPPROTO_RAWIPPROTO_IGMP 的情况下创建原始套接字,则将返回 EPROTONOSUPPORT 错误。

所需的权限: net_rawaccess

影响:这将影响使用原始套接字接口来实现网络协议的软件或者需要创建/检查 TCP/IP 数据包头的软件。

解决方法:N/A

相关命令:N/A

swapctl

swapctl(2)-管理交换空间

限制:不能添加 (SC_ADD) 或删除 (SC_REMOVE) 交换资源。

所需的权限: sys_config

影响:需要添加或删除交换资源的所有软件都将受到影响。这将很有可能影响您的安装和配置。

解决方法:交换空间是系统范围的资源,因此必须从全局区域进行配置。

相关命令: swap(1M)

uadmin

uadmin(2)-管理控制

限制:A_REMOUNT?¢A_FREEZE oí A_DUMP 命令不受支持 (ENOTSUP)。A_SHUTDOWN 命令的 AD_IBOOT 函数不受支持 (ENOTSUP)。

所需的权限: sys_config

影响:这可能会影响在某些特定条件下需要强制执行故障转储的软件。

解决方法:N/A

相关命令: uadmin(1M)

库函数

与系统调用不同,由于区域中进程的权限受限,因此某些库调用可能会返回错误。在大多数情况下,没有适当权限的 进程将会返回 EPERM。 这些发生失败的情况需要以前的 Solaris 版本中的超级用户权限。

clock_settime

clock_settime(3RT)-高分辨率时钟操作

限制:不能设置 CLOCK_REALTIMECLOCK_HIGHRES 时钟,因为它们是系统范围的时钟。

所需的权限: sys_time

影响:实时软件最有可能受到无法设置时钟这一限制的影响。

解决方法:N/A

cpc_bind_cpu

cpc_bind_cpu(3CPC)-将请求集合绑定到硬件计数器

限制:此函数将请求集合绑定到指定的 CPU 并度量在该 CPU 上发生的事件,而不管正在运行哪个 LWP。这在区域中是不允许的,因为这样您就可以监视在您的区域以外运行的进程的 CPU 事件。此调用之所以会失败,是因为该函数尝试打开 /devices 目录中代表 CPU 的特殊文件,而 /devices 目录不是区域名称空间的一部分。由于没有 /devices, 因此,由 cpc_bind_cpu(3CPC) 发出的 open(2) 系统调用将生成一个 ENOENT 返回代码。

所需的权限: cpc_cpu

影响:这可能会影响您的开发环境。例如,您可能正在通过调用 cpc_bind_cpu(3CPC) 来确定您的代码的高速缓存命中率。

解决方法:允许在区域中使用 cpc_bind_curlwp(3CPC), 因此您可以监 视发出调用的 LWP 的 CPU 计数器。

mlock, munlock, mlockall, munlocall, plock

mlock(3C), munlock(3C)- 锁定或解除锁定内存中的页
mlockall(3C), munlockall(3C)- 锁定或解除锁定地址空 间
plock(3C)-锁定或解除锁定内存进程、文本或数据

限制:不能使用这些库函数来锁定内存或解除对内存的锁定。此问题与针对 memcntl(2) 出现的问题相同。

所需的权限: proc_lock_memory

影响:这可能会影响需要锁定内存的软件。例如,数据库程序可能需要锁定内存,使数据表缓冲区保 留在不可分页的内存中,以 便提高性能。应当注意的是,锁定内存可能会导致某些动态重新配置事件(例如,使用 cfgadm(1M) 命令调用的事件)失败。

解决方法:如果要锁定共享内存段,则应当考虑使用 shmctl 中介绍的解决方法。

pthread_setschedparam

pthread_setschedparam (3C)-访问动态线程调度参 数

限制:不能更改线程的基础调度策略和参数。此问题与针对 priocntl 出现的问题相同。

所需的权限: proc_priocntl

影响:根据应用程序需求的性质,您的软件可能需要设置内核级的线程和基础 LWP 调度优先级。

解决方法:N/A

timer_create

timer_create(3RT)-创建计时器

限制:不能使用高分辨率系统时钟 (CLOCK_HIGHRES) 创建计时器。

所需的权限: proc_clock_highres

影响:需要高分辨率计时器的软件。

解决方法:N/A

t_open

t_open(3NSL)-建立传输端点

限制:STREAMS 驱动程序 /dev/rawip 是 TLI 传输提供器,可用来提供对 IP 的原始访问。此设备节点在区域中不可用,因此,当在该驱动程序上使用此调用时,将返回 ENOENT 错误。

所需的权限: net_rawaccess

影响:这还将影响使用 /dev/rawip 设备来实现网络协议的软件、需要创建/检查 TCP/IP 数据包头的软件以及其他一些软件。

解决方法:N/A

下面的库列表中提供的 API 在区域中不受支持。共享对象存在于区域的 /usr/lib 目录中,因此,如果代码中包括对这些库的引用,将不会出现链接时间错误。可以检查自己的 make 文件,以确定应用程序是否已显式绑定到其中的任何库,并可以在执行应用程序时,使用 pmap(1) 来验证是否未动态装入其中的任何库。

  • libdevinfo(3LIB)-设备信息库
  • libcfgadm(3LIB)-配置管理库
  • libpool(3LIB)-池配置处理库
  • libtnfctl(3LIB)-TNF 探测控制库
  • libsysevent(3LIB)-系统事件接口库
设备

区域具有一组受限制的设备,这些设备主要由构成 Solaris 编程 API 一部分的伪设备组成。这些设备包括 /dev/null/dev/zero/dev/poll/dev/random/dev/tcp 等。除非管理员进行了相应的配置,否则不能直接从区域中访问物理设备。由于设备通常是系统中的共享资源,因此,在使设备在区域中可用时需要设置一些限制, 以免危及系统安全。

  • /dev 名称空间由指向 /devices 中物理路径的符号链接(逻辑路径)组成。只在全局区域中可用的 /devices 名称空间可反映由驱动程序创建的附加设备实例的当前状态。只有逻辑路径 /dev 在非全局区域中可见。
  • 正如“区域限制”一节中提到的那样,区域中的进程不能创建新设备节点(即,mknod(2) 将会失败)。如果指定了 /dev 中的文件,create(2)link(2)mkdir(2)rename(2)symlink(2)unlink(2) 系统调用将会失败并发出 EACCES 错误。您可以创建指向 /dev 中的某个项的符号链接 symlink(2),但是不能在 /dev 中创建该链接。
  • 能够公开系统数据的设备只能在全局区域中使用。此类设备的示例包括:dtrace(7D)kmem(7D)ksyms(7D)kmdb(7D)trapstat(1M)lockstat(7D) 等。
  • /dev 名称空间由构成缺省“安全”驱动程序集的设备节点以及由 zonecfg(1M) 命令为该区域指定的设备节点组成。
  • 支持 DLPI 编程接口的所有 NIC 设备在非全局区域中都不可访问。此类设备节点的示例包括:hme(7D)ce(7D)ge(7D)eri(7D)bge(7D)dmfe(7D)dnet(7D)e1000g(7Delxl(7D)iprb(7D)pcelx(7D)pcn(7D)qfe(7D)rtls(7D)sk98sol(7D)skfp(7D)spwr(7D)

下面的设备列表在非全局区域的名称空间中不可见。除了 cpuidfcipksyms 以外,这些设备的接口不是公共接口(接口稳定性:专用),因此,这应当不会影响正常工作的软件。

  • mem(7D), kmem(7D), allkmem(7D)- 物理或虚拟内存访问
  • kmdb(7D)-内核调试器
  • ksyms(7D)-内核符号
  • dtrace(7D)-DTrace 动态跟踪工具
  • lockstat(7D)-DTrace 内核锁定检测过程提供器
  • fcip(7D)-通过光纤通道数据报封装驱动器的 IP/ARP
联网

每个非全局区域都有自己的逻辑网络和回送接口。上层流与逻辑接口之间的绑定受到限制,因此流只能与同一区域中 的逻辑接口建立绑定。与之 类似,来自逻辑接口的数据包只能传递给与该逻辑接口在同一区域中的上层流。到回送地址的绑定将保留在区域内,但以下情况例外:当一个区域中的流试图访问另 一区域中某一接口的 IP 地址时。

尽管可以将一个区域中的应用程序绑定到特权网络端口,但是它们无法控制网络配置(包括 IP 地址和路由表)。

 
 
 

并非所有的软件都能在本地区域中正常工作。本节将研究如何检测和诊断执行过程中所出现的问题的根源,以及如何 使正在本地区域中运行的软 件正常工作(大多可以通过禁用某些功能来实现)。

检测本地区域中的软件故障

当系统调用因权限错误而失败时,问题的原因并非总是显而易见的。要调试这样的问题,可以使用一种称为权限调试 的工具。当针对某个进程启 用了权限调试,内核会报告该进程的控制终端上缺少权限。(使用 ppriv(1)-D 选项为进程启用调试功能。)另外,管理员可以通过在全局区域的 /etc/system 文件中设置系统变量 priv__debug = 1 来启用系统范围的权限调试。

global# zlogin redzonene
redzone# ls -l /tmp
total 8
drwxr-xr-x 2 root root 69 Apr 19 22:11 testdir
redzone# ppriv -D -e unlink /tmp/testdir
unlink[1245]: missing privilege "sys_linkdir"
(euid = 0, syscall = 10)
needed at tmp_remove+0x6e
unlink: Not owner
redzone# ppriv -D -e rmdir /tmp/testdir
redzone# ls -l /tmp
total 0
 

Solaris 10 OS 提供许多可用来在运行时标识和检查您的应用程序发出的系统/库调用的工具。我们将介绍其中的三个工具:apptrace(1M)dtrace(1M)truss(1M)。尽管 dtrace(1M) 命令在非全局区域中不受支持,但是,可以使用 DTrace 在全局区域中监视正在非全局区域中执行的进程,因为全局区域对于系统上的所有进程均可见。

在确定了无法在区域中正常工作的系统调用或库调用之后,可以通过对系统调用使用 apptrace(1)dtrace(1M)truss(1) 来检查参数列表。以下示例说明 msgctltst.c 代码将无法在区域中正常工作,因为它使用 IPC_SET 来增加信息队列的大小。在区域中,可以减小队列的大小,但是不能增加队列的大小。

redzone# cat msgctltst.c 

#include < 0) {
fprintf (stderr,"msgget(IPC_PRIVATE), errno = %d\n", errno);
}

if ((rc = msgctl(msgid, IPC_STAT, &msgc)) < 0) {
fprintf (stderr,"msgctl(IPC_STAT), errno = %d\n", errno);
}

msgc.msg_qbytes--;
if ((rc = msgctl(msgid, IPC_SET, &msgc)) < 0) {
fprintf (stderr,"msgctl(IPC_SET), errno = %d\n", errno);
}

msgc.msg_qbytes++;
if ((rc = msgctl(msgid, IPC_SET, &msgc)) < 0) {
fprintf(stderr,"msgctl(IPC_SET) growing queue,
errno = %d\n", errno);

}
return(0);
}
 

以下示例说明如何使用 truss(1) 来检查由 msgsctlstst 发出的系统调用。如果尝试增加信息队列中的字节数,该示例将失败。

redzone# truss ./msgctltst
execve("msgctltst", 0x08047EA8, 0x08047EB0) argc = 1
...
...
...
msgget(IPC_PRIVATE, IPC_CREAT) = 3
msgctl(3, IPC_STAT, 0x08047E00) = 0
msgctl(3, IPC_SET, 0x08047E00) = 0
msgctl(3, IPC_SET, 0x08047E00) Err#1 EPERM [sys_ipc_config]
...
...
...
...
...

 

apptrace(1) 实用程序运行指定的可执行程序,并跟踪该可执行程序对 Solaris 共享库发出的所有函数调用。对于每个可跟踪的函数调用,apptrace(1) 都报告所调用的库接口的名称、所传递参数的值以及返回值。同样,下面的示例说明在将第二个参数设置为 IPC_SET (0xb) 时,应用程序如何调用 msgctl(2)

redzone# apptrace ./msgctltst
-> msgctltst -> libc.so.1:atexit(0x80505a8, 0xd27e6fd0, 0x0) ** NR
-> msgctltst -> libc.so.1:atexit(0xd27e6fd0, 0x0, 0x0) ** NR
-> msgctltst -> libc.so.1:atexit(0x80508d9, 0xd27e6fd0, 0x0) ** NR
-> msgctltst -> libc.so.1:void __fpstart(void)
<- msgctltst -> libc.so.1:__fpstart() = 0xd254cc3c
-> msgctltst -> libc.so.1:int msgget(key_t = 0x0, int = 0x200)
<- msgctltst -> libc.so.1:msgget() = 0x1
-> msgctltst -> libc.so.1:int msgctl(int = 0x1, int = 0xc,
struct msqid_ds * = 0x8047da0)
<- msgctltst -> libc.so.1:msgctl()
-> msgctltst -> libc.so.1:int msgctl(int = 0x1, int = 0xb,
struct msqid_ds * = 0x8047da0)
<- msgctltst -> libc.so.1:msgctl()
-> msgctltst -> libc.so.1:int msgctl(int = 0x1, int = 0xb,
struct msqid_ds * = 0x8047da0)
<- msgctltst -> libc.so.1:msgctl() = 0xffffffff
...
...
...
...
...
...
 

同一个程序,使用 Dtrace,并从全局区域执行。如果将第二个参数设置为 IPC_SET, 则每次调 用 msgctl(2) 函数时,都将触发 DTrace 探测 syscall::msgsys:entry。 如果系统调用返回错误,则将触发 syscall::msgsys:return 探测。将在非全局区域 redzone 中执行 msgctltst 程序。由此可见,DTrace 比 truss(1)apptrace(2) 的功能强大,因为我们可以实际检查数据结构,并有条件地执行探测操作以及显示调用栈跟踪。

global# cat msgctl.d

#!/usr/sbin/dtrace -Cqs
#include
 

如果无法在非全局区域中进行测试,则应当在全局区域中测试您的软件,但是需要使用 ppriv(1) 命令从该程序的权限集中删除在非全局区域中不可用的权限。如果您的软件在删除了这些权限的全局区域中失败,它在非全局区域中也将失败。此方法不会捕捉对非 全局区域中不可用的设备接口和库的访问,因此,建议您在非全局区域中测试软件。以上所介绍的运行时工具只会查找所实行的代码路径中的错误,无法代替代码检 查功能。

防御性编程和错误报告
何时需要在本地区域中具有不同的行为?
 

某些软件无法像在全局区域中那样在非全局区域中完全发挥出所有功能。tar(1) 命令就是其中的一个示例。当在区域中运行时,tar(1) 能够创建归档文件(这些归档文件中保留了各个文件的 sticky 位),但是无法将设置了 sticky 位的文件重新写入文件系统。在这种情况下,tar(1) 命令将会失败并不会发出任何提示,因为 chmod(2) 系统调用不会在出现这种问题时进行故障报告。

这样的软件应当根据其运行环境(全局区域或非全局区域)以不同的方式工作。如果 tar(1) 命令将这种情况报告为警告,以便您至少能够发现错误,那就更好了。可以通过以下方法来实现此操作:通过 tar(1) 检测软件是否在非全局区域中运行,并在每次将设置了 sticky 位的正规文件写入文件系统时记录一个警告。

同样,不难想象,如果某个应用程序使用其他受到区域限制的系统调用,那么,当在非全局区域中执行该应用程序 时,该应用程序的某些功能会 被禁用。这允许软件尽可能正常运行,而不会给软件用户带来故障诊断方面的负担。

在本地区域中检测执行情况
 

如果决定了您的软件必须识别区域(原因可能如上),可以利用所提供的 API 确定区域的标识。

  • getzoneid(3C)-返回调用进程的区域 ID
  • getzoneidbyname(3C)-返回与命名区域相对应的区域 ID
  • getzonenamebyid(3C)-将由区域 ID 标识的区域的名称存储到用户提供的缓冲区中

全局区域 ID GLOBAL_ZONEID 是在 /usr/include/sys/zone.h 中定义的。

global# cat myzone.c
#include
 

从全局区域中执行代码:

global# ls -l zonename
-rwxr-xr-x 1 root root 5704 Apr 19 23:06 zonename
global# ./myzone
Global Zone!
global
 

从非全局区域 redzone 中执行代码:

global# zlogin redzone
[Connected to zone 'redzone' pts/5]
Last login: Tue Apr 19 22:01:08 from 192.168.2.2
Sun Microsystems Inc. SunOS 5.10 s10_71 December 2004
redzone# cd zonetest
redzone# zoneadm list
redzone
redzone# ./myzone
redzone
 
 
 
 

软件安装失败可能是由以下两个问题引起的:

  • 只读文件系统
  • CD-ROM 访问

在创建区域时,可以使用两个选项来创建区域的根文件系统:稀疏根模型和完全根模型。完全根模型将所有必需的软 件包和任何选定的可选 Solaris 软件包安装到区域的专用文件系统中,因此可提供最大化的配置能力。此模型的优点是:允许区域管理员定制其区域的文件系统布局(例如,创建 /usr/local), 并可以添加任意非随附或第三方软件包。此模型的缺点是:无法共享虚拟内存系统共享的可执行文件和共享库中的文本段,并且磁盘使用量也会显著增加-如此进行 配置的每个非全局区域约增加 2 GB。

稀疏根模型只安装根软件包的子集(即将 pkginfo(4) 参数 SUNW_PKGTYPE 设置为 root 的根软件包)并使用只读回送文件系统来访问其他文件,从而可优化对象的共享。这与配置无盘客户机的方式类似,其中 /usr 和其他文件系统通过网络与 NFS 挂载。缺省情况下,在该模型中,/lib/platform/sbin/usr 目录是作为只读的回送文件系统挂载的。此模型的优点在于可提供更高的性能,原因是可以有效地共享可执行文件和共享库,并且区域本身的磁盘使用量会小很多。 稀疏根模型只需要将大约 100 MB 的文件系统空间用于区域本身。

在稀疏根模型区域中,任何需要在 /usr(或任何其他只读回送文件系统)中安 装组件的安装软件都将失 败。

第二个问题与 CD-ROM 设备有关。可通过两种方法来访问 CD-ROM。一种常见的方法就是将全局区域中的 /cdrom 目录回送挂载到非全局区域中:

# zonecfg -z myzone
add fs
set dir=/cdrom
set special=/cdrom
set type=lofs
set options=[nodevices]
end
 

如果您使用此方法,而且在安装过程中需要多个 CD 卷,则需要将 CD 从全局区域中弹出。安装脚本中 CD-ROM 设备的任何显式弹出 (eject(1)) 都将失败。在非全局区域中访问 CD-ROM 设备的另一种方法是,将物理设备从全局区域导出到非全局区域,但是不建议使用此方法。如果您选择使用此方法,则应当注意,卷管理守护进程 (vold(1M)) 无法在非全局区域中工作。

Solaris 软件包

每个区域都维护自己的软件包和修补程序数据库。可将软件包或修补程序单独安装到一个非全局区域中,也可以从全 局区域安装到所有区域中。 区域环境中的打包行为会根据以下因素而有所变化:

  • pkgadd(1M) 中对 -G 选项的使用
  • pkginfo 文件中对 SUNW_PKG_ALLZONES、code>SUNW_PKG_HOLLOW 和 SUNW_PKG_THISZONE 变量的设置(请参见 pkginfo(4) 了解详细信息)
  • 在其中调用 pkgadd(1M) 的区域的类型,即全局区域或非全局区域

表 2 显示了区域环境中的打包行为,这些行为会根据以下因素而有所变化。

pkinfo 变量值
全局区域 pkgadd
全局区域 pkgadd -G
本地区域 pkgadd
本地区域 pkgadd -G

SUNW_PKG_ALLZONES
false

SUNW_PKG_HOLLOW
false

SUNW_PKG_THISZONE
false

添加到 gz、当前的 lz 和以后的 lz 中

仅添加到 gz 中,而不添加到当前的 lz 或以后的 lz 中

仅添加到此 lz 中

仅添加到此 lz 中

SUNW_PKG_ALLZONES
true

SUNW_PKG_HOLLOW
false

SUNW_PKG_THISZONE
false

添加到 gz、当前的 lz 和以后的 lz 中

不允许执行任何操作

不允许执行任何操作

不允许执行任何操作

SUNW_PKG_ALLZONES
true

SUNW_PKG_HOLLOW
true

SUNW_PKG_THISZONE
false

添加到 gz 中

添加到当前 lz 和以后 lz 中的 pkginfo db 中

不允许执行任何操作

不允许执行任何操作

不允许执行任何操作

SUNW_PKG_ALLZONES
true

SUNW_PKG_HOLLOW
true

SUNW_PKG_THISZONE
true

无效的选项组合

无效的选项组合

无效的选项组合

无效的选项组合

SUNW_PKG_ALLZONES
false

SUNW_PKG_HOLLOW
true

SUNW_PKG_THISZONE
false

无效的选项组合

无效的选项组合

无效的选项组合

无效的选项组合

SUNW_PKG_ALLZONES
false

SUNW_PKG_HOLLOW
true

SUNW_PKG_THISZONE
true

无效的选项组合

无效的选项组合

无效的选项组合

无效的选项组合

SUNW_PKG_ALLZONES
false

SUNW_PKG_HOLLOW
false

SUNW_PKG_THISZONE
true

仅添加到 gz 中,而不添加到当前的 lz 或以后的 lz 中

仅添加到 gz 中,而不添加到当前的 lz 或以后的 lz 中

仅添加到此 lz 中

仅添加到此 lz 中

SUNW_PKG_ALLZONES
true

SUNW_PKG_HOLLOW
false

SUNW_PKG_THISZONE
true

无效的选项组合

无效的选项组合

无效的选项组合

无效的选项组合

 

说明:
gz = 全局区域
lz = 非全局区域

“无效的选项组合”是指软件包属性设置毫无意义,对于这三个属性的设置,并非所有可能的组合都合法。它们应当 由 pkgmk(1M) 捕获,而且不应当创建软件包。

“不允许执行任何操作”是指 pkgadd 命令将输出错误消息,而且无法基于以下三个因素的组合来添加软件包:命令行选项、软件包属性设置以及正在其中运行 pkgadd 的区域的类型。

配置问题

使软件在区域中工作仅是问题的一部分。系统管理员必须了解如何为他们打算运行的软件适当地配置其区域。区域有 许多可能的配置,本节并未 涵盖所有不同的配置情况,而是将重点放在一些为软件管理用户提供的策略上,这些策略规定如何针对所需的区域配置进行有用的配置和通信。

可使用多种不同的方法来配置区域。如上所述,稀疏根区域 (Sparse Root Zone) 将使用在全局区域和非全局区域之间共享的文件(如 /lib/usr),而完全根区域 (Whole Root Zone) 则是自己维护所有文件的副本。稀疏根区域环境比完全根区域环境的限制性大,因为共享目录在非全局区域中以只读文件系统形式公开。区域配置的限制性越小,灵 活性越高,但这会占用额外的资源(特别是硬盘驱动器空间和内存)。为了在软件可以部署到的区域配置中实现最大灵活性,一定要使用限制性最大、但是尽可能合 理的区域配置。

缺省的区域配置是在区域中只置备少数几个设备的稀疏根区域。与全局区域共享的目录(采用只读回送挂载)包括:/lib/platform/sbin/usr。从部署非随附软件的角度看,这提供了限制性相当大的区域,因为在安装和执行软件的过程中,不可能修改文件或将 它们写入到这些目录中。对于软件来说,最好考虑将缺省区域配置作为起始点。它提供适用于区域的最大权限集,从磁盘空间需求的角度看,上面提到的目录限制似 乎是一种不错的折衷方法。

从此缺省区域配置开始,需要搜索和记录在区域中成功部署软件所必需的配置,这一点很重要。如果在安装过程中需 要写入只读目录,则应当考 虑修改软件。如果不能通过修改软件来解决此问题,请对区域进行相应的配置。请记住,您是在确定可在其中安装和执行软件的限制性最大的环境。由此可以进行更 自由的配置,而不必担心会出现任何问题。

确保跟踪所需区域配置的所有元素。这应当包括删除所继承的目录、设备配置和网络要求。这些信息应当包括在安装 文档或任何相关的配置指南 中。

不再使用此策略的配置详细信息能够帮助系统管理员为多个软件库和应用程序配置单个非全局区域。通过声明每个软 件的最低要求,一组软件的 最低区域配置将是每个软件包的最低区域配置的简单累加。此方案简化了如下任务:即为区域部署规划必需的资源。

 
 
 

一旦确定了要用来部署软件的限制性最大的区域配置,就应当验证软件是否能够在该配置中正常工作。非全局区域环 境比全局区域环境的限制性 大。从调用各个系统调用所需的权限以及是否能够使用特定设备或修改特定目录中的内容的角度看,的确如此。在进行迁移以支持具有全局区域和非全局区域的 Solaris 10 OS 时,可以充分利用这一点。您只需在非全局区域中进行 QA 测试,完全不必为了增加本地区域支持而重复执行 QA 测试标准。

如果软件能在非全局区域中完全正常工作,也定会在全局区域中完全正常工作。在非全局区域中完成 QA 测试之后,只需验证部署工作是否能够在全局区域中正常进行即可。

 
 
 
  • 如果您的软件不需要特殊超级用户权限即可在以前的 Solaris 版本中运行,则它可以在区域中工作。可将工作重点放在安装、配置和管理任务上。
  • 进行测试,以便当软件在区域中运行时,找出限制性尽可能大的区域配置。这便于为在公共区域中运行的软件 集确定必需的区域配置。
  • 如果软件能在非全局区域中完全正常工作,也将会在全局区域中完全正常工作。在非全局区域中完成 QA 测试之后,只需验证部署工作是否能够在全局区域中正常进行即可。
 
 
 
 
 
 

Paul Lovvik 已在 Sun 工作七年,他是市场开发工程部门中某个组的首席工程师,该部门致力于使合作伙伴采用面向 x86 平台的 Solaris OS。在过去的一年中,Paul 及其工程团队已经帮助许多合作伙伴向其产品中添加了对面向 x86 的 Solaris 的支持。

Joseph Balenzano 已在 Sun 工作七年,他现在是市场开发工程部门中某个组的工程师,该部门致力于使合作伙伴采用面向 x86 平台的 Solaris OS。他有 20 多年为 ISV 开发软件的经验。