Mac OS X Internals: A Systems Approach
5.4. Mach Subsystem Initialization
kernel_bootstrap() performs much of the higher-level kernel startup. In fact, it eventually launches BSD initialization, which in turn ends by initiating user-level system startup. Figure 511 shows the key steps performed by kernel_bootstrap(). Figure 511. Bootstrapping kernel subsystems
lck_mod_init() [osfmk/kern/locks.c] initializes data structures that are used by Mach's locking primitives. 5.4.1. Scheduler Initialization
sched_init() [osfmk/kern/sched_prim.c] initializes the processor scheduler. It sets the default preemption rate to DEFAULT_PREEMPTION_RATE (defined to be 100 per second in osfmk/kern/sched_prim.c), unless a boot argument was used to set it to some other value. sched_init() calculates the values of fundamental scheduling constants. For example, it calculates the standard timeslicing quantum in microseconds as the number 1,000,000 divided by the default preemption rate. It then prints a message advertising the value so calculated. In a verbose boot (PowerPC), this is the first kernel message seen by the user: standard timeslicing quantum is 10000 us
sched_init() also performs the following specific operations.
5.4.2. High-Level Virtual Memory Subsystem Initialization
kernel_bootstrap() calls vm_mem_bootstrap() [osfmk/vm/vm_init.c] to initialize the platform-independent virtual memory subsystema major step in bootstrapping. Figure 512 shows the sequence of actions performed by vm_mem_bootstrap(). Figure 512. High-level virtual memory subsystem initialization
vm_page_bootstrap() [osfmk/vm/vm_resident.c] initializes the resident memory module.[14] It allocates memory for VM management data structures, initializes the page queues, "steals" memory for Mach's map and zone subsystems, allocates and initializes the virtual-to-physical table hash buckets, allocates the resident page table by calling pmap_startup() [osfmk/vm/vm_resident.c], and computes the number of pages that must be marked as "wired" because they cannot be moved. pmap_startup() calculates the amount of free memory and allocates space for all page frames that would be needed. It then iterates over available physical pages, calling vm_page_init() [osfmk/vm/vm_resident.c] to initialize page frames. Figure 513 depicts this operation. [14] We will look at Mach VM details in Chapter 8. Figure 513. Initializing page frames during VM subsystem initialization
Once vm_page_bootstrap() returns, all physical memory is accounted for, and the kernel can explicitly use virtual addresses. As Figure 512 shows, vm_mem_bootstrap() then initializes various other components of the VM subsystem. zone_bootstrap() [osfmk/kern/zalloc.c] initializes zone_zone, the "zone of zones,"[15] which uses fixed memory allocated earlier during memory subsystem initialization. [15] We will look at Mach's zone-based memory allocator in Chapter 8. vm_object_bootstrap() [osfmk/vm/vm_object.c] initializes Mach's VM objects module. This includes initializing zones for VM objects (vm_object_zone) and VM object hash entries (vm_object_hash_zone). The kernel object (kernel_object) and the submap object (vm_submap_object) are also initialized here. Mach's external page management hint technology,[16] which maintains a (potentially incomplete) map of pages written to external storage for a range of virtual memory, is initialized via a call to vm_external_module_initialize() [osfmk/vm/vm_external.c]. [16] This should not be confused with Mach's external (to the kernel) memory management, which is not available in Mac OS X. vm_map_init() [osfmk/vm/vm_map.c] initializes a zone for allocating vm_map structures (vm_map_zone), a zone for allocating nonkernel vm_map_entry structures (vm_map_entry_zone), a special zone for allocating kernel-only vm_map_entry structures (vm_map_kentry_zone), and a zone for vm_map_copy structures (vm_map_copy_zone). kmem_init() [osfmk/vm/vm_kern.c] initializes the kernel's virtual memory map (kernel_map). Moreover, any virtual memory that may have been allocated so faras determined by the difference between the constant VM_MIN_KERNEL_ADDRESS and the lower address bound passed to kmem_init()is reserved by entering it into the kernel's map. kmem_init() also sets the value of the vm_page_wire_count global variable, which, at this point, represents all kernel memory used. vm_page_wire_count = (atop_64(max_mem) - (vm_page_free_count + vm_page_active_count + vm_page_inactive_count));
pmap_init() [osfmk/ppc/pmap.c] finishes the initialization of the physical map module by allocating the remaining data structures that the module needs to map virtual memory. It initializes a zone of pmap structures (pmap_zone) from which new physical maps are allocated, marks pmap as initialized, and sets the free pmap count to zero. vm_mem_bootstrap() then checks for the presence of the zsize boot argument, which, if present, specifies the maximum amount of address space allocated for zonesthat is, the zone map size. By default, the kernel uses 25% of the physical memory size (sane_size). Regardless of whether zsize is specified or not, the kernel clamps the maximum and minimum values of the zone map size to 768MB and 12MB, respectively. vm_mem_bootstrap() now calls zone_init() [osfmk/kern/zalloc.c] to allocate address space for zones. kalloc_init() [osfmk/kern/kalloc.c] initializes the kernel memory allocator, which uses multiple power-of-2-sized zones and a 16MB submap allocated from kernel_map. The latter is used in conjunction with kmem_alloc() [osfmk/vm/vm_kern.c] for allocations that are too large for a zone. kalloc_init() determines the value for kalloc_max, which represents the first power of 2 for which no zone exists. By default, kalloc_max is set to 16KB, unless the page size is more than 16KB, in which case it is set to the page size. kalloc_init() then iterates over supported allocation sizesstarting from 1 and going up to kalloc_max in powers of two. It initializes a zone for sizes KALLOC_MINSIZE (16 bytes) and higher by calling zinit(). The zones are named kalloc.16, kalloc.32, kalloc.64, and so on. The maximum number of elements in a zone depends on the size that the zone handles.
vm_fault_init() [osfmk/vm/vm_fault.c] initializes any private data structures that the kernel might have in the page-fault-handling module. vm_page_module_init() [osfmk/vm/vm_resident.c] initializes a zone for "fictitious" resident page structures that do not actually refer to any physical pages but are used for holding important page information. The physical page address for such pages is set to -1. memory_manager_default_init() [osfmk/vm/memory_object.c] initializes a mutex that is used while getting or setting the default memory manager Mach port. memory_object_control_bootstrap() [osfmk/vm/memory_object.c] initializes a zone (mem_obj_control_zone) used for allocating pager[17] request ports. device_pager_bootstrap() [osfmk/vm/device_vm.c] initializes a zone for device node pager structures (device_pager_zone). [17] As we will see in Chapter 8, a pager in Mach's VM subsystem represents a data source. 5.4.3. IPC Initialization
kernel_bootstrap() calls ipc_bootstrap() [osfmk/ipc/ipc_init.c] to set up the IPC subsystem[18] enough for the kernel task to be created. ipc_bootstrap() performs the following actions. [18] Chapter 9 discusses the Mac OS X IPC subsystem.
5.4.4. Finishing VM and IPC Initialization
kernel_bootstrap() next calls vm_mem_init() [osfmk/vm/vm_init.c], which in turn calls vm_object_init() [osfmk/vm/vm_object.c] to finish initializing the kernel object. ipc_init() [osfmk/ipc/ipc_init.c] performs the final initialization of the IPC subsystem. It allocates two pageable maps: the ipc_kernel_map map to manage memory allocations made during Mach IPC calls and the ipc_kernel_copy_map map in which space is allocated during Mach IPC for out-of-line data that is to be physically copied. ipc_init() finally calls ipc_host_init() [osfmk/kern/ipc_host.c], which performs the following actions.
5.4.5. Initializing Miscellaneous Subsystems
machine_init() [osfmk/ppc/model_dep.c] calls clock_config() [osfmk/kern/clock.c] to configure the clock subsystem. clock_config() calls the configuration (but not initialization) functions of all available clock devices, such as the calendar and the system clocks. It also calls timer_call_initialize() [osfmk/kern/timer_call.c], which registers timer_call_interrupt() [osfmk/kern/timer_call.c] as the function to be called from the real-time clock device interrupt handler whenever the real-time clock timer expires (in other words, timer_call_interrupt() services the timer callout queue for a processor). machine_init() also calls perfmon_init() [osfmk/ppc/hw_perfmon.c], which initializes a lock used by the performance-monitoring facility. kmod_init() [osfmk/kern/kmod.c] initializes locks and a command queue used by the kernel module subsystem. The kernel enqueues data packets containing module load requests in this queue, which is serviced by the kextd user-space daemon. clock_init() [osfmk/kern/clock.c] calls the initialization functions of all available clock devices. Note that unlike clock_config(), which is called only once on the master processor at boot time, clock_init() is called every time a processor is started. ledger_init() [osfmk/kern/ledger.c] initializes Mach ledgers. A ledger is a kernel abstraction used for resource accounting. It can be used to limit consumption of other resources. Ledgers are not used in Mac OS X, and xnu's implementation of ledgers is not functional. 5.4.6. Tasks and Threads
task_init() [osfmk/kern/task.c] initializes a zone (task_zone) from which new task structures are allocated. The built-in limit on the number of tasks is defined as TASK_MAX in osfmk/kern/mach_param.h, with a typical value of 1024. task_init() calls task_create_internal() [osfmk/kern/task.c] to create the first taskthe kernel task (kernel_task). The kernel task's default address space map is deallocatedkernel_map is assigned as its address space instead. Note that since the kernel task's parent task is TASK_NULL, it does not inherit any memory. thread_init() [osfmk/kern/thread.c] initializes a zone (thread_zone) from which new thread structures are allocated. The built-in limit on the number of threads is defined as THREAD_MAX (2560) in osfmk/kern/mach_param.h. tHRead_init() also calls stack_init() [osfmk/kern/stack.c], which allocates a map (stack_map) for kernel stacks. A kernel stack is 16KB in size and resides in nonpageable memory. thread_init() also calls machine_thread_init() [osfmk/ppc/pcb.c], which may perform machine-specific initializations. 5.4.7. Launching the Kernel Bootstrap Thread
kernel_bootstrap() now creates a kernel thread, with kernel_bootstrap_thread() [osfmk/kern/startup.c] as the continuation function,[19] for completing the remaining kernel startup operations. This will be the first kernel thread to run on the processor. The thread's resources are deallocated before it is handed over to load_context() [osfmk/kern/startup.c] for execution. load_context() calls machine_set_current_thread() [osfmk/ppc/machine_routines_asm.s], which loads the SPRG1 register with the current thread pointer.[20] It then calls processor_up() [osfmk/kern/machine.c], the machine-independent Mach-level CPU enabler routine that adds the specified processor to the default processor set. The processor's state is set as PROCESSOR_RUNNING. The global machine_info structure's avail_cpus field is atomically incremented by one. processor_up() also calls the ml_cpu_up() [osfmk/ppc/machine_routines.c] machine-dependent routine. ml_cpu_up() atomically increments the physical_cpu and logical_cpu fields of machine_info by one each. Finally, load_context() calls machine_load_context() [osfmk/ppc/cswtch.s] to load the thread's hardware context and set it running. [19] We will look at continuations in Chapter 7. [20] The Mac OS X kernel conventionally uses SPRG1 for this purpose. |
Категории