A kernel process is a process that is created in the kernel protection domain and always executes in the kernel protection domain. Kernel processes can be used in subsystems, by complex device drivers, and by system calls. They can also be used by interrupt handlers to perform asynchronous processing not available in the interrupt environment. Kernel processes can also be used as device managers where asynchronous I/O and device management is required.
A kernel process (kproc) exists only in the kernel protection domain and differs from a user process in the following ways:
A kernel process controls directly the kernel threads. Because kernel processes are always in the kernel protection domain, threads within a kernel process are kernel-only threads. For more information on kernel threads, see Understanding Kernel Threads.
A kernel process inherits the environment of its parent process (the one calling the creatp kernel service to create it), but with some exceptions. The kernel process will not have a root directory or a current directory when initialized. All uses of the file system functions must specify absolute path names.
Kernel processes created during phase 1 of system boot must not keep any long-term opens on files until phase 2 of system boot or run time has been reached. This is because Base Operating System changes root file systems between phase 1 and phase 2 of system boot. As a result, the system crashes if any files are open at root file system transition time.
Because kernel processes execute in the more privileged kernel protection domain, a kernel process can access data that user processes cannot. This applies to all kernel data, of which there are three general categories:
The u-block (or u-area) structure exists for kernel processes and contains roughly the same information for kernel processes as for user-mode processes. A kernel process must use kernel services to query or manipulate data from the u-area to maintain modularity and increase portability of code to other platforms.
To ensure binary compatibility with older applications, each kernel process has a stack called the process stack. This stack is used by the process initial thread.
The location of the stack for a kernel process is implementation-dependent. This stack can be located in global memory or in the process-private segment of the kernel process. A kernel process must not assume automatically that its stack is located in global memory.
A kernel process can also access global kernel memory as well as allocate and de-allocate memory from the kernel heaps. Because it runs in the kernel protection domain, a kernel process can access any valid memory location within the global kernel address space. Memory dynamically allocated from the kernel heaps by the kernel process must be freed by the kernel process before exiting. Unlike user-mode processes, memory that is dynamically allocated by a kernel process is not freed automatically upon process exit.
Kernel processes must be provided with a valid cross-memory descriptor to access address regions outside the kernel global address space or kernel process address space. For example, if a kernel process is to access data from a user-mode process, the system call using the process must obtain a cross-memory descriptor for the user-mode region to be accessed. Calling the xmattach or xmattach64 kernel service provides a descriptor that can then be made available to the kernel process.
The kernel process should then call the xmemin and xmemout kernel services to access the targeted cross-memory data area. When the kernel process has completed its operation on the memory area, the cross-memory descriptor must be detached by using the xmdetach kernel service.
A kernel process is created by a kernel-mode routine by calling the creatp kernel service. This service allocates and initializes a process block for the process and sets the new process state to idle. This new kernel process does not run until it is initialized by the initp kernel service, which must be called in the same process that created the new kernel process (with the creatp service). The creatp kernel service returns the process identifier for the new kernel process.
The process is created with one kernel-only thread, called the initial thread. See Understanding Kernel Threads to get more information about threads.
After the initp kernel service has completed the process initialization, the initial thread is placed on the run queue. On the first dispatch of the newly initialized kernel process, it begins execution at the entry point previously supplied to the initp kernel service. The initialization parameters were previously specified in the call to the initp kernel service.
A kernel process terminates when it executes a return from its main entry routine. A process should never exit without both freeing all dynamically allocated storage and releasing all locks owned by the kernel process.
When kernel processes exit, the parent process (the one calling the creatp and initp kernel services to create the kernel process) receives the SIGCHLD signal, which indicates the end of a child process. However, it is sometimes undesirable for the parent process to receive the SIGCHLD signal due to ending a process. In this case, the kproc can call the setpinit kernel service to designate the init process as its parent. The init process cleans up the state of all its child processes that have become zombie processes. A kernel process can also issue the setsid subroutine call to change its session. Signals and job control affecting the parent process session do not affect the kernel process.
A kernel process is initially created with the same process priority as its parent. It can therefore be replaced by a more favored kernel or user process. It does not have higher priority just because it is a kernel process. Kernel processes can use the setpri subroutine to modify their execution priority.
The kernel process can use the locking kernel services to serialize access to critical data structures. This use of locks does not guarantee that the process will not be replaced, but it does ensure that another process trying to acquire the lock waits until the kernel process owning the lock has released it.
Using locks, however, does not provide serialization if a kernel routine can access the critical data while executing in the interrupt environment. Serialization with interrupt handlers must be handled by using locking together with interrupt control. The disable_lock and unlock_enable kernel services should be used to serialize with interrupt handlers.
Kernel processes must ensure that their maximum path lengths adhere to the specifications for interrupt handlers when executing at an interrupt priority more favored than INTBASE. This ensures that system real-time performance is not degraded.
Signals are delivered to exactly one thread within the process which has not blocked the signal from delivery. If all threads within the target process have blocked the signal from delivery, the signal remains pending on the process until a thread unblocks the signal from delivery, or the action associated with the signal is set to ignore by any thread within the process. See Thread Signal Handling for more information on signal handling by threads.
Signals whose action is applied at generation time (rather than delivery time) have the same effect regardless of whether the target is a kernel or user process. A kernel process can poll for unmasked signals that are waiting to be delivered by calling the sig_chk kernel service. This service returns the signal number of a pending signal that was not blocked or ignored. The kernel process then uses the signal number to determine which action should be taken. The kernel does not automatically call signal handlers for a kernel process as it does for user processes.
A kernel process should also use the exception-catching facilities (setjmpx, and clrjmpx) available in kernel mode to handle exceptions that can be caused during run time of the kernel process. Exceptions received during the execution of a kernel process are handled the same as exceptions that occur in any kernel-mode routine.
Unhandled exceptions that occur in kernel mode (in any user process while in kernel mode, in an interrupt handler, or in a kernel process) result in a system crash. To avoid crashing the system due to unhandled exceptions, kernel routines should use the setjmpx, clrjmpx, and longjmpx kernel services to handle exceptions that might possibly occur during run time. See Understanding Exception Handling for more details on handling exceptions.
System calls made by kernel processes do not result in a change of protection domain because the kernel process is already within the kernel protection domain. Routines in the kernel (including routines executing in a kernel process) are bound by the loader to the system call function and not to the system call handler. When system calls use kernel services to access user-mode data, these kernel services recognize that the system call is running within a kernel process instead of a user process and correctly handle the data accesses.
However, the error information returned from a kernel process system call must be accessed differently than for a user process. A kernel process must use the getuerror kernel service to retrieve the system call error information normally provided in the errno global variable for user-mode processes. In addition, the kernel process can use the setuerror kernel service to set the error information to 0 before calling the system call. The return code from the system call is handled the same for all processes.
Kernel processes can use only a restricted set of the base system calls. System Calls Available to Kernel Extensions lists system calls available to kernel processes.