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 = -, /* 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内核中运行用户应用程序的更多相关文章
- 在Linux中运行Nancy应用程序
最近在研究如何将.NET应用程序移植到非Windows操作系统中运行,逐渐会写一些文章出来.目前还没有太深的研究,所以这些文章大多主要是记录我的一些实验. 这篇文章记录了我如何利用NancyFx编写一 ...
- vc++ 在程序中运行另一个程序的方法
在vc++ 程序中运行另一个程序的方法有三个: WinExec(),ShellExcute()和CreateProcess() 三个SDK函数: WinExec,ShellExecute ,Creat ...
- 树莓派3中运行Netcore2.0程序
一.简介 Netcore2.0发部后,可以运行在Arm平台上.因此,我们可以尝试在装了Debain的树莓派中运行. 二.方法: 1.在自己的电脑上使用VS写一个NetCore2.0的控制台程序,我假设 ...
- Windbg 内核态调试用户态程序然后下断点正确触发方法(亲自实现发现有效)
先开启真机内核态kernel调试 !process 0 0 svchost.exe 找到进程cid的地址 然后进入 .process /p fffffa8032be2870 然后 .process ...
- PyCharm中运行同一个python程序时选择平行窗口运行
问题描述 当我们进行Socket编程时,客户端可能有多个,原则上如果有n个客户端,那么我们就要编辑n客户端的代码.然而其实我们每个客户端的代码都是相同,如果编辑n遍,将会相当的浪费空间. 解决办法 学 ...
- 在docker中运行ASP.NET Core Web API应用程序
本文是一篇指导快速演练的文章,将介绍在docker中运行一个ASP.NET Core Web API应用程序的基本步骤,在介绍的过程中,也会对docker的使用进行一些简单的描述.对于.NET Cor ...
- 在 Docker 容器中运行应用程序
案例说明 运行 3 个容器,实现对网站的监控. 三个容器的说明: 容器 web: 创建自 nginx 映像,使用 80 端口,运行于后台,实现 web 服务. 容器 mailer: 该容器中运行一个 ...
- 内核创建的用户进程printf不能输出一问的研究
转:http://www.360doc.com/content/09/0315/10/26398_2812414.shtml 一:前言上个星期同事无意间说起,在用核中创建的用户空间进程中,使用prin ...
- linux内核空间与用户空间信息交互方法
linux内核空间与用户空间信息交互方法 本文作者: 康华:计算机硕士,主要从事Linux操作系统内核.Linux技术标准.计算机安全.软件测试等领域的研究与开发工作,现就职于信息产业部软件与 ...
随机推荐
- vue - 子路由-路由嵌套
描述:子路由,也叫路由嵌套,采用在children后跟路由数组来实现,数组里和其他配置路由基本相同,需要配置path和component,然后在相应部分添加<router-view/>来展 ...
- 使用JNDI连接数据库
第一步:实现一个Java类: package com.logistic.data; import java.sql.*;import javax.sql.*;import javax.naming.* ...
- Map 和 WeakMap 数据结构
Map 和 WeakMap 是ES6 新增的数据结构 一.Map 它们本质与对象一样,都是键值对的集合,但是他们与 Object 对象主要的不同是,键可以是各种类型的数值,而Object 对象的键 只 ...
- sql通过某个字段名找到数据库中对应的表
sql通过某个字段名找到数据库中对应的表 SELECT sb.name FROM syscolumns s JOIN sysobjects sb ON s.id=sb.id WHERE s.name= ...
- JSP 九大隐式对象
final javax.servlet.jsp.PageContext pageContext; javax.servlet.http.HttpSession session = null; fina ...
- Python-try except else finally有return时执行顺序探究
学习python或者其他有异常控制的编程语 言, 大家很有可能说try except finally(try catch finally)的执行很简单,无非就是有异常的话执行except, final ...
- Linux下TCP/IP内核参数优化
/proc/sys/net目录 所有的TCP/IP参数都位于/proc/sys/net目录下(请注意,对/proc/sys/net目录下内容的修改都是临时的,任何修改在系统重启后都会丢失),例如下面这 ...
- 安卓编译报错:Dex Loader] Unable to execute dex: java.nio.BufferOverflowException. Check the Eclipse log for stack trace.
今天在编译别人的project时遇到了这个错误,心想应该不是代码的问题.网上搜了一下,应该是sdk版本设置的问题,要设置为最新sdk版本才可以. 解决办法就是修改project.properties里 ...
- python 中 numpy array 中的维度
简介 numpy 创建的数组都有一个shape属性,它是一个元组,返回各个维度的维数.有时候我们可能需要知道某一维的特定维数. 二维情况 >>> import numpy as np ...
- OSI各层的功能和主要协议(转载)
OSI各层的功能和主要协议: 物理层 物理层规定了激活.维持.关闭通信端点之间的机械特性.电气特性.功能特性以及过程特性.该层为上层协议提供了一个传输数据的物理媒体. 在这一层,数据的单位称为比特(b ...