Tuesday, December 9, 2008

Do You Know Your Limits!?

To continue further our discussion of what are process resource limits, let’s see some real figures. Try putting following two printk() statements in FirstModuleSource.c posted here.

printk (KERN_INFO "Process Name: %s", current -> comm);
printk(KERN_INFO "current limit- # processes: %ul\n", current -> rlim[RLIMIT_NOFILE].rlim_cur);
printk(KERN_INFO "maximum limit- # processes: %ul\n", current -> rlim[RLIMIT_NOFILE].rlim_max);

These statements print soft and hard limits (for the maximum number of files the current user can open) imposed on current process (which is insmod). On similarly printing the limits of other resources, we see that all values of soft and hard limits are assigned limits from array INIT_RLIMITS (see #line 24 in snapshot of resource.h posted here).


To see the difference between resource limits of a kernel process with an ordinary user process, we shall print the resource limits of a user process by adding following lines of code in the FirstModuleSource.c

int some_pid = 3630; /*reader should change this value appropriately*/
struct task_struct *task = find_task_by_pid(some_pid);
printk (KERN_INFO "Process Name: %s", task -> comm);
printk (KERN_INFO "current limit- # processes: %ul\n", task -> rlim[RLIMIT_NOFILE].rlim_cur);
printk (KERN_INFO "maximum limit- # processes: %ul\n", task -> rlim[RLIMIT_NOFILE].rlim_max);

Here, 3630 is the pid of the shell command tail, which I got by running tail and looking for it in output of ps. The find_task_by_pid() kernel function returns the process descriptor belonging to some_pid. We can specify pid of any user process running in background; but that pid must be alive while the module is inserted.

On my i386/RHEL-4 machine, both sets of statements given above produce following output, respectively:

Nov 15 21:27:32 localhost kernel: Process Name: insmod<6>
Nov 15 21:27:32 localhost kernel: current limit- # processes: 1024l
Nov 15 21:27:32 localhost kernel: maximum limit- # processes: 1024l

Nov 15 21:27:32 localhost kernel: Process Name: tail<6>
Nov 15 21:27:32 localhost kernel: current limit- # processes: 1024l
Nov 15 21:27:32 localhost kernel: maximum limit- # processes: 1024l

On similarly printing the limits of other resources and analyzing the output, we see that all the user/kernel processes’ resource limits are initialized with the same set of values which are specified by array INIT_RLIMITS of resource.h. This small experiment suggests that by default, the kernel imposes similar resource limits on any user/kernel mode process. To verify this further, I changed some values in INIT_RLIMITS, and then ran the same program again after compiling the kernel. The changed values were reflected as-is in the output.

How are Limits Assigned?
Unless a user is assigned custom limits by the superuser, all the processes belonging to that user are assigned default limits as follows.

The first Linux process which is created during system bootup is the swapper process. After kernel initialization, the swapper creates another process init, which then takes full control of all the system activities and creates more processes. swapper's data structures are initialized through init_task.h. It is the only process, for which data structures are statically allocated like this. Data structures for all other processes are dynamically allocated.

As we see in the snapshot here, INIT_TASK macro initializes the task_struct of swapper. At #line 80, rlim field of task_struct has been initialized with INIT_RLIMITS structure defined in resource.h (see last post), #line 82 is the name of the process.
Since every process inherits properties of its parent process when it is created, init receives the same values as swapper for all of its fields, including rlim. Further, when init creates more processes, these values are inherited by every child process and to their child processes and so on. This goes on until and unless
  1. A process executes setrlimit() system call
  2. The superuser has configured custom values for a user.
We've already covered the first point in last post. For information on second point, do a `man limits.conf' and open your /etc/security/limits.conf. The custom values for a user/group are specified in this configuration file by the system superuser.

Therefore, if a particular limit for a resource is set for user X in limits.conf, that limit shall be assigned to all the processes created by user X, at the time of process creation.

** Try shell command `ulimit –a’ to see all soft limits for current user. `ulimit -s unlimited’ sets the stack size soft limit to maximum for all the processes belonging to current session of the current user. (Current session is nothing but the current shell on which ulimit is entered; the changes by ulimit do not affect other shells.) If a user/resource doesn't appear in limits.conf, ulimit shall display default limits (i.e. the values of INIT_RLIMIT array) for that user/resource.

Shweta is going to share some findings on this very soon... on the same page.
-------------------------------------------------------------

Shweta added:

The above explanation can be understood with the help of a small and quick experiment. This should make clear this behavior of resource limits assignment. This experiment has two parts:

First, clear the entries belonging to user X from limits.conf, (if any), then login to your machine with user account X and write a user program to print value of RLIMIT_NOFILE using getrlimit(). Also check this value using 'ulimit'. Both outputs should show the value specified in INIT_RLIMIT array. Now change the value of RLIMIT_NOFILE using setrlimit(), fork() a new process in the same program and print the value of RLIMIT_NOFILE for child process using getrlimit(). This should output the value changed in parent process using setrlimit().

Now for the second part of the experiment, make an entry for RLIMIT_NOFILE for user X in limits.conf and repeat part-1 of the experiment. The outputs this time would differ in the way that wherever previous experiment printed values from INIT_RLIMIT array, this experiment would print values specified in limits.conf file.

No comments: