Mac OS X Internals: A Systems Approach

5.5. The First Thread

kernel_bootstrap_thread() [osfmk/kern/startup.c] performs the following operations.

  • It calls idle_thread_create() [osfmk/kern/sched_prim.c] to create the idle kernel thread for the processor. An idle thread, which is bound to a processor, runs at the idle priority (IDLEPRI), looking for other threads to execute. It is marked as being in the TH_RUN and TH_IDLE states simultaneously.

  • It calls sched_startup() [osfmk/kern/sched_prim.c], which starts scheduler services. In particular, it creates the scheduler tick thread (sched_tick_thread), which sets a scheduler variable (sched_tick_deadline) to the Timebase Register's contents, which have been retrieved via mach_absolute_time() [osfmk/ppc/machine_routines_asm.s]. The scheduler tick thread performs periodic scheduler-related bookkeeping, such as aging processor usage, scanning the run queues for timesharing threads that may need to have their priorities recalculated, and computing the Mach Factor. sched_startup() then calls thread_daemon_init() [osfmk/kern/thread.c], which creates kernel threads for running the "terminate" and "stack" daemons. The former deals with terminating threads that have been enqueued for final clean up. The latter allocates stacks for threads that have been enqueued on the stack allocation queue.[21] Finally, sched_startup() calls thread_call_initialize() [osfmk/kern/thread_call.c] to initialize the thread-based callout module. This includes initializing the relevant locks, initializing wait and callout queues, initializing a delayed timer callout, and creating the activate thread that executes callout threads. Callouts are used by various kernel subsystems[22] to register functions to be invoked at some time in the future.

    [21] The thread_invoke() function enqueues threads on the stack allocation queue if it fails to allocate a kernel stack for the thread.

    [22] For example, the kernel uses a callout to display the gearwheel progress animation during a graphical boot.

  • It calls thread_bind() [osfmk/kern/sched_prim.c] to force itself to continue executing on the current (master) processor as additional processors come online.

  • It calls mapping_adjust() [osfmk/ppc/mappings.c] to perform bookkeeping of virtual-to-physical mappings. In this first invocation, mapping_adjust() also initializes a callout for itself, which is used indirectly by the pageout daemon when it attempts to trigger garbage collection.

  • It calls clock_service_create() [osfmk/kern/clock.c] to initialize the clock IPC service facility. clock_service_create() initializes IPC control of clocks by allocating each clock's service and control ports. Moreover, it enables IPC access to each clock by calling ipc_kobject_set() [osfmk/kern/ipc_kobject.c] to make the ports represent that clock kernel object. clock_service_create() also initializes a zone (alarm_zone) for allocating user alarm structures (alarm_t).

  • It calls device_service_create() [osfmk/device/device_init.c] to initialize the device service. This allocates the master device port (master_device_port), which is the host's master privileged I/O object (HOST_IO_MASTER_PORT). Recall that other host special ports were created earlier.

  • It calls shared_file_boot_time_init() [osfmk/vm/vm_shared_memory_server.c], which calls shared_file_init() [osfmk/vm/vm_shared_memory_server.c] to allocate two 256MB regions that are later mapped into address spaces of tasks, allowing them to share the contents of these regions. We will come across these functions again in Section 5.7.8. shared_region_mapping_create() [osfmk/vm/vm_shared_memory_server.c] is called to allocate and initialize data structures for this shared region mapping. shared_com_boot_time_init() [osfmk/vm/vm_shared_memory_server.c] is called to initialize the "comm" region (or the commpage area)[23]a range of pages meant to contain data and text shared between all processes on the systemthe region is mapped read-only into every task's address space. There are separate 32-bit and 64-bit comm regions, each being _COMM_PAGE_AREA_LENGTH (defined to be seven 4KB pages in osfmk/ppc/cpu_capabilities.h) in size. Note that a task structure contains a pointer to the system shared region (system_shared_region). shared_file_boot_time_init() finally calls vm_set_shared_region() [osfmk/vm/vm_shared_memory_server.c] to set this pointer in the current task.

    [23] We will discuss the commpage area in Chapter 6.

  • It calls PE_init_iokit() [pexpert/ppc/pe_init.c] to initialize the I/O Kit. We will look at details of PE_init_iokit() in Section 5.6.

  • It calls commpage_populate() [osfmk/ppc/commpage/commpage.c] to populate the 32-bit and 64-bit comm regions. A comm region resides in wired memory. Its contents include information about processor capabilities and features, a mapping of the Timebase Register, and frequently used routines.

  • It calls bsd_init() [bsd/kern/bsd_init.c] to initialize the BSD portion of the kernel and to initiate user-level startup. We will look at details of bsd_init() in Section 5.7.

  • It calls serial_keyboard_init() [osfmk/ppc/serial_io.c], which checks whether the system console is on the serial port. If not, it simply returns; otherwise it starts a kernel thread running serial_keyboard_start() [osfmk/ppc/serial_io.c], which hands control to serial_keyboard_poll() [osfmk/ppc/serial_io.c]. The latter runs forever, calling scc_getc() [osfmk/ppc/serial_io.c] to retrieve any characters that may be available in the serial port's buffer. The retrieved characters are fed to the keyboard monitor module by calling cons_cinput() [bsd/dev/ppc/km.c]. cons_cinput() uses the console's tty structure to fetch the corresponding entry in the line-discipline switch table (the linesw structure) and calls the receiver interrupt function pointer (l_rint).

  • It unbinds the current thread from the master processor by calling thread_bind() with a PROCESSOR_NULL argument.

  • Finally, kernel_bootstrap_thread() becomes the pageout daemon by calling vm_pageout() [osfmk/vm/vm_pageout.c]. vm_pageout() sets its thread's vm_privilege field to trUE, which allows it to use reserved memory if necessary. It then adjusts its thread's priority, initializes paging parameters, adjusts some other relevant information, and runs forever in vm_pageout_scan() [osfmk/vm/vm_pageout.c], which implements the pageout daemon's core functionality.

Категории