从Linux内核调用用户空间应用程序

简介: Linux? 系统调用接口允许用户空间应用程序调用内核功能,那么从 内核调用用户空间应用程序又如何呢?探索 usermode-helper API,并学习如何调用用户空间应用程序并控制其输出。

调用特定的内核函数(系统调用)是 GNU/Linux 中软件开发的原本就有的组成部分。但如果方向反过来呢,内核空间调用用户空间?确实有一些有这种特性的应用程序需要每天使用。例如,当内核找到一个设备,这时需要加载某个模块,进程如何处理?动态模块加载在内核通过 usermode-helper 进程进行。

让我们从探索 usermode-helper 应用程序编程接口(API)以及在内核中使用的例子开始。 然后,使用 API 构造一个示例应用程序,以便更好地理解其工作原理与局限。

usermode-helper API

usermode-helper API 是个很简单的 API,其选项为用户熟知。例如,要创建一个用户空间进程,通常只要设置名称为 executable,选项都为 executable,以及一组环境变量(指向 <font face="Courier New">execve</font> 主页)。创建内核进程也是一样。但由于创建内核空间进程,还需要设置一些额外选项。

内核版本

本文探讨的是 2.6.27 版内核的 usermode-helper API。

表 1 展示的是 usermode-helper API 中一组关键的内核函数


表 1. usermode-helper API 中的核心函数

API 函数描述
<font face="Courier New">call_usermodehelper_setup</font>准备 user-land 调用的处理函数
<font face="Courier New">call_usermodehelper_setkeys</font>设置 helper 的会话密钥
<font face="Courier New">call_usermodehelper_setcleanup</font>为 helper 设置一个清空函数
<font face="Courier New">call_usermodehelper_stdinpipe</font>为 helper 创建 <font face="Courier New">stdin</font> 管道
<font face="Courier New">call_usermodehelper_exec</font>调用 user-land

表 2 中还有一些简化函数,它们封装了的几个内核函数(用一个调用代替多个调用)。这些简化函数在很多情况下都很有用,因此尽可能使用他们。


表 2. usermode-helper API 的简化

API 函数描述
<font face="Courier New">call_usermodehelper</font>调用 user-land
<font face="Courier New">call_usermodehelper_pipe</font>使用 <font face="Courier New">stdin</font> 管道调用 user-land
<font face="Courier New">call_usermodehelper_keys</font>使用会话密钥调用 user-land

让我们先浏览一遍这些核心函数,然后探索简化函数提供了哪些功能。核心 API 使用了一个称为 <font face="Courier New">subprocess_info</font> 结构的处理函数引用进行操作。该结构(可在 ./kernel/kmod.c 中找到)集合了给定的 usermode-helper 实例的所有必需元素。该结构引用从 <font face="Courier New">call_usermodehelper_setup</font> 调用返回。该结构(以及后续调用)将会在 <font face="Courier New">call_usermodehelper_setkeys</font>(用于存储凭证)、<font face="Courier New">call_usermodehelper_setcleanup</font> 以及 <font face="Courier New">call_usermodehelper_stdinpipe</font> 的调用中进一步配置。最后,一旦配置完成,就可通过调用 <font face="Courier New">call_usermodehelper_exec</font> 来调用配置好的用户模式应用程序。

声明

该方法提供了一个从内核调用用户空间应用程序必需的函数。尽管这项功能有合理用途,还应仔细考虑是否需要其他实现。这是一个方法,但其他方法会更合适。

核心函数提供了最大程度的控制,其中 helper 函数在单个调用中完成了大部分工作。管道相关调用(<font face="Courier New">call_usermodehelper_stdinpipe</font> 和 helper 函数 <font face="Courier New">call_usermodehelper_pipe</font>)创建了一个相联管道供 helper 使用。具体地说,创建了管道(内核中的文件结构)。用户空间应用程序对管道可读,内核对管道可写。对于本文,核心转储只是使用 usermode-helper 管道的应用程序。在该应用程序(./fs/exec.c <font face="Courier New">do_coredump()</font>)中,核心转储通过管道从内核空间写到用户空间。

这些函数与 <font face="Courier New">sub_processinfo</font> 以及 <font face="Courier New">subprocess_info</font> 结构的细节之间的关系如图 1 所示。


图 1. Usermode-helper API 关系
从Linux内核调用用户空间应用程序

表 2 中的简化函数内部执行 <font face="Courier New">call_usermodehelper_setup</font> 函数和 <font face="Courier New">call_usermodehelper_exec</font> 函数。表 2 中最后两个调用分别调用的是 <font face="Courier New">call_usermodehelper_setkeys</font><font face="Courier New">call_usermodehelper_stdinpipe</font>。可以在 ./kernel/kmod.c 找到 <font face="Courier New">call_usermodehelper_pipe</font><font face="Courier New">call_usermodehelper</font> 的代码,在 ./include/linux/kmod.h 中找到 <font face="Courier New">call_usermodhelper_keys</font> 的代码。

相关推荐