Invoking specific kernel functions (system calls) is a natural part of application development on GNU/Linux. But what about going in the other direction, kernel space calling user space? It turns out that there are a number of applications for this feature that you likely use every day. For example, when the kernel finds a device for which a module needs to be loaded, how does this process occur? Dynamic module loading occurs from the kernel through the usermode-helper process.
Let's begin with an exploration of usermode-helper, its application programming interface (API), and some of the examples of where this feature is used in the kernel. Then, using the API, you'll build a sample application to better understand how it works and its limitations.
The usermode-helper API is a simple API with a well-known set of options. For example, to create a process from user space, you commonly provide the name of the executable, the options for the executable, and a set of environment variables (refer to the man page for execve
). The same applies for creating a process from the kernel. But because you're starting the process from kernel space, a few additional options are available.
This article explores the usermode-helper API from the 2.6.27 kernel.
Table 1 shows the core set of kernel functions available in the usermode-helper API.
API function | Description |
---|---|
call_usermodehelper_setup | Prepare a handler for a user-land call |
call_usermodehelper_setkeys | Set the session keys for a helper |
call_usermodehelper_setcleanup | Set a cleanup function for the helper |
call_usermodehelper_stdinpipe | Create a stdin pipe for a helper |
call_usermodehelper_exec | Invoke the user-land call |
Also in this table are a couple of simplification functions that encapsulate the kernel functions in Table 2 (requiring a single call instead of multiple calls). These simplification functions are useful for most cases, so you're encouraged to use them, if possible.
API function | Description |
---|---|
call_usermodehelper | Make a user-land call |
call_usermodehelper_pipe | Make a user-land call with a pipe stdin |
call_usermodehelper_keys | Make a user-land call with session keys |
Let's first walk through the core functions, then explore the capabilities that the simplification functions provide. The core API operates using a handler reference called a subprocess_info
structure. This structure (which can be found in ./kernel/kmod.c) aggregates all of the necessary elements for a given usermode-helper instance. The structure reference is returned from a call to call_usermodehelper_setup
. The structure (and subsequent calls) is further configured through calls to call_usermodehelper_setkeys
(for credentials storage), call_usermodehelper_setcleanup
, and call_usermodehelper_stdinpipe
. Finally, once configuration is complete, you can invoke the configured user-mode application through a call to call_usermodehelper_exec
.
This method provides a necessary function for invoking user-space applications from the kernel. Although there are legitimate uses for this functionality, you should strongly consider whether other implementations are needed. This is one approach, but other approaches are better suited.
The core functions provide you with the greatest amount of control, where the helper functions do more of the work for you in a single call. The pipe-related calls (call_usermodehelper_stdinpipe
and the helper function call_usermodehelper_pipe
) create an associated pipe for use by the helper. Specifically, a pipe is created (a file structure in the kernel). The pipe is readable by the user-space application and writable by the kernel side. As of this writing, core dumps are the only application that can use a pipe with a usermode-helper. In this application (./fs/exec.c do_coredump()
), the core dump is written through the pipe from kernel space to user space.
The relationship between these functions and the sub_processinfo
along with the details of the subprocess_info
structure is shown in Figure 1.
The simplification functions in Table 2 perform the call_usermodehelper_setup
function and call_usermodehelper_exec
function internally. The last two calls in Table 2 invoke the call_usermodehelper_setkeys
and call_usermodehelper_stdinpipe
, respectively. You can find the source to call_usermodehelper_pipe
in ./kernel/kmod.c and to call_usermodehelper
and call_usermodhelper_keys
in ./include/linux/kmod.h.
Let's now look at some of the places in the kernel where the usermode-helper API is put to use. Table 3 doesn't provide an exclusive list of applications but represents a cross-section of interesting uses.
Application | Source location |
---|---|
Kernel module loading | ./kernel/kmod.c |
Power management | ./kernel/sys.c |
Control groups | ./kernel/cgroup.c |
Security key generation | ./security/keys/request_key.c |
Kernel event delivery | ./lib/kobject_uevent.c |
One of the most straightforward applications of the usermode-helper API is loading kernel modules from kernel space. The function request_module
encapsulates the functionality of the usermode-helper API and provides a simple interface. In a common usage model, the kernel identifies a device or needed service and makes a call to request_module
to have the module loaded. Through the usermode-helper API, the module is loaded into the kernel via modprobe
(the application invoked in user space via request_module
).
A similar application to module loading is device hot-plugging (to add or remove devices at run time). This feature is implemented with the usermode-helper API, invoking the /sbin/hotplug utility in user space.
An interesting application of the usermode-helper API (via request_module
) is the textsearch API (./lib/textsearch.c). This application provides a configurable text searching infrastructure in the kernel. This application uses the usermode-helper API through the dynamic loading of search algorithms as loadable modules. In the 2.6.30 kernel release, three algorithms are supported, including Boyer-Moore (./lib/ts_bm.c), a naive finite-state machine approach (./lib/ts_fsm.c), and finally the Knuth-Morris-Pratt algorithm (./lib/ts_kmp.c).
The usermode-helper API also supports Linux in an orderly system shutdown. When a system power-off is necessary, the kernel invokes the /sbin/poweroff command in user space to accomplish it. Other applications are listed in Table 3, with the accompanying source location.
You'll find the source and API for the usermode-helper API in kernel/kmod.c (illustrating its primary use as the kernel-space kernel module loader). The implementation uses kernel_execve
for the dirty work. Note that kernel_execve
is the function used to start the init
process at boot time and does not use the usermode-helper API.
The implementation of the usermode-helper API is quite simple and straightforward (see Figure 2). The work of the usermode-helper begins with the call to call_usermodehelper_exec
(which is used to kick off the user-space application from a preconfigured subprocess_info
structure). This function accepts two arguments: the subprocess_info
structure reference and an enumeration type (whether to not wait, wait for the process to be kicked of, or wait for the entire process to be completed). The subprocess_info
(or rather, the work_struct
element of this structure) is then enqueued onto a work structure (khelper_wq
), which asynchronously performs the invocation.
When an element is placed onto the khelper_wq
, the handler function for the work queue is invoked (in this case, __call_usermodehelper
), which is run through the khelper
thread. This function begins by dequeuing the subprocess_info
structure, which contains all of the necessary information for the user-space invocation. The path next depends upon the wait
variable enumeration. If the requester wants to wait for the entire process to finish, including user-space invocation (UMH_WAIT_PROC
) or not wait at all (UMH_NO_WAIT
), then a kernel thread is created from the function wait_for_helper
. Otherwise, the requester simply wants to wait for the user-space application to be invoked (UMH_WAIT_EXEC
) but not complete. In this case, a kernel thread is created for ____call_usermodehelper()
.
In the wait_for_helper
thread, a SIGCHLD signal handler is installed, and another kernel thread is created for ____call_usermodehelper
. But in the wait_for_helper
thread, a call is made to sys_wait4
to await termination of the ____call_usermodehelper
kernel thread (indicated by a SIGCHLD signal). The thread then performs any necessary cleanup (either freeing the structures for UMH_NO_WAIT
or simply sending a completion notification back to call_usermodehelper_exec()
.
The function ____call_usermodehelper
is where the real work happens for getting the application started in user space. This function begins by unblocking all signals and setting the session key ring. It also installs the stdin
pipe (if requested). After a bit more initialization, the user-space application is invoked through a call to kernel_execve
(from kernel/syscall.c), which includes the previously defined path
, argv
list (including the user-space application name), and environment. When this process is complete, the thread exits through a call to do_exit()
.
This process also uses Linux completions, which is a semaphore-like operation. When the call_usermodehelper_exec
function is invoked, a completion is declared. After the subprocess_info
structure is placed on the khelper_wq
, a call is made to wait_for_completion
(using the completion variable as its only argument). Note that this variable is also stored in the subprocess_info
structure as the complete
field. When the child threads want to wake up the call_usermodehelper_exec
function, they call the kernel method complete
, noting the completion variable from the subprocess_info
structure. This call unlocks the function so that it can continue. You can find the implementation of this API in include/linux/completion.h.
You'll find more information on the usermode-helper API by following the links in the Resources section.
Now, let's look at a simple use of the usermode-helper API. You'll first look at the standard API, then learn how to simplify things further using the helper functions.
For this demonstration, you develop a simple loadable kernel module that invokes the API. Listing 1 presents the boilerplate module functions, defining the module entry and exit functions. These two functions are invoked on modprobe
or insmod
of the module (module entry function) and rmmod
of the module (module exit).
#include <linux/module.h>#include <linux/init.h>#include <linux/kmod.h>MODULE_LICENSE( "GPL" );static int __init mod_entry_func( void ){ return umh_test();}static void __exit mod_exit_func( void ){ return;}module_init( mod_entry_func );module_exit( mod_exit_func );
The use of the usermode-helper API is shown in Listing 2, which you'll explore in detail. The function begins with the declaration of a variety of needed variables and structures. Start with the subprocess_info
structure, which contains all of the information necessary to perform the user-space invocation. This invocation is initialized when you call call_usermodehelper_setup
. Next, define your argument list, called argv
. This list is similar to the argv
list used in common C
programs and defines the application (first element of the array) and argument list. A NULL terminator is required to indicate the end of the list. Note here that the argc
variable (argument count) is implicit, because the length of the argv
list is known. In this example, the application name is /usr/bin/logger, and its argument is help!
, which is followed by your terminating NULL. The next required variable is the environment array (envp
). This array is a list of parameters that define the execution environment for the user-space application. In this example, you define a few typical parameters that are defined for the shell and end with a terminating NULL entry.
static int umh_test( void ){ struct subprocess_info *sub_info; char *argv[] = { "/usr/bin/logger", "help!", NULL }; static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL }; sub_info = call_usermodehelper_setup( argv[0], argv, envp, GFP_ATOMIC ); if (sub_info == NULL) return -ENOMEM; return call_usermodehelper_exec( sub_info, UMH_WAIT_PROC );}
Next, make a call to call_usermodehelper_setup
to create your initialized subprocess_info
structure. Note that you use your previously initialized variables along with a fourth parameter that indicates the GFP mask for memory initialization. Internal to the setup function, there's a call to kzalloc
(which allocates kernel memory and zeroes it). This function requires either GFP_ATOMIC
or the GFP_KERNEL
flag (where the former defines that the call should not sleep and the latter that sleep is possible). After a quick test of your new structure (namely, it's not NULL), continue to make the call using the call_usermodehelper_exec
function. This function takes your subprocess_info
structure and an enumeration defining whether to wait (described in the internals section). And that's it! Once the module is loaded, you should see the message in your /var/log/messages file.
You can simplify this process further by using the call_usermodehelper
API function, which performs the call_usermodehelper_setup
and call_usermodehelper_exec
functions together. As shown in Listing 3, this not only removes a function but also removes the need for the caller to manage the subprocess_info
structure.
static int umh_test( void ){ char *argv[] = { "/usr/bin/logger", "help!", NULL }; static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL }; return call_usermodehelper( argv[0], argv, envp, UMH_WAIT_PROC );}
Note that in Listing 3, the same requirements exist to set up and make the call (such as initializing the argv
and envp
arrays). The only difference here is that the helper function performs the setup
and exec
functions.
The usermode-helper API is an important aspect to the kernel, given its wide and varying use (from kernel module loading, device hot-plugging, and event distribution for udev). Although it's important to validate genuine applications of the API, it's an important aspect of the kernel to understand and therefore a useful addition to your Linux kernel toolkit.
联系客服