Kernel APIs, Part 1: Invoking user-space applications from the kernel

本文详细介绍了Linux系统如何通过KernelAPIs从内核调用用户空间应用,包括核心功能、使用示例以及常见应用场景。通过内部实现解析和示例代码展示,读者可以更好地理解这一特性及其工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

Kernel APIs, Part 1: Invoking user-space applications from the kernel

Implementation and use of the usermode-helper API

 

 

Summary:  The Linux® system call interface permits user-space applications to invoke functionality in the kernel, but what about invoking user-space applications from the kernel? Explore the usermode-helper API, and learn how to invoke user-space applications and manipulate their output.

View more content in this series

 

 

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-helperprocess.

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

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.

Kernel version

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.


Table 1. Core functions 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.


Table 2. Simplifications of the usermode-helper API
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 tocall_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 tocall_usermodehelper_exec.

Disclaimer

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.


Figure 1. Usermode-helper API relationships
Usermode-helper API relationships  

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 andcall_usermodhelper_keys in ./include/linux/kmod.h.

Why invoke a user-space application from the kernel?

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.


Table 3. Applications of the usermode-helper API in the kernel
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.

Usermode-helper API internals

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). Thesubprocess_info (or rather, the work_struct element of this structure) is then enqueued onto a work structure (khelper_wq), which asynchronously performs the invocation.


Figure 2. Internal implementation of the usermode-helper API 
Internal implementation of the usermode-helper API  

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_infostructure, which contains all of the necessary information for the user-space invocation. The path next depends upon the waitvariable 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 pathargv 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 towait_for_completion (using the completion variable as its only argument). Note that this variable is also stored in thesubprocess_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.

Sample application

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).


Listing 1. Module boilerplate functions
#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 argcvariable (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.


Listing 2. Simple usermode_helper API test
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 thecall_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.


Listing 3. An even simpler usermode-helper API test
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.

Going further

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.


Resources

Learn

  • Little information exists about the usermode-helper API, but the implementation is quite clean and simple to follow. You can review the implementation through LXR (the Linux Cross Referencer—source browser for all source revisions). The two primary files of interest are kmod.c and kmod.h

  • The /proc file system provides a method for communicating between the kernel and user space—namely, though a virtual file system. You can learn more about the /proc file system in "Access the Linux kernel using the /proc filesystem" (developerworks, March 2006). 

  • The Linux system call interface provides the means for user-space applications to invoke kernel functionality. For more details on Linux system calls, including how to add new system calls, see "Kernel command using system calls" (developerworks, March 2007). 

  • To illustrate the usermode-helper API, this article uses loadable kernel modules to install test applications into the kernel. To learn more about loadable kernel modules and their implementation, check out "Anatomy of Linux loadable kernel modules" (developerworks, July 2008). 

  • To learn more about the 2.6 kernel work queue interface, check out this older Linux Journal article from 2003, which provides a good introduction to the API and operation of kernel work queues. 

  • In the developerWorks Linux zone, find more resources for Linux developers. 

  • Stay current with developerWorks technical events and Webcasts

  • Follow developerWorks on Twitter.

Get products and technologies

  • With IBM trial software, available for download directly from developerWorks, build your next development project on Linux.

Discuss

  • Get involved in the My developerWorks community. Connect with other developerWorks users while exploring the developer-driven blogs, forums, groups, and wikis. 
资源下载链接为: https://pan.quark.cn/s/f989b9092fc5 今天给大家分享一个关于C#自定义字符串替换方法的实例,希望能对大家有所帮助。具体介绍如下: 之前我遇到了一个算法题,题目要求将一个字符串中的某些片段替换为指定的新字符串片段。例如,对于源字符串“abcdeabcdfbcdefg”,需要将其中的“cde”替换为“12345”,最终得到的结果字符串是“ab12345abcdfb12345fg”,即从“abcdeabcdfbcdefg”变为“ab12345abcdfb12345fg”。 经过分析,我发现不能直接使用C#自带的string.Replace方法来实现这个功能。于是,我决定自定义一个方法来完成这个任务。这个方法的参数包括:原始字符串originalString、需要被替换的字符串片段strToBeReplaced以及用于替换的新字符串片段newString。 在实现过程中,我首先遍历原始字符串,查找需要被替换的字符串片段strToBeReplaced出现的位置。找到后,就将其替换为新字符串片段newString。需要注意的是,在替换过程中,要确保替换操作不会影响后续的查找和替换,避免遗漏或重复替换的情况发生。 以下是实现代码的大概逻辑: 初始化一个空的字符串result,用于存储最终替换后的结果。 使用IndexOf方法在原始字符串中查找strToBeReplaced的位置。 如果找到了,就将originalString中从开头到strToBeReplaced出现位置之前的部分,以及newString拼接到result中,然后将originalString的查找范围更新为strToBeReplaced之后的部分。 如果没有找到,就直接将剩余的originalString拼接到result中。 重复上述步骤,直到originalStr
<think>嗯,用户问的是关于Maven中一个特定的依赖项迁移问题:xml-apis:xml-apis:jar:2.0.2被重定位到1.0.b2版本。首先,我需要确定用户为什么会遇到这个问题。通常这种情况发生在使用Maven构建项目时,可能出现了依赖冲突或者版本不兼容的警告。用户可能是在构建日志中看到相关的提示信息,所以想知道如何处理。 接下来,我需要回忆xml-apis这个库的相关信息。xml-apis原本是Apache Xerces项目的一部分,提供了XML相关的API,比如DOM、SAX等。但后来,这个库被拆分或重新组织,导致不同版本之间的变动。尤其是2.0.2版本可能已经被标记为过时,或者被重定向到另一个版本,比如1.0.b2,这可能是因为项目维护者进行了版本号的调整或者模块的重组。 用户的问题中提到“relocated”,这在Maven中通常指的是使用<relocation>标签进行的依赖重定向。当一个artifact被移动到不同的groupId或artifactId时,Maven仓库会通过重定向来确保向后兼容,但版本号的变化可能意味着存在重大变更或修复。需要解释为什么会出现这种情况,可能的原因包括版本错误、依赖冲突,或者维护者主动调整。 然后,我需要考虑用户的实际需求。用户可能希望解决构建中的警告或错误,或者想知道是否需要手动修改pom.xml中的依赖声明。需要给出具体的解决步骤,比如如何排除旧版本,引入新版本,或者确认是否需要升级到其他替代库,比如使用XML相关的标准API或引入新的依赖如xercesImpl。 另外,用户可能需要了解更深层次的原因,比如为什么版本号反而从2.0.2降到了1.0.b2,这可能是版本管理上的混乱,或者是维护者重新组织了项目结构,导致版本号的重新设定。需要解释清楚这种情况在开源项目中可能发生的原因,以及如何正确应对。 还需要检查是否有常见的误操作或误解,比如用户可能误以为版本号越高越好,但在这里反而需要降级,所以需要解释版本号变化背后的原因,避免用户产生困惑。同时,提醒用户注意依赖冲突,使用Maven的依赖树分析工具来排查问题,确保项目稳定。 最后,总结解决步骤,提供示例代码,让用户能够直接修改pom.xml,并验证是否解决了问题。同时,给出进一步的建议,比如寻找替代库或升级到更新的版本,保持项目的依赖健康。</think>关于 `xml-apis:xml-apis:jar` 的版本重定向问题,以下是技术解析和解决方案: --- ### 1. **现象解释** 当 Maven 提示 `xml-apis:xml-apis:jar:2.0.2 has been relocated to xml-apis:xml-apis:jar:1.0.b2`,表示该依赖的**元数据已被官方重新映射**。 本质是:**旧版本 `2.0.2` 被标记为废弃**,并强制指向 `1.0.b2` 版本。 --- ### 2. **根本原因** - **历史背景**:`xml-apis` 是 Apache Xerces 项目的衍生库,早期用于提供 XML 解析相关 API(如 DOM、SAX 等)。 - **版本混乱**:由于历史维护问题,`2.0.2` 版本实际是**错误发布**的版本号,官方最终将其修正为 `1.0.b2`。 - **Maven 重定向**:通过 `pom.xml` 中的 `<relocation>` 标签强制修正版本映射。 --- ### 3. **影响范围** | 问题类型 | 具体表现 | |----------------|-------------------------------| | **依赖冲突** | 多个模块同时声明不同版本 | | **构建警告** | `mvn install` 输出版本重定向提示 | | **运行时错误** | 类加载冲突(如 `NoSuchMethodError`)| --- ### 4. **解决方案** #### 步骤 1:显式声明正确版本 在 `pom.xml` 中**强制指定版本**,覆盖旧版本: ```xml <dependency> <groupId>xml-apis</groupId> <artifactId>xml-apis</artifactId> <version>1.0.b2</version> </dependency> ``` #### 步骤 2:排除冲突依赖 若其他库间接引用了 `2.0.2`,需手动排除: ```xml <dependency> <groupId>some.library</groupId> <artifactId>problematic-lib</artifactId> <exclusions> <exclusion> <groupId>xml-apis</groupId> <artifactId>xml-apis</artifactId> </exclusion> </exclusions> </dependency> ``` #### 步骤 3:验证依赖树 通过命令检查最终依赖版本: ```bash mvn dependency:tree -Dincludes=xml-apis:xml-apis ``` --- ### 5. **替代方案建议** - **优先使用标准 API**:Java 6+ 已内置 `javax.xml` 包,无需额外依赖 `xml-apis`。 - **升级到新库**:改用 `xerces:xercesImpl` 提供完整 XML 解析支持: ```xml <dependency> <groupId>xerces</groupId> <artifactId>xercesImpl</artifactId> <version>2.12.2</version> </dependency> ``` --- ### 6. **技术总结** | 关键点 | 说明 | |-----------------------|--------------------------------------------------------------------| | **版本重定向本质** | Maven 仓库元数据修正,非代码变更 | | **长期建议** | 避免直接依赖 `xml-apis`,改用 Java 原生 API 或 `xercesImpl` | | **兼容性检查** | 确保 `1.0.b2` 与其他库(如 Xalan、Saxon)无冲突 | 通过以上操作可解决版本冲突问题。若仍遇异常,需检查是否有多处隐式依赖未排除。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值