[FreeBSD] Run-time structure of the kernel

2024. 7. 10. 17:50ComputerScience/FreeBSD

 

 

 

Run-Time Organization

  • The kernel can be logically divided into a top half and a bottom half
  • The top half of the kernel provides services to processes in response to system calls or traps. This software can be thought of as a library of routines shared by all processes. The top half of the kernel executes in a privileged execution mode, in which it has access both to kernel data structures and to the context of user-level processes.
  • The context of each process is contained in two areas of memory reserved for process-specific information. The first of these areas is the process structure, which has historically contained the information that is necessary even if the process has been swapped out.
    • In FreeBSD, this information includes the identifiers associated with the process, the process’s rights and privileges, its descriptors, its memory map, pending external events and associated actions, maximum and current resource utilization, and many other things.
  • The second is the thread structure, which has historically contained the information that is not necessary when the process is swapped out. (프로세스가 swap되면 필요 없어질 정보들)
    • In FreeBSD, the thread-structure information of each process includes the hardware thread state block (TSB), its kernel stack, and minor additional information for debugging and creating a core dump.
  • Deciding what was to be stored in the process structure (유지) and the thread structure (변동) was far more important in previous systems than it was in FreeBSD. As memory became a less limited resource, most of the thread structure was merged into the process structure for convenience. (swap 이후 사라지지 않아도 OK)

 

 

 

Run-time structure of the kernel

 

 

 

  • The bottom half of the kernel comprises routines that are invoked to handle hardware interrupts. Activities in the bottom half of the kernel are synchronous with respect to the interrupt source but are asynchronous, with respect to the top half, and the software cannot depend on having a specific (or any) process running when an interrupt occurs. Thus, the state information for the process that initiated the activity is not available. The top and bottom halves of the kernel communicate through data structures, generally organized around work queues.
  • The FreeBSD kernel is rarely preempted to run another user process while executing in the top half of the kernel—for example, while executing a system call—although it will explicitly give up the processor if it must wait for an event or for a shared resource.
  • Its execution may be interrupted, however, by the need to run a real-time process or by interrupts for the bottom half of the kernel. When an interrupt is received, the kernel process that handles that device is scheduled to run. Normally these device-interrupt processes have a higher priority than user processes or processes running in the top half of the kernel. Thus, when an interrupt causes a device-interrupt process to be made runnable, it will usually preempt (선점하다) the currently running process. When a process running in the top half of the kernel wants to add an entry to the work list for a device, it needs to ensure that it will not be preempted by that device part way through linking the new element onto the work list. In FreeBSD, the work list is protected by a mutex.
  • Any process (top or bottom half) seeking to modify the work list must first obtain the mutex. Once held, any other process seeking to obtain the mutex will wait until the process holding it has finished modifying the list and released the mutex. (뮤테스 락 관련 내용!)
  • Processes cooperate in the sharing of system resources, such as the disks and memory. The top and bottom halves of the kernel also work together in implementing certain system operations, such as I/O. Typically, the top half will start an I/O operation, and then relinquish (포기하다) the processor; then the requesting process will sleep, awaiting notification from the bottom half that the I/O request has completed.

 

 

 

Entry to the Kernel

  • When a process enters the kernel through a trap or an interrupt, (트랩이나 인터럽트를 통해 커널로 진입 가능) the kernel must save the current machine state before it begins to service the event. For the PC, the machine state that must be saved includes the program counter, the user stack pointer, the general-purpose registers, and the processor status longword. The PC trap instruction saves the program counter and the processor status longword as part of the exception stack frame; the user stack pointer and registers must be saved by the software trap handler. If the machine state were not fully saved, the kernel could change values in the currently executing program in improper ways. 
  • Since interrupts may occur between any two user-level instructions (and on some architectures between parts of a single instruction), and because they may be completely unrelated to the currently executing process, an incompletely saved state could cause correct programs to fail in mysterious and not easily reproducible ways.
  • The exact sequence of events required to save the process state is completely machine dependent, although the PC provides a good example of the general procedure. A trap or system call will trigger the following events:
    • The hardware switches into kernel (supervisor) mode, so that memory-access checks are made with kernel privileges, references to the stack use the per-process kernel stack, and privileged instructions can be executed.
    • The hardware pushes onto the per-process kernel stack the program counter, processor status longword, and information describing the type of trap. (On architectures other than the PC, this information can include the system-call number and general-purpose registers as well.)
    • An assembly-language routine saves all state information not saved by the hardware. On the PC, this information includes the general-purpose registers and the user stack pointer, also saved onto the per-process kernel stack.

 

  • After this preliminary state saving, the kernel calls a C routine that can freely use the general-purpose registers as any other C routine would, without concern about changing the unsuspecting process’s state.
  • There are three major kinds of handlers, corresponding to particular kernel entries:
    1. Syscall() for a system call
    2. Trap() for hardware traps and for software-initiated traps other than system calls
    3. The appropriate device-driver interrupt handler for a hardware interrupt

 

  • Each type of handler takes its own specific set of parameters. For a system call, they are the system-call number and an exception frame. For a trap, they are the type of trap, the relevant floating-point and virtual-address information related to the trap, and an exception frame. (The exception-frame arguments for the trap and system call are not the same. The PC hardware saves different information based on different types of traps.) For a hardware interrupt, the only parameter is a unit (or board) number.

 

 

 

https://www2.it.uu.se/education/course/homepage/os/vt18/module-1/exception-and-interrupt-handling/

 

 

 

Return from the Kernel

  • When the handling of the system entry is completed, the user-process state is restored, and control returns to the user process. Returning to the user process reverses the process of entering the kernel: (커널 진입의 역과정) 
    • An assembly-language routine restores the general-purpose registers and user-stack pointer previously pushed onto the stack. (이전 상태로 복구한다)
    • The hardware restores the program counter and program status longword, and switches to user mode, so that future references to the stack pointer use the user’s stack pointer, privileged instructions cannot be executed, and memory-access checks are done with user-level privileges.
  • Execution then resumes at the next instruction in the user’s process.