Chapter 7 FreeBSD System Programming |
||
---|---|---|
Prev | Next |
The BSD Unix system provides ways for system administrators to control system resources, in order to support multiple simultaneous users and application connections. Such resources include CPU time, memory usage, and disk usage. The resource controls let you tune the system to best accommodate its usage. In earlier versions of Unix, some system limits where set at compile time, and as such, changing a limit would require a recompilation of the entire system. However, modern BSD systems are able to adjust most if not all of the system resource at runtime without recompiling the entire system.
This chapter discusses limits associated with process, both for system wide and user imposed limits.
We will look at ways we can find these limits as well as possibly modify them, and also discuss ways a process
can actually query its resource usage.
7.2 Determining System Limits
getrlimit, setrlimit
The getrlimit will allow a process to query the imposed system limits. These system limits are specified by a hard limit and a soft limit pair. When a soft limit is exceeded, the process will be allowed to continue, but depending on the type of limit, a signal may be sent to the process. On the other hand, a process may not exceed its hard limit. The soft limit value may be set by the process to any value between 0 and the hard limit maximum. Hard limits can be irreversibly lowered by any process; only the super user can increase them, however.
#include <sys/types.h> #include <sys/time.h> #include <sys/resource.h> int getrlimit(int resource, struct rlimit *rlp); int setrlimit(int resource, const struct rlimit *rlp);
The getrlimit and setrlimit both make use of the following structure:
struct rlimit { rlim_t rlim_cur; rlim_t rlim_max; };
Now let's look at each member. The rlim_cur member will specify the current system soft limit for the specified resource. The rlim_max member will specify the current system hard limit for the specified resource.
The first argument to the getrlimit and setrlimit functions is the resource parameter. This will specify the resource that the process is interested in receiving information about. The possible resource values are listed below. You can also find them in /usr/include/sys/resource.h.
#define RLIMIT_CPU 0 /* cpu time in milliseconds */
The RLIMIT_CPU resource specifies how many milliseconds a process can take for execution on the CPU. Normally, a process has only a soft limit, and no hard limit. If the soft limit is exceeded, the process will receive a SIGXCPU signal.
#define RLIMIT_FSIZE 1 /* maximum file size */
The RLIMIT_FSIZE limit specifies how large a file in bytes a process can create. For example, if the RLIMIT_FSIZE is set to 0, then the process will not be able to create a file at all. If the process exceeded this limit, it will be sent a SIGFSZ signal.
#define RLIMIT_DATA 2 /* data size */
The RLIMIT_DATA limit specifies the maximum amount of bytes the process data segment can occupy. The data segment for a process is the area in which dynamic memory is located (that is, memory allocated by malloc() in C, or in C++, with new()). If this limit is exceeded, calls to allocate new memory will fail.
#define RLIMIT_STACK 3 /* stack size */
The RLIMIT_STACK limit specifies the maximum amount of bytes the process's stack can occupy. Once the hard limit is exceeded, the process will receive a SIGSEV signal.
#define RLIMIT_CORE 4 /* core file size */
The RLIMIT_CORE will specify the maximum size of a core file a process can create. If this limit is set to 0, then a core file will not be created. On the other hand, all processes writing to a core file will be terminated once this limit has been reached.
#define RLIMIT_RSS 5 /* resident set size */
The RLIMIT_RSS limit specifies the maximum amount of bytes that a process's resident set size can occupy. The resident set size of a process refers to the amount of physical memory a processes is using.
#define RLIMIT_MEMLOCK 6 /* locked-in-memory address space */
The RLIMIT_MEMLOCK limit specifies the maximum amount of bytes a process can lock using a call to mlock.
#define RLIMIT_NPROC 7 /* number of processes */
The RLIMIT_NPROC limit specifies the maximum amount of concurrent processes allowed for a given user. The user is determined by the effective user id of the process.
#define RLIMIT_NOFILE 8 /* number of open files */
The RLIMIT_NOFILE limit specifies the maximum number of files the processes is allowed to have open.
#define RLIMIT_SBSIZE 9 /* maximum size of all socket buffers */
The RLIMIT_SBSIZE limit species the amount mbufs a user can use at any moment. See the socket man page for a definition of mbufs.
#define RLIMIT_VMEM 10 /* virtual process size (inclusive of mmap) */
The RLIMIT_VMEN limit indicates the amount of bytes a process's mapped address space can occupy. Once this limit is exceeded, calls to allocate dynamic memory or calls to mmap will fail.
#define RLIM_INFINITY
The RLIM_INFINITY macro is used to remove a limit on a resource. In other words, setting a resource hard limit to RLM_INIFINITY will allow usage without any system imposed limit. Setting a soft limit to RLIM_INFINITY will prevent the process from receiving any soft limit notices. This might be of use if your process does not want to set a signal handler for a resource that would cause the process to be sent a signal once the soft limit is exceeded.
When calling getrlimit the second parameter needs to be a valid pointer to a rlimit structure. The getrlimit will then place the proper limit values inside this structure. On the other hand, setrlimit will use the values supplied inside the second parameter when altering limits. Setting a limit value to 0 will prevent usage of that resource. Setting a resource limit to RLIM_INFINITY will remove any limits on that resource. These functions both return 0 on success and -1 otherwise. If an error occurs, these functions will set errno accordingly.
The getpagesize Function
#include <unistd.h> int getpagesize(void);
Before we cover the getrusage function, we should discuss the getpagesize function. Some process statistics
are reported in terms of pages used. A page of memory is just a chunk of memory usually around 4096 bytes.
The size of a page can vary, however, and it should not be hard coded into your programs. Instead, to determine
the local system pagesize you should use the getpagesize function. The return value from getpagesize will be
the amount of bytes used per page.
7.3 Determining Process Resource Usage
The getrusage Function
Now that we can determine the system limits, we should then be able to determine current processes resource usage. That is where the getrusage function is used. The getrusage function is quite comprehensive. A process can determine its memory usage, amount of time running inside the CPU, and even find out information regarding child processes. As such, one use for the getrusage function is to help avoid runaway processes. A runaway process is one that is out of control, either using up to much CPU (being caught in an infinite loop) or maybe has exceeded some memory usage limit (resulting from a memory leak).
#include <sys/types.h> #include <sys/time.h> #include <sys/resource.h> #define RUSAGE_SELF 0 #define RUSAGE_CHILDREN -1 int getrusage(int who, struct rusage *rusage);
The getrusage function takes two parameters. The first parameter who is set to either RUSAGE_SELF or RUSAGE_CHILDREN. Setting the parameter RUSAGE_SELF will fill the rusage structure with information regarding the current process. Setting the parameter RUSAGE_CHILDREN will fill the rusage structure with aggregate information regarding child processes.
The rusage structure definition can be found inside /usr/include/sys/resource.h. It includes the following members:
struct rusage { struct timeval ru_utime; /* user time used */ struct timeval ru_stime; /* system time used */ long ru_maxrss; /* max resident set size */ long ru_ixrss; /* integral shared memory size */ long ru_idrss; /* integral unshared data " */ long ru_isrss; /* integral unshared stack " */ long ru_minflt; /* page reclaims */ long ru_majflt; /* page faults */ long ru_nswap; /* swaps */ long ru_inblock; /* block input operations */ long ru_oublock; /* block output operations */ long ru_msgsnd; /* messages sent */ long ru_msgrcv; /* messages received */ long ru_nsignals; /* signals received */ long ru_nvcsw; /* voluntary context switches */ long ru_nivcsw; /* involuntary " */ };
Now let's look at each member in detail.
B>ru_utime,ru_stime
The ru_utime and ru_stime members contain the total amount of time spent executing in user mode and system mode, respectively. They both make use of the timeval structure. (Please see the previous chapter for a brief description of this structure.)
ru_maxrss
The ru_maxrss member contains the total amount of resident set memory used. The value will be in terms of memory pages used.
ru_ixrss
The ru_ixrss value is expressed as the total amount of memory used by the text segment in kilobytes multiplied by the execution-ticks. The text segment of a program refers to the part of the binary that, amongst other things, contains read-only instructions and data.
ru_idrss
The ru_idrss value is expressed as the total amount of private memory used by a process in kilobytes multiplied by execution-ticks.
ru_isrss
The ru_isrss value is expressed as the total amount of memory used by the stack in kilobytes multiplied by execution-ticks.
ru_minflt
The ru_minflt value is the number of page faults that required no I/O activity. A page fault occurs when the kernel needs to retrieve a page of memory for the process to access.
ru_majflt
The ru_majflt value is the number of page faults that required I/O. A page fault occurs when the kernel needs to retrieve a page of memory for a process to access.
ru_nswap
Occasionally, a process will be swapped out of memory to make room for another to execute. The ru_nswap value indicates the number of times a process was swapped out of memory.
ru_inblock
The ru_inblock member contains the total number of inputs the file system had to perform for a read request.
ru_oublock
The ru_oublock member contains the total number of times the file system had to perform output for a write request.
ru_msgsnd
The ru_msgsnd member indicates the total number of IPC messages sent.
ru_msgrcv
The ru_msgrcv member contains the total number of IPC messages received.
ru_nsignals
The ru_nsignals member contains the total number of signals the process received.
ru_nvcsw
The ru_nvsw member indicates the total number of voluntary context switches for a process. A voluntary context switch occurs when the process "gives up" its time slice on the CPU. Usually this happens when the process is waiting for some type of resource to become available.
ru_nivcsw
The ru_nivcsw member contains the total number of context switches due to a higher priority process becoming runnable.
Upon a successful call to getrusage, 0 will be returned. Otherwise, -1 will be returned, with the errno set
accordingly.
7.4 Conclusion
In this chapter we covered ways a program can obtain the system limits. These limits should never be hardwired inside your code. Your program should make use of these interfaces instead. This is because these limits are platform-dependent, and differ even from system to system. For example, a system administrator can adjust the amount of allowed open file descriptors, so hard coding a value of 4096 might not be the case if they are lowered or increased. Another example is page sizes. Some 64 bit systems use 8096 as the default page size whereas most 32 bit systems are 4096 bytes. Again this is a tunable parameter and cannot be guaranteed to stay at a fixed value.
We also looked at ways a program can obtain its current usage or query for usage statistics of its child processes. Using these interfaces may help to detect and avoid a process from going out of control. They are also potentially useful in debugging errors that occur when a program tries to exceed its established hard and soft limits.
Prev | Next | |
Chapter 7 FreeBSD System Programming |
---|