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 = -, /* don't wait at all */
UMH_WAIT_EXEC = , /* wait for the exec, but not the process */
UMH_WAIT_PROC = , /* 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 = ;
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 = ;
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)

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

  1. 在Linux中运行Nancy应用程序

    最近在研究如何将.NET应用程序移植到非Windows操作系统中运行,逐渐会写一些文章出来.目前还没有太深的研究,所以这些文章大多主要是记录我的一些实验. 这篇文章记录了我如何利用NancyFx编写一 ...

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

    在vc++ 程序中运行另一个程序的方法有三个: WinExec(),ShellExcute()和CreateProcess() 三个SDK函数: WinExec,ShellExecute ,Creat ...

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

    一.简介 Netcore2.0发部后,可以运行在Arm平台上.因此,我们可以尝试在装了Debain的树莓派中运行. 二.方法: 1.在自己的电脑上使用VS写一个NetCore2.0的控制台程序,我假设 ...

  4. Windbg 内核态调试用户态程序然后下断点正确触发方法(亲自实现发现有效)

    先开启真机内核态kernel调试 !process 0 0 svchost.exe 找到进程cid的地址 然后进入 .process /p  fffffa8032be2870 然后 .process ...

  5. PyCharm中运行同一个python程序时选择平行窗口运行

    问题描述 当我们进行Socket编程时,客户端可能有多个,原则上如果有n个客户端,那么我们就要编辑n客户端的代码.然而其实我们每个客户端的代码都是相同,如果编辑n遍,将会相当的浪费空间. 解决办法 学 ...

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

    本文是一篇指导快速演练的文章,将介绍在docker中运行一个ASP.NET Core Web API应用程序的基本步骤,在介绍的过程中,也会对docker的使用进行一些简单的描述.对于.NET Cor ...

  7. 在 Docker 容器中运行应用程序

    案例说明 运行 3 个容器,实现对网站的监控. 三个容器的说明: 容器 web: 创建自 nginx 映像,使用 80 端口,运行于后台,实现 web 服务. 容器 mailer: 该容器中运行一个 ...

  8. 内核创建的用户进程printf不能输出一问的研究

    转:http://www.360doc.com/content/09/0315/10/26398_2812414.shtml 一:前言上个星期同事无意间说起,在用核中创建的用户空间进程中,使用prin ...

  9. linux内核空间与用户空间信息交互方法

    linux内核空间与用户空间信息交互方法     本文作者: 康华:计算机硕士,主要从事Linux操作系统内核.Linux技术标准.计算机安全.软件测试等领域的研究与开发工作,现就职于信息产业部软件与 ...

随机推荐

  1. vue - 子路由-路由嵌套

    描述:子路由,也叫路由嵌套,采用在children后跟路由数组来实现,数组里和其他配置路由基本相同,需要配置path和component,然后在相应部分添加<router-view/>来展 ...

  2. 使用JNDI连接数据库

    第一步:实现一个Java类: package com.logistic.data; import java.sql.*;import javax.sql.*;import javax.naming.* ...

  3. Map 和 WeakMap 数据结构

    Map 和 WeakMap 是ES6 新增的数据结构 一.Map 它们本质与对象一样,都是键值对的集合,但是他们与 Object 对象主要的不同是,键可以是各种类型的数值,而Object 对象的键 只 ...

  4. sql通过某个字段名找到数据库中对应的表

    sql通过某个字段名找到数据库中对应的表 SELECT sb.name FROM syscolumns s JOIN sysobjects sb ON s.id=sb.id WHERE s.name= ...

  5. JSP 九大隐式对象

    final javax.servlet.jsp.PageContext pageContext; javax.servlet.http.HttpSession session = null; fina ...

  6. Python-try except else finally有return时执行顺序探究

    学习python或者其他有异常控制的编程语 言, 大家很有可能说try except finally(try catch finally)的执行很简单,无非就是有异常的话执行except, final ...

  7. Linux下TCP/IP内核参数优化

    /proc/sys/net目录 所有的TCP/IP参数都位于/proc/sys/net目录下(请注意,对/proc/sys/net目录下内容的修改都是临时的,任何修改在系统重启后都会丢失),例如下面这 ...

  8. 安卓编译报错:Dex Loader] Unable to execute dex: java.nio.BufferOverflowException. Check the Eclipse log for stack trace.

    今天在编译别人的project时遇到了这个错误,心想应该不是代码的问题.网上搜了一下,应该是sdk版本设置的问题,要设置为最新sdk版本才可以. 解决办法就是修改project.properties里 ...

  9. python 中 numpy array 中的维度

    简介 numpy 创建的数组都有一个shape属性,它是一个元组,返回各个维度的维数.有时候我们可能需要知道某一维的特定维数. 二维情况 >>> import numpy as np ...

  10. OSI各层的功能和主要协议(转载)

    OSI各层的功能和主要协议: 物理层 物理层规定了激活.维持.关闭通信端点之间的机械特性.电气特性.功能特性以及过程特性.该层为上层协议提供了一个传输数据的物理媒体. 在这一层,数据的单位称为比特(b ...