next up previous contents index
Next: 9.3 Memory Regions Up: 9. Process Address Space Previous: 9.1 Managing the Address   Contents   Index

Subsections

9.2 Process Address Space Descriptor

The process address space is described by the mm_struct. Only one exists for each process but there is still a count field for reference as threads share a mm_struct and that is how threads of a process are identified. The count may also be incremented if a kernel subsystem needs to ensure the struct does not get reclaimed unexpectedly or that the struct is being temporarily shared for the purposes of Lazy TLB.

Kernel threads have no user space context and so the task_struct$\rightarrow$mm field is NULL. For some tasks such as the boot idle task, it is not setup but for kernel threads, a call to daemonize()() calls exit_mm() to delete it. These tasks use what is called Lazy TLB during context switches during schedule(). Instead of carrying out an expensive TLB flush, these processes borrow the mm of the previous task and place it in the task_struct$\rightarrow$active_mm field.

This reduces the need to flush the TLB for a process that should not be page faulting in any case. The only exception is faulting in vmalloc space which is treated as a special case of the page fault handling code. As TLB flushes can be extremely expensive, especially with architectures such as the PPC, the use of Lazy TLB can show large improvements for context switches.

When entering Lazy TLB, the function enter_lazy_tlb() is called to ensure that a mm is not shared between processors in SMP machines although on UP machines, the function is a a NULL operation. The second time when lazy TLB is used is during process exit when start_lazy_tlb() is used briefly while the process is waiting to be reaped by the parent.

The mm_struct is defined in include/linux/sched.h as follows;

210 struct mm_struct {
211         struct vm_area_struct * mmap;           
212         rb_root_t mm_rb;
213         struct vm_area_struct * mmap_cache;     
214         pgd_t * pgd;
215         atomic_t mm_users;                      
216         atomic_t mm_count;                      
217         int map_count;                          
218         struct rw_semaphore mmap_sem;
219         spinlock_t page_table_lock;             
220 
221         struct list_head mmlist;                
222      
226         unsigned long start_code, end_code, start_data, end_data;
227         unsigned long start_brk, brk, start_stack;
228         unsigned long arg_start, arg_end, env_start, env_end;
229         unsigned long rss, total_vm, locked_vm;
230         unsigned long def_flags;
231         unsigned long cpu_vm_mask;
232         unsigned long swap_address;
233 
234         unsigned dumpable:1;
235 
236         /* Architecture-specific MM context */
237         mm_context_t context;
238 };

mmap The head of a linked list of all VMA regions in the address space

mm_rb The VMAs are arranged in a linked list and in a red-black tree for fast lookups. This is the root of the tree

mmap_cache The vma found during the last call to find_vma() is stored in this field on the assumption that the area will be used again soon

pgd The Page Global Directory for this process

mm_users Count of the number of threads accessing an mm. A cloned thread will up this count to make sure an mm_struct() is not destroyed early. The swap_out code will increment this count when swapping out portions of the mm

mm_count A reference count to the mm. This is important for lazy TLB switches where a task may be using one mm_struct temporarily

map_count Number of VMAs in use

mmap_sem This is a long lived lock which protects the vma list for readers and writers. As the taker could run for so long, a spinlock is inappropriate. A reader of the list takes this semaphore with down_read(). If they need to write, it must be taken with down_write() and the page_table_lock must be taken as well

page_table_lock This protects most fields on the mm_struct. As well as the page tables, It protects the rss count and the vma from modification

mmlist All mm's are linked together via this field

start_code, end_code The start and end address of the code section

start_data, end_data The start and end address of the data section

start_brk, end_brk The start and end address of the heap

arg_start, arg_end The start and end address of command line arguments

env_start, env_end The start and end address of environment variables

rss Resident Set Size (RSS), the number of resident pages for this process

total_vm The total memory space occupied by all vma regions in the process

locked_vm The amount of memory locked with mlock() by the process

def_flags It has only one possible value, VM_LOCKED. It is used to determine if all future mappings are locked by default or not

cpu_vm_mask A bitmask representing all possible CPU's in an SMP system. The mask is used with IPI to determine if a processor should execute a particular function or not. This is important during TLB flush for each CPU for example

swap_address Used by the vmscan code to record the last address that was swapped from when swapping out entire processes

dumpable Set by prctl(), this flag is important only to ptrace

context Architecture specific MMU context

There is a small number of functions for dealing with mm_structs which is described in Table 9.2


Table 9.2: Functions related to memory region descriptors
\begin{table}\ \begin{center}
\begin{tabularx}{13.5cm}{\vert l\vert X\vert}
\...
...ct to the slab allocator \\
\hline
\end{tabularx}
\end{center} \end{table}


9.2.1 Allocating a Descriptor

Two functions are provided to allocate a mm_struct(). To be slightly confusing, they are essentially the name but with important differences. allocate_mm will allocate a mm_struct from the slab allocator. alloc_mm will allocate from slab and then call the function mm_init() to initialise it.

9.2.2 Initialising a Descriptor

The initial mm_struct in the system is called init_mm() and is statically initialised at compile time using the macro INIT_MM().

242 #define INIT_MM(name) \
243 {                                                       \
244         mm_rb:          RB_ROOT,                        \
245         pgd:            swapper_pg_dir,                 \
246         mm_users:       ATOMIC_INIT(2),                 \
247         mm_count:       ATOMIC_INIT(1),                 \
248         mmap_sem:       __RWSEM_INITIALIZER(name.mmap_sem), \
249         page_table_lock: SPIN_LOCK_UNLOCKED,            \
250         mmlist:         LIST_HEAD_INIT(name.mmlist),    \
251 }

Once it is established, new mm_structs are copies of their parent mm_struct copied using copy_mm() with the process specific fields initialised with init_mm().

9.2.3 Destroying a Descriptor

A new user to an mm increments the usage could with a simple call,

atomic_int(&mm->mm_users};

As long as the count is above 0, the caller is guaranteed that the mm_struct will not disappear prematurely. It is decremented with a call to mmput(). If the count reaches zero, all the mapped regions with exit_mmap() and the mm destroyed with mm_drop().


next up previous contents index
Next: 9.3 Memory Regions Up: 9. Process Address Space Previous: 9.1 Managing the Address   Contents   Index
Mel 2003-01-14