现在,让我们看看 usermode-helper API 的简单应用。首先看一下标准 API,然后学习如何使用 helper 函数使事情更简单。
在该例中,首先开发了一个简单的调用 API 的可加载内核模块。清单 1 展示了样板模块功能,定义了模块入口和出口函数。这两个函数根据模块的 modprobe(模块入口函数)或 insmod(模块入口函数),以及 rmmod(模块出口函数)被调用。
清单 1. 模块样板函数
#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 );
usermode-helper API 的使用如 清单 2 所示,其中有详细描述。函数开始是声明所需变量和结构。以 subprocess_info 结构开始,它包含所有的执行用户空间调用的信息。该调用在调用 call_usermodehelper_setup 时初始化。下一步,定义参数列表,使 argv 被调用。该列表与普通 C 程序中的 argv 列表类似,定义了应用程序(数组第一个元素)和参数列表。需要 NULL 终止符来提示列表末尾。请注意这里的 argc 变量(参数数量)是隐式的,因为 argv 列表的长度已经知道。该例中,应用程序名是 /usr/bin/logger,参数是 help!,然后是 NULL 终止符。下一个所需变量是环境数组(envp)。该数组是一组定义用户空间应用程序执行环境的参数列表。本例中,定义一些常用的参数,这些参数用于定义 shell 并以 NULL 条目结束。
清单 2. 简单的 usermode_helper API 测试
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 ); }
下一步,调用 call_usermodehelper_setup 来创建已初始化的 subprocess_info 结构。请注意使用了先前初始化的变量以及指示用于内存初始化的 GFP 屏蔽第四个参数。在安装函数内部,调用了 kzalloc(分配内核内存并清零)。该函数需要 GFP_ATOMIC 或 GFP_KERNEL 标志(前者定义调用不可以休眠,后者定义可以休眠)。快速测试新结构(即,非 NULL)后,使用 call_usermodehelper_exec 函数继续调用。该函数使用 subprocess_info 结构以及定义是否等待的枚举变量(在 “Usermode-helper API 内部” 一节中有描述)。全部完成! 模块一旦加载,就可以在 /var/log/messages 文件中看到信息。
还可以通过 call_usermodehelper API 函数进一步简化进程,它同时执行 call_usermodehelper_setup 和 call_usermodehelper_exec 函数。如清单 3 所示,它不仅删除函数,还消除了调用者管理 subprocess_info 结构的必要性。
清单 3. 更简单的 usermode-helper API 测试
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 ); }
请注意在清单 3 中,有着同样的安装并调用(例如初始化 argv 和 envp 数组)的需求。此处惟一的区别是 helper 函数执行 setup 和 exec 函数。