背景知识:
call_usermodehelper()用来让内核空间的驱动程序启用用户空间的若干应用程序,
如:/sbin/hotplug、/bin/gliethttp_hello等,它的函数原型如下:
static inline int call_usermodehelper(char *path, char **argv, char **envp, enumumh_wait wait);
path --- 用户空间所要启用的应用程序路径,如:"/sbin/hotplug",那么hotplug应用程序就会被内核加载启用
argv --- 传递给启用了的用户空间应用程序的参数argv
envp --- 传递给启用了的用户空间应用程序的环境变量envp,类似int main(int argc, char*_argv[])模式
wait --- 调用call_usermodehelper的内核程序是否等到被exec的用户空间应用程序,如:"/sbin/hotplug"退出后,才继续执行.
call_usermodehelper()执行之后会在工作队列khelper_wq中加入一个工作线程__call_usermodehelper,这个工作队列上的线程运行之后,会根据wait的类型,调用kernel_thread启用相应类型的线程wait_for_helper()或者____call_usermodehelper(),之所以调用kernel_thread生成新的线程,目的在于让并行运行实现最大化,充分利用cpu.
部分代码如下:
if (wait == UMH_WAIT_PROC || wait == UMH_NO_WAIT)
pid = kernel_thread(wait_for_helper, sub_info,
CLONE_FS | CLONE_FILES | SIGCHLD);
else
pid = kernel_thread(____call_usermodehelper, sub_info,
CLONE_VFORK | SIGCHLD);
线程wait_for_helper()或者____call_usermodehelper()最终调用kernel_execve()启动用户空间的应用程序,并把参数传给该应用程序,如:"/sbin/hotplug",由此可知call_usermodehelper()是内核驱动程序向外界应用程序程序传递内核信息的好手段,但是因为内核驱动会产生相当多的hotplug事件,所以后来就使用"/sbin/udevsend"临时代替,到了2.6.15内核之后,高效的netlink广播接口开始被采用,逐渐取代"/sbin/hotplug"和"/sbin/udevsend"的部分角色,成为一个新亮点,悄悄地登上了历史舞台
// 上面的介绍引用自 约定之地 的博客
下面开始我们的实验:
1.用户空间程序准备
#cat /usr/src/linux-2.6.23/joseph/helper //路径可以任意
#!/bin/bash echo Kernel datastructure $TROUBLED_DS is in trouble >> /var/log/messages
#chmod +x /usr/src/linux-2.6.23/joseph/helper //添加可执行属性
2. 驱动代码准备
// hello.c
#include <linux/init.h> #include <linux/module.h> #include <linux/sched.h> #include <linux/kthread.h> #include <linux/sysctl.h> MODULE_LICENSE("Dual BSD/GPL"); static void run_umode_handler(int event_id) { int i = 0; char *argv[2], *envp[4], *buffer = NULL; int value; printk("%s is starting .../n", __func__); argv[i++] = "/proc/sys/kernel/myevent_handler"; // Defined in kernel/sysctl.c // Fill in the id corresponding to the data structure in trouble if (!(buffer = kmalloc(32, GFP_KERNEL))) return; sprintf(buffer, "TROUBLED_DS=%d", event_id); //If no user mode handlers are found, return if (!argv[0]) return; argv[i] = 0; // Prepare the environment for /path/to/helper i = 0; envp[i++] = "HOME=/"; envp[i++] = "PATH=/sbin:/usr/sbin:/bin:/usr/bin"; envp[i++] = buffer; envp[i] = 0; //Excute the user mode program, /path/to/helper value = call_usermodehelper("/usr/src/linux-2.6.23/joseph/helper", 0, envp, 0); kfree(buffer); } static int hello_init(void) { printk("Hello, I am a test module/n"); //create my kernel thread run_umode_handler(9527); return 0; } static void hello_exit(void) { printk("Bye, my dear!/n Cruel world/n"); } module_init(hello_init); module_exit(hello_exit);
3. Makefile
obj-m := hello.o
4.
# make -C /usr/src/linux-2.6.23 M=`pwd` module
6. #cat /var/log/messages
...
May 8 19:37:55 joseph kernel: [43485.026220] Hello, I am a test module
May 8 19:37:55 joseph kernel: [43485.026411] run_umode_handler is starting ...
Kernel datastructure 9527 is in trouble
...