call_usermodehelper内核中运行用户应用程序

init是用户空间第一个程序,在调用init前程序都运行在内核态,之后运行init时程序运行到用户态。

操作系统上,一些内核线程在内核态运行,它们永远不会进入用户态。它们也根本没有用户态的内存空间。它的线性地址空间就是共享内核的线性地址空间。一些用户进程通常在用户态运行。有时因为系统调用而进入内核态,调用内核提供的系统调用处理函数。

但有时,我们的内核模块或者内核线程希望能够调用用户空间的进程,就像系统启动之初init_post函数做的那样。

如,一个驱动从内核得到了主从设备号,然后需要使用mknod命令创建相应的设备文件,以供用户调用该设备。

如,一个内核线程想神不知鬼不觉地偷偷运行个有特权的后门程序。

等等之类的需求。

linux kernel提供了call_usermodehelper,用于内核中直接新建和运行用户空间程序,并且该程序具有root权限。

函数原型

call_usermodehelper(char *path, char **argv, char **envp, enum umh_wait wait);
enum umh_wait {
    UMH_NO_WAIT = -1,       /* don‘t wait at all */
    UMH_WAIT_EXEC = 0,      /* wait for the exec, but not the process */
    UMH_WAIT_PROC = 1,      /* wait for the process to complete */
 };

默认为UMH_WAIT_EXEC,内核exec用户空间进程后就退出;UMH_WAIT_PROC会一直等到用户空间进程结束为止。

call_usermodehelper函数的参数用法和execve函数一致,

argv是字符串数组,是将被传输到新程序的参数。

envp是字符串数组,格式是key=value,是传递给新程序的环境变量。

argv和envp都必须以NULL字符串结束。以此来实现对字符串数组的大小统计。

这就意味着,argv的第一个参数也必须是程序名。也就是说,新程序名要在execve函数的参数中传递两次。

函数原理

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"的部分角色,成为一个新亮点,悄悄地登上了历史舞台。

使用示例

驱动中实现调用。

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/moduleparam.h>
    #include <linux/kernel.h>
    #include <linux/sched.h>  

    MODULE_LICENSE("DualBSD/GPL");  

    static __init int hello_init(void)
    {
            int result = 0;
            char cmd_path[] = "/usr/bin/touch";
            char *cmd_argv[] = {cmd_path, "/home/yu/test.txt", NULL};
            char *cmd_envp[] = {"HOME=/", "PATH=/sbin:/bin:/user/bin", NULL};  

            result = call_usermodehelper(cmd_path, cmd_argv, cmd_envp, UMH_WAIT_PROC);
            printk(KERN_DEBUG"THe result of call_usermodehelper is %d\n", result);
            return result;
    }  

    static __exit void hello_exit(void)
    {
            int result = 0;
            char cmd_path[] = "/bin/rm";
            char *cmd_argv[] = {cmd_path, "/home/yu/test.txt", NULL};
            char *cmd_envp[] = {"HOME=/", "PATH=/sbin:/bin:/user/bin", NULL};  

            result = call_usermodehelper(cmd_path, cmd_argv, cmd_envp,
                            UMH_WAIT_PROC);
            printk(KERN_DEBUG"THe result of call_usermodehelper is %d\n", result);
    }  

    module_init(hello_init);
    module_exit(hello_exit);  
    obj-m := hello.o
    KDIR := /lib/modules/$(shell uname -r)/build
    PWD := $(shell pwd)
    default:
            $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
    clean:
            rm -rf *.o *.ko *.mod* *.order *.sym*  

参考:

1. 使用call_usermodehelper在Linux内核中直接运行用户空间程序

2. Linux call_usermodehelper()

3. Invoking user-space applications from the kernel(IBM)

时间: 2024-10-03 14:25:07

call_usermodehelper内核中运行用户应用程序的相关文章

Win7下Eclipse中运行远程MapReduce程序

1.hadoop插件的参数配置 2.运行时的参数 3.运行结果 Win7下Eclipse中运行远程MapReduce程序,布布扣,bubuko.com

在Linux中运行Nancy应用程序

最近在研究如何将.NET应用程序移植到非Windows操作系统中运行,逐渐会写一些文章出来.目前还没有太深的研究,所以这些文章大多主要是记录我的一些实验. 这篇文章记录了我如何利用NancyFx编写一个自托管(Self-host)的应用程序,并且将其发布到Linux系统中. 什么是NancyFx? 简单地说,这真是一个神奇的框架.它给自己的定义是:lightweigh web framework for .NET.不用不知道,一用吓一跳哈 http://nancyfx.org/ 与微软官方的AS

Genymotion中运行cocos2d-x的程序

在Genymotion中运行cocos2d-x程序,修改的地方如下: 1.修改Application.mk文件,增加如下一行 APP_ABI := armeabi armeabi-v7a x86 2.修改Cocos2dxActivity.java文件,修改isAndroidEmulator函数如下: 1 private final static boolean isAndroidEmulator() { 2 String model = Build.MODEL; 3 Log.d(TAG, "mo

vc++ 在程序中运行另一个程序的方法

在vc++ 程序中运行另一个程序的方法有三个: WinExec(),ShellExcute()和CreateProcess() 三个SDK函数: WinExec,ShellExecute ,CreateProcess可以实现调用其他程序的要求,其中以WinExec最为简单,ShellExecute比WinExec灵活一些,CreateProcess最为复杂.    WinExec 两个参数,前一个指定路径,后一个指定显示方式.    ShellExecute 可以指定工作目录,并且还可以寻找文件

树莓派3中运行Netcore2.0程序

一.简介 Netcore2.0发部后,可以运行在Arm平台上.因此,我们可以尝试在装了Debain的树莓派中运行. 二.方法: 1.在自己的电脑上使用VS写一个NetCore2.0的控制台程序,我假设我就写个Helloworld. 2.在项目目录下使用cmd命令执行:  dotnet publish -r linux-arm 说明:-r表示运行平台,可以是win-arm.linux-arm.win-x86.win-x64等等. 3.打开项目所在目录下的bin\Debug\netcoreapp2.

如何在android中运行C语言程序

问题阐述: 本人使用mini6410开发了一个sqlite数据库的程序,在mini6410的linux系统下已经能够成功运行了.因为Android使用的也是linux内核,所以我想当然的认为按照同样的方法将程序移植到mini6410的android系统中也可以成功运行,但是当我运行程序的时候却提示我不能找到可执行文件(xlisten-arm是交叉编译出来的可执行文件): / # ./xlisten-arm/system/bin/sh: ./xlisten-arm: not found 1.探索:

QT5中运行QT4场景程序 QGraphicsItem *QGraphicsScene::itemAt 函数报错的解决

int main(int argc,char* argv[ ]) { QApplication app(argc,argv); //新建场景 QGraphicsScene scene; //创建矩形图形项 QTransform transform; //QT5添加 transform.rotate(+0.0);//QT5添加 QGraphicsRectItem *item = new QGraphicsRectItem(0, 0, 100, 100); //将图形项添加到场景中 scene.ad

在android项目中运行普通的java程序出错!

http://www.cnblogs.com/wangmars/p/3255044.html # # A fatal error has been detected by the Java Runtime Environment: # #  Internal Error (javaClasses.cpp:136), pid=1996, tid=11456 #  fatal error: Invalid layout of preloaded class # # JRE version:  (7.

在docker中运行ASP.NET Core Web API应用程序

本文是一篇指导快速演练的文章,将介绍在docker中运行一个ASP.NET Core Web API应用程序的基本步骤,在介绍的过程中,也会对docker的使用进行一些简单的描述.对于.NET Core以及docker的基本概念,网上已经有很多文章对其进行介绍了,因此本文不会再详细讲解这些内容.对.NET Core和docker不了解的朋友,建议首先查阅与这些技术相关的文档,然后再阅读本文. 先决条件 要完成本文所介绍的演练任务,需要准备以下环境: Visual Studio 2015,或者Vi