Chapter 7
FreeBSD System Programming
Prev
top
Next

Copyright(C) 2001,2002,2003,2004 Nathan Boeger and Mana Tominaga


Code samples for this chapter: here

7.1 Process Resources and System Limits

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 resources at runtime without recompiling the entire system.

This chapter discusses limits associated with processes, 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 process 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 process is allowed to have open.

  #define  RLIMIT_SBSIZE  9     /* maximum size of all socket buffers */

The RLIMIT_SBSIZE limit specifies the amount of 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_VMEM limit indicates the amount of bytes a process' 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 RLIM_INFINITY 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 too much CPU (being caught in an infinite loop) or maybe exceeding 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.

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 even differ from a system to another. 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 bits systems use 8096 as the default page size whereas most 32 bits systems use 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
top
Next
Chapter 7
FreeBSD System Programming