
4.8 资源和限制
Linux系统上运行的程序会受到资源限制的影响。它们可能是硬件方面的物理性限制(例如内存)、系统策略的限制(例如,允许使用的CPU时间)或具体实现的限制(如整数的长度或文件名中所允许的最大字符数)。UNIX规范定义了一些可由应用程序决定的限制。第7章对限制及突破限制的后果做了进一步讨论。
头文件limits.h中定义了许多代表操作系统方面限制的显式常量,如表4-8所示。
表4-8

还有许多其他对应用程序有用的限制,请参考你自己系统中的头文件。
注意:NAME_MAX是特定于文件系统的。为了写可移植性更好的代码,你应该使用pathconf函数。详细信息请参考pathconf的手册页。
头文件sys/resource.h提供了资源操作方面的定义,其中包括对程序长度、执行优先级和文件资源等方面限制进行查询和设置的函数:

id_t是一个整数类型,它用于用户和组标识符。在头文件sys/resource.h中定义的rusage结构用来确定当前程序已耗费了多少CPU时间,它至少包含表4-9所示的两个成员。
表4-9

timeval结构定义在头文件sys/time.h中,它包含成员tv_sec和tv_usec,分别代表秒和微秒。
一个程序耗费的CPU时间可分为用户时间(程序执行自身的指令所耗费的时间)和系统时间(操作系统为程序执行所耗费的时间,即执行输入输出操作的系统调用或其他系统函数所花费的时间)。
getrusage函数将CPU时间信息写入参数r_usage指向的rusage结构中。参数who可以是表4-10所示的常量之一。
表4-10

我们将在第11章讨论子进程和任务优先级,但考虑到完整性,我们将在这里简单介绍它们对系统资源的影响。就现在而言,了解下面一点就够了:每个运行的程序都有一个与之关联的优先级,优先级越高的程序将分配到更多的CPU可用时间。
普通用户只能降低其程序的优先级,而不能升高。
应用程序可以用getpriority和setpriority函数确定和更改它们(和其他程序)的优先级。被优先级函数检查或更改的进程可以用进程标识符、组标识符或用户来确定。which参数指定了对待who参数的方式,如表4-11所示。
表4-11

因此,为确定当前进程的优先级,你可以调用:

setpriority函数用于设置一个新的优先级(如果可能的话)。
默认的优先级是0。正数优先级用于后台任务,它们只在没有其他更高优先级的任务准备运行时才执行。负数优先级使一个程序运行更频繁,获得更多的CPU可用时间。优先级的有效范围是-20~+20。这很容易让人困惑,因为数值越高,执行优先级反而越低。
getpriority在成功时返回一个有效的优先级,失败时返回-1并设置errno变量。因为-1本身是一个有效的优先级,所以在调用getpriority之前应将errno设置为0,并在函数返回时检查它是否仍为0。setpriority在成功返回0,否则返回-1。
系统资源方面的限制可以通过getrlimit和setrlimit来读取和设置。这两个函数都利用一个通用结构rlimit来描述资源限制。该结构定义在头文件sys/resource.h中,它包含表4-12所示的成员。
表4-12

类型rlim_t是一个整数类型,它用来描述资源级别。一般来说,软限制是一个建议性的最好不要超越的限制,如果超越可能会导致库函数返回错误。硬限制如果被超越,则可能会导致系统通过发送信号的方式来终止程序的运行。例如,当CPU时间限制被超越时系统会发送SIGXCPU信号,数据长度限制被超越时系统会发送SIGSEGV信号。程序可以把自己的软限制设置为小于硬限制的任何值。它也可以减小自己的硬限制。但只有以超级用户权限运行的程序才能增加硬限制。
有许多系统资源可以进行限制,它们由rlimit函数中的resource参数指定,并在头文件sys/resource.h中定义,如表4-13所示。
表4-13

下面的实验给出了一个程序limits.c,它模拟一个典型的应用程序。该程序设置并超越了一个资源限制。
实验 资源限制
(1)首先,把你在程序中要用到的所有函数对应的头文件包含进来:

(2)void work( )函数将一个字符串写入一个临时文件10000次,然后做一些算术运算以产生CPU负载:

(3)main函数调用work函数,然后用getrusage函数来发现它耗费的CPU时间,并把该信息显示在屏幕上:

(4)接着,main函数分别调用getpriority和getrlimit来发现它的当前优先级和文件大小限制:

(5)最后,我们用setrlimit设置文件大小限制并再次调用work,这次work函数的执行会失败,因为它试图创建一个太大的文件:

当运行这个程序时,你可以看到消耗的CPU资源有多少以及程序运行的默认优先级。一旦设置了文件大小限制,程序就不能往临时文件里写入多于2048个字节了。

你可以用nice命令启动程序来改变程序的优先级。这里,你看到程序的优先级变成了+10。因此,程序的执行时间变长了。

实验解析
limits程序通过调用work函数来模拟一个典型程序的行为。它执行一些运算并产生一些输出,在本例中,它输出大约150K字节的数据到临时文件。它调用资源函数来发现其优先级和文件大小限制。在本例中,文件大小限制未设置,所以你想创建多大的文件就可以创建多大的文件(只要磁盘空间允许)。随后,程序设置它的文件大小限制为2K并再次执行一些工作。此时,work函数的调用失败了,因为它不能创建太大的临时文件。
你也可以通过bash的ulimit命令为在某一特定shell中运行的程序设置限制。
在本例中,出错信息Error writing to temporary file(写临时文件出错)可能不会像你期望的那样打印出来。这是因为当资源限制被超越时,一些系统(如Linux 2.2和后续版本)会通过发送信号SIGXFSZ的方式来终止程序。你将在第11章学习有关信号及其使用的更多知识。其他一些POSIX兼容的系统可能只是让资源限制被超越的函数返回一个错误。