Linux环境(六)--资源与限制
资源与限制
运行在Linux系统上的程序是有资源限制的。这些也许是硬件引起的限制(例如内存),也许由系统策略引起的限制(例如,允许 的CPU时间),或者是实现的限制(例如,整数的尺寸或是文件名允许的最大字符个数)。Unix规范定义了一些可以由程序确定的限制。在第7章我们会进行 更为深入的讨论。
limits.h头文件定义了许多表示操作系统限制的常量。他们包括:
限制常量 用途
NAME_MAX 文件名中的最大字符个数
CHAR_BIT 一个字符值的位数
CHAR_MAX 最大字符值
INT_MAX 最大int值
还有许多其他的可以为程序使用的限制,要得到更为详细的信息,我们可以查看我们安装的头文件。
注意:NAME_MAX是文件系统相关的。为了更为可移植的代码,我们应使用pathconf函数。我们可以查看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时间。他必须至少包含下列成员:
rusage成员 描述
struct timeval ru_utime 使用的用户时间
struct timeval ru_stime 使用的系统时间
timeval结构定义在sys/time.h文件中,包含分别代表秒与微秒的tv_sev与tv_usec域。
一个程序所消耗的CPU时间被分为用户时间(程序本身执行他自己的指令所消耗的时间)和系统时间(操作系统执行程序的行为所消耗的时间;也就是说是消耗在执行输入和输出的系统调用或是其他的系统函数上的时间)。
getrusage函数将CPU时间信息写入由参数r_usage所指向的rusage结构。who参数可是下面的常之一:
who常量 描述
RUSAGE_SELF 只返回当前程序的使用
RUSAGE_CHILDREN 同时也包含子进程的使用信息
我们会在第11章讨论子进程以及任务优先级,但是为了完整,我们会在这里讨论他们所涉及的系统资源。就目前而言,我们可以说每一个运行的程序都具有一个与其相关联的优先级,而具有高优先级的程序会分配到更多的可用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变量。因为-1本身是一个可用的优先级,所以在调用 getpriority之前应将errno设置为0,并且在函数返回检查是否仍为0。如果成功setpriority函数会返回0,否则返回-1。
系统资源的限制可以通过getrlimit与setrlimit函数进行读取与设置。这两个函数都会使用一个通用目的的结构,rlimit,来描述资源限制。他是在sys/resource.h中定义,并且包含下列成员:
rlimit成员 描述
rlim_t rlim_cur 当前的软限制
rlim_t rlim_max 硬限制
rlim_t 是一个整数类型,用来描述资源级别。通常,软限制是一个不应超过的建议限制;如果这样做会使得库函数返回错误。如果超过硬限制也许会使用系统试图向其程序 发送消息来终结其运行。例如超过CPU时间的SIGXCPU信号以及超过数据尺寸限制的SIGSEGV信号。一个程序也许会将其软限制设置为小于硬限制的 任何值,也可能减少其硬限制。只有以超级权限运行的程序才可以增加硬限制。
存在许多可以限制的系统资源。这是由rlimit函数的resource参数来指定的,他们定义在sys/resource.h中,描述如下:
resource参数 描述
RLIMIT_CORE 以字节表示的核心复制文件尺寸
RLIMIT_CPU 以秒表示的CPU时间限制
RLIMIT_DATA 以字节表示的data()段限制
RLIMIT_FSIZE 以字节表示的文件尺寸限制
RLIMIT_NOFILE 可打开的文件数目的限制
RLIMIT_STACK 以字节表示的栈尺寸的限制
RLIMIT_AS 以字节表示的地址空间的限制(栈与数据)
下面的试验部分演示了一个程序,limits.c,来模拟一个通常的程序。他同时设置与突破资源限制。
试验--资源限制
1 包含我们程序中会用到的所有函数所需要头文件:
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdio.h>
#include <math.h>
2 work函数将一个字符串写入一个临时文件10000次,然后执行一些算术运行来在CPU上产生负载:
void work()
{
FILE *f;
int i;
double x = 4.5;
f = tmpfile();
for(i = 0; i < 10000; i++) {
fprintf(f,”Do some output/n”);
if(ferror(f)) {
fprintf(stderr,”Error writing to temporary file/n”);
exit(1);
}
}
for(i = 0; i < 1000000; i++)
x = log(x*x + 3.21);
}
3 main函数调用work函数,然后使用getrusage函数来计算他已经使用了多少CPU时间。他会在屏幕上显示这些信息:
int main()
{
struct rusage r_usage;
struct rlimit r_limit;
int priority;
work();
getrusage(RUSAGE_SELF, &r_usage);
printf(“CPU usage: User = %ld.%06ld, System = %ld.%06ld/n”,
r_usage.ru_utime.tv_sec, r_usage.ru_utime.tv_usec,
r_usage.ru_stime.tv_sec, r_usage.ru_stime.tv_usec);
4 接下来,他会调用getpriority函数与getrlimit函数来分别查看当前的级别以及文件尺寸限制:
priority = getpriority(PRIO_PROCESS, getpid());
printf(“Current priority = %d/n”, priority);
getrlimit(RLIMIT_FSIZE, &r_limit);
printf(“Current FSIZE limit: soft = %ld, hard = %ld/n”,
r_limit.rlim_cur, r_limit.rlim_max);
5 最后,我们使用setrlimit函数来设置一个文件尺寸限制并且再次调用work函数,这会失败,因为他在尝试创建一个太大的文件:
r_limit.rlim_cur = 2048;
r_limit.rlim_max = 4096;
printf(“Setting a 2K file size limit/n”);
setrlimit(RLIMIT_FSIZE, &r_limit);
work();
exit(0);
}
当我们运行这个程序时,我们会看到他已消耗了多少CPU时间以及程序运行的默认优先级。一旦设置了文件尺寸限制,这个程序就不可以向一个临时文件写入大于2048字节的内容了。
$ cc -o limits limits.c -lm
$ ./limits
CPU usage: User = 0.980000, System = 0.010000
Current priority = 0
Current FSIZE limit: soft = -1, hard = -1
Setting a 2K file size limit
File size limit exceeded
我们可以通过使用nice命令启动来改变程序的优先级。在这里,我们将程序的优先级改为+10,结果,系统会花费更长的时间来执行这个程序:
$ nice ./limits
CPU usage: User = 1.000000, System = 0.000000
Current priority = 10
Current FSIZE limit: soft = -1, hard = -1
Setting a 2K file size limit
File size limit exceeded
工作原理
limits 程序调用work函数来模拟通常的程序行为。他会执行一些计算并且产生一些输出,在这个例子中,大约向临时文件写入了150K的内容。他调用资源函数来查 看其优先级以及文件尺寸限制。在这个例子中,文件尺寸限制并没有设置,从而可以允许我们创建我们希望大小的文件。程序然后将其文件尺寸限制为2K,并且再 次尝试执行work函数。这一次work函数会失败,因为他不可以创建这样大的一个临时文件。
注意:也可以使用特定shell的ulimit命令来设置一个运行程序的限制。
在 这个例子中,错误信息‘Error writing to temporary file’ 也许并没有如我们所期望的那样显示出来。这是因为一些系统会超过资源限制时终止程序的执行。这是通过发送一个SIGXFSZ信号来做到的。我们将会在第 11章了解到更多的关于信号以及如何使用他们的内容。而其他的一些POSIX系统也许只是简单的使得超过限制的函数返回一个错误。
总结
在这一章,我们了解了Linux环境并且检测了程序运行的条件。我们讨论了命令行参数以及环境变量,这两者都可以用来改变程序的默认行为以及提供有用的程序选项。
我们也了解了一个程序如何使用库函数来操作日期与时间值,得到自身的信息,以及他所运行的用户和主机的信息。
Linux程序通常需要共享精确的资源,所以我们也讨论了这些程序如何确定与管理。