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_structmm 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
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 };
There is a small number of functions for dealing with mm_structs which is described in Table 9.2
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.
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().
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().