WM5.0内核启动过程
The Boot Process
If you're a systems programmer, you might enjoy, as I do, seeing how a system boots up. When you think about it, booting up poses some interesting problems. How does the system load its first process when the process loader is part of that process you want to load? How do you deal with 30 different CPUs, each with its own method of initialization?
In the case of Windows CE, we have a somewhat better view of this process. Because the hardware varies radically across the different platforms, Windows CE requires that OEMs write some of the initialization code. In each instance, this initialization code is incorporated in the HAL (hardware abstraction layer), under the kernel. When an OEM builds the system for a specific hardware platform, the HAL is statically linked with the Windows CE kernel code to produce NK.exe.
Actually, the OEM writes far more than the HAL when porting Windows CE to a new platform. The OEM also writes a thin layer under the Graphics Windowing and Event Subsystem (GWES) to link in some of the more basic drivers used by GWE. In addition, the OEM must write a series of device drivers, from a display device driver to drivers for the keyboard, touch panel, serial, and audio devices. The actual collection of drivers is, of course, dependent on the hardware. This collection of the HAL layer plus the drivers is called the OEM Adaptation Layer, or OAL.
In any case, let's get back to the boot process. This boot process is described through the documentation and code examples provided in the Platform Builder. Our journey starts, as with any CPU, at the occurrence of a reset.
Reset
When the system is reset, the CPU jumps to the entry point of NK.exe, which is the kernel module for Windows CE.1 The code at the entry point is actually written by the OEM, not Microsoft. This routine, written in assembler, is traditionally named Startup and is responsible for initializing the CPU into a known state for the kernel. Since most CPUs supported by Windows CE are embedded CPUs, they generally have a number of registers that must be set to configure the system for the speed and sometimes even the base address of memory. Startup is also responsible for initializing any caches and for ensuring that the system is in an uncached, flat addressing mode.
NK.exe
When Startup has completed its tasks, it jumps to the entry point of the kernel, KernelStart. This is the entry point for the Microsoft written code for NK. KernelStart configures the virtual memory manager, initializes the interrupt vector table to a default handler, and calls down to the OEM layer to initialize the debug serial port.2 KernelStart then initializes its local heap by copying the initialized heap data from ROM into system RAM, in a routine named KernelRelocate. Now that the local heap for NK.exe has been initialized, the code can start acting less like a loader and more like a program. The kernel then calls back down to the HAL to the OEMInit routine.
The job of OEMInit, which is customarily written in C, is to initialize any OEM-specific hardware. This includes hooking interrupts, initializing timers, and testing memory.3 Many systems perform some initial configuration of integrated peripherals, if only to place them in a quiescent state until the driver for that peripheral can be loaded. The OEMInit routine is generally responsible for drawing the splash screen on the display during a boot process.
When OEMInit returns, the kernel calls back into the HAL to ask whether any additional RAM is available to the system. When an OEM creates a ROM image of the Windows CE files, it makes some preliminary estimates about the size and location of the RAM as well as defining the size and location of the ROM. This routine, OEMGetExtensionDRAM, allows the OEM to tell the kernel about additional RAM that can be used by the system. Once OEMGetExtensionDRAM returns, the kernel enables interrupts and calls the scheduler to schedule the first thread in the system.
At this point, the kernel looks for the file FileSys.exe and launches that application. The system loader—the code that loads EXEs and DLLs into memory—looks in the object store to locate FileSys.exe. The directories that are searched and the order of the search is the same search order that I described in Chapter 6. However, if the module, in this case FileSys, isn't found, one more place is queried for the module.
The kernel has extensions to determine whether the system is connected to a debugging station. A debugging station is a PC running a special debugging tool.4 The connection between the PC and the Windows CE system can be through a serial port, a parallel port, or a dedicated Ethernet link. If such a connection is found, the loader takes the additional step of looking on the PC for modules (EXEs and DLLs) when the system asks it to load a file. In effect, this procedure seamlessly extends the \Windows directory on the Windows CE system to include any modules in a specific directory on the debugging PC. This procedure allows the system to load modules that aren't in the initial ROM image during the boot process. Later, when the system is running, you can directly load modules from the PC without having to first copy them into the object store of the Windows CE system.
FileSys.exe
FileSys is the process that manages the file system, the database functions, and more important at this stage, the registry. When FileSys is loaded, it looks in the RAM to see whether it can find a file system already initialized. If one is found, FileSys uses the already initialized file system, which allows Windows CE devices to retain the data in their RAM-based file systems over a reboot of the system.
If FileSys doesn't find a file system, it creates one that merges an empty RAM file system with the files on ROM. FileSys knows which files are in ROM by means of a table that's built into the ROM image by the ROM builder program, which merged all the disparate programs into one image. FileSys reads the default directory structure from a file stored on ROM, which is composed of entries suggested by Microsoft for the OEM. In addition to initializing the file system, FileSys creates default database images and a default registry. The initial images of the default databases and default registry are also defined in files in ROM written by the OEM and Microsoft. This file-driven initialization process allows OEMs to customize the initial images of the file system from the directory tree to the individual entries in the registry.
Launching Optional Processes
Once FileSys has initialized, the system initialization can proceed. The kernel needs to wait because, at this point, it needs data from the registry to continue the boot process. Specifically, the kernel looks in the registry for values under the key [HKEY_LOCAL_MACHINE]\Init. The values in this key provide the name, order, and dependencies of a set of processes that should be loaded as part of the boot process. The processes to be launched are specified by values named Launchxx where the xx is a number defining the order of the launch. An optional value, Dependxx, can be used to make the launch of a process dependent on another process specified earlier in the order. For example, the following set of values was taken from the registry of a typical Handheld PC.
Value Data Comments
Launch10 SHELL.EXE
Launch20 DEVICE.EXE
Launch30 GWES.EXE
Depend30 0014 Depends on Device (0x14 == 20)
Launch50 EXPLORER.EXE
Depend50 0014 001E Depends on Device and GWE
While I've listed the values in their launch order for clarity, the values don't need to be in order in the registry. The numbers embedded in the names of the values define the launch order.
The kernel loads each of the modules listed in their own process space. When a process completes its initialization successfully, it signals this event to the kernel by calling the function SignalStarted and passing the application's launch number. The kernel knows from these calls to SignalStarted that any dependent processes can now be launched.
What's interesting here is that each of these components of the operating system functions as a standard user-level process. Just because a process appears in this list doesn't mean that it's part of the operating system. While this launch list is generally used only by OEMs, you can insert other processes in this list, as long as the functions needed by that application have been loaded earlier in the list. For example, you could write an application that's loaded after Device and before GWES.exe as long as that application didn't make any calls to the window manager or the graphics functions until GWE is initialized. On the other hand, launching an application with a standard user interface before Explorer loads can confuse Explorer. So unless you need to launch a process to support system services, you should use Explorer to launch your applications on startup. One additional point—you can't separately launch an application that depends on Explorer to launch successfully because Explorer.exe doesn't call SignalStarted during its initialization. Now let's follow this sequence and examine each of these launched processes.
Shell.exe
Shell is an interesting process because it's not even in the ROM of most systems. Shell.exe is the Windows CE side of CESH, the command line–based monitor. Because Shell.exe isn't in the ROM, the only way to load it is by connecting the system to the PC debugging station so that the file can be automatically downloaded from the PC.
CESH uses the kernel link to the debugging PC to communicate with the programmer. Instead of opening a file on the PC, CESH opens a console session on the PC. The CESH debugger provides a number of useful functions to the Windows CE OEM. First it gives the OEM developer a command line shell, running on a PC that can be used to launch applications, query system status, and read and write memory on the system.
CESH also lets the OEM developer manipulate a very handy feature of debug builds of Windows CE named debug zones. When you're developing software, it's often useful to insert debugging messages that print out information. On a Windows CE system, these debugging messages are sent via the debug serial port. The problem is that too many messages can hide a critical error behind a blizzard of irrelevant informational messages. On the other hand, Murphy says that the day after you strip all your debugging messages from a section of code, you'll need those messages to diagnose a newly reported bug. Debug zones allow the developer to interactively enable and disable sets of debug messages that are built into debug builds of Windows CE. All of the base processes bundled with Windows CE as well as all the device drivers have these debugging messages built into them. Every message is assigned to one of 16 defined debug zones for that process or DLL. So, a developer can use CESH to enable or disable each of the 16 zones for a module, which enables or disables the messages for that zone.