从内核模块执行/调用用户空间程序,并获取其pid

我检查了内核API,第1部分:从内核调用用户空间应用程序 ,并从内核空间执行用户空间函数 – 堆栈溢出 – 这里是一个小的内核模块callmodule.c ,certificate:

 // http://people.ee.ethz.ch/~arkeller/linux/code/usermodehelper.c #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/proc_fs.h> #include <asm/uaccess.h> static int __init callmodule_init(void) { int ret = 0; char userprog[] = "/path/to/mytest"; char *argv[] = {userprog, "2", NULL }; char *envp[] = {"HOME=/", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; printk("callmodule: init %s\n", userprog); /* last parameter: 1 -> wait until execution has finished, 0 go ahead without waiting*/ /* returns 0 if usermode process was started successfully, errorvalue otherwise*/ /* no possiblity to get return value of usermode process*/ ret = call_usermodehelper(userprog, argv, envp, UMH_WAIT_EXEC); if (ret != 0) printk("error in call to usermodehelper: %i\n", ret); else printk("everything all right\n"); return 0; } static void __exit callmodule_exit(void) { printk("callmodule: exit\n"); } module_init(callmodule_init); module_exit(callmodule_exit); MODULE_LICENSE("GPL"); 

…使用Makefile

 obj-m += callmodule.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 

当我通过sudo insmod ./callmodule.ko && sudo rmmod callmodule运行这个时,我进入/var/log/syslog

 Feb 10 00:42:45 mypc kernel: [71455.260355] callmodule: init /path/to/mytest Feb 10 00:42:45 mypc kernel: [71455.261218] everything all right Feb 10 00:42:45 mypc kernel: [71455.286131] callmodule: exit 

这显然意味着一切顺利。 (使用Linux 2.6.38-16-generic#67-Ubuntu SMP)

我的问题是 – 我怎么能从内核模块实例化进程的PID? 除了call_usermodehelper是否有类似的进程,这将允许我在内核空间实例化一个用户空间进程,并获得它的PID?


请注意,可能无法使用call_usermodehelper并获取实例化的进程PID:

Re:call_usermodehelper的pid? – Linux内核新手

我想创build一个内核模块的用户空间进程,并能够杀死它,发送信号,等等…

我可以知道它的PID吗?

不,你不能。 但是由于在执行过程中pid是已知的,所以可用的补丁不会太难(注意,内核中的错误总是负的,并且pid是正的,限于2 ** 16)。 你将不得不修改所有期待0成功的呼叫者。

我绕了一些资源,似乎最终有一个调用链: call_usermodehelper – > call_usermodehelper_setup – > __call_usermodehelper ,它看起来像:

 static void __call_usermodehelper(struct work_struct *work) { struct subprocess_info *sub_info = container_of(work, struct subprocess_info, work); // ... if (wait == UMH_WAIT_PROC) pid = kernel_thread(wait_for_helper, sub_info, CLONE_FS | CLONE_FILES | SIGCHLD); else pid = kernel_thread(____call_usermodehelper, sub_info, CLONE_VFORK | SIGCHLD); ... 

…所以使用了一个内核线程的PID,但是它没有保存; 另外, work_structsubprocess_info都没有一个pid字段( task_struct ,但是这里没有任何东西似乎使用task_struct )。 logging这个pid需要改变内核的来源 – 我想避免这种情况,这就是为什么我也对call_usermodehelper以外的方法感兴趣的原因…

Solutions Collecting From Web of "从内核模块执行/调用用户空间程序,并获取其pid"

从我对kmod.c中的实现的理解中获得一个试探性答案。

如果您查看call_usermodehelper的代码,您将看到它调用call_usermodehelper_setup ,然后调用call_usermodehelper_exec

call_usermodehelper_setup需要一个init函数作为参数,它将在do_execve之前do_execve 。 我相信当init函数被执行时, current的值将得到用户进程的task_struct

所以要获得pid,你需要:

  1. 在代码中复制call_usermodehelper的实现。
  2. 定义一个init函数,您将作为参数传递给call_usermodehelper_setup
  3. 在init函数中检索当前的task_struct ,然后是PID。

那么,这是很乏味的…下面是一个相当不好的方法,至少在我的平台上做到这一点,作为callmodule.c (同样的Makefile可以使用)。 因为我不能相信是应该这样做的方式,更正确的答案仍然是受欢迎的(希望,也有我可以测试的代码示例)。 但是至少它只是作为一个内核模块来完成这个工作,而不需要为2.6.38版本修补内核本身,这对我来说非常重要。

基本上,我复制了所有功能(用“B”后缀重新命名),直到PID可用的点。 然后我使用一个带有额外字段的subprocess_info副本来保存它(尽管这不是必须的:为了不使用返回值的函数签名,我必须把pid保存为一个全局变量;作为练习)。 现在,当我运行sudo insmod ./callmodule.ko && sudo rmmod callmodule ,在/var/log/syslog我得到:

 Feb 10 18:53:02 mypc kernel: [ 2942.891886] callmodule: > init /path/to/mytest Feb 10 18:53:02 mypc kernel: [ 2942.891912] callmodule: symbol @ 0xc1065b60 is wait_for_helper+0x0/0xb0 Feb 10 18:53:02 mypc kernel: [ 2942.891923] callmodule: symbol @ 0xc1065ed0 is ____call_usermodehelper+0x0/0x90 Feb 10 18:53:02 mypc kernel: [ 2942.891932] callmodule:a: pid 0 Feb 10 18:53:02 mypc kernel: [ 2942.891937] callmodule:b: pid 0 Feb 10 18:53:02 mypc kernel: [ 2942.893491] callmodule: : pid 23306 Feb 10 18:53:02 mypc kernel: [ 2942.894474] callmodule:c: pid 23306 Feb 10 18:53:02 mypc kernel: [ 2942.894483] callmodule: everything all right; pid 23306 Feb 10 18:53:02 mypc kernel: [ 2942.894494] callmodule: pid task a: ec401940 c: mytest p: [23306] s: runnable Feb 10 18:53:02 mypc kernel: [ 2942.894502] callmodule: parent task a: f40aa5e0 c: kworker/u:1 p: [14] s: stopped Feb 10 18:53:02 mypc kernel: [ 2942.894510] callmodule: - mytest [23306] Feb 10 18:53:02 mypc kernel: [ 2942.918500] callmodule: < exit 

其中一个令人讨厌的问题是,一旦你开始复制函数,在某个特定的时间你会发现一个使用了内核函数的地方,这个函数没有被导出,比如在这个例子中是wait_for_helper 。 我所做的基本上是查看/proc/kallsyms (记住sudo !)以获得绝对地址,例如wait_for_helper ,然后将内核模块中的那些硬编码为函数指针 – 似乎工作。 另一个问题是内核源代码中的函数引用了enum umh_wait ,它不能用作模块的参数(那些需要简单转换为使用int )。

因此,模块启动用户空间进程,获得PID( 注意“ 内核调用PID实际上是内核级线程ID(通常称为TID)……什么被认为是POSIX意义上的”进程“的PID,另一方面,在内核中称为“线程组ID”或“TGID” ),获取相应的task_struct及其父项,并尝试列出父项的所有子项以及衍生进程本身的所有子项。 所以我可以看到, kworker/u:1通常是父级,而且除mytest以外没有其他子级,而且由于mytest非常简单(在我的情况下只是对磁盘文件进行单个写入),所以它不产生任何线程自己的,所以也没有孩子。

我遇到了一些需要重新启动的Oopses – 我认为他们现在已经解决了,但为了以防万一,请注意emptor。

这里是callmodule.c代码(最后有​​一些注释/链接):

 // callmodule.c with pid, url: https://stackoverflow.com/questions/21668727/ #include <linux/module.h> #include <linux/slab.h> //kzalloc #include <linux/syscalls.h> // SIGCHLD, ... sys_wait4, ... #include <linux/kallsyms.h> // kallsyms_lookup, print_symbol // global variable - to avoid intervening too much in the return of call_usermodehelperB: static int callmodule_pid; // >>>>>>>>>>>>>>>>>>>>>> // modified kernel functions - taken from // http://lxr.missinglinkelectronics.com/linux+v2.6.38/+save=include/linux/kmod.h // http://lxr.linux.no/linux+v2.6.38/+save=kernel/kmod.c // define a modified struct (with extra pid field) here: struct subprocess_infoB { struct work_struct work; struct completion *complete; char *path; char **argv; char **envp; int wait; //enum umh_wait wait; int retval; int (*init)(struct subprocess_info *info); void (*cleanup)(struct subprocess_info *info); void *data; pid_t pid; }; // forward declare: struct subprocess_infoB *call_usermodehelper_setupB(char *path, char **argv, char **envp, gfp_t gfp_mask); static inline int call_usermodehelper_fnsB(char *path, char **argv, char **envp, int wait, //enum umh_wait wait, int (*init)(struct subprocess_info *info), void (*cleanup)(struct subprocess_info *), void *data) { struct subprocess_info *info; struct subprocess_infoB *infoB; gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL; int ret; populate_rootfs_wait(); // is in linux-headers-2.6.38-16-generic/include/linux/kmod.h infoB = call_usermodehelper_setupB(path, argv, envp, gfp_mask); printk(KBUILD_MODNAME ":a: pid %d\n", infoB->pid); info = (struct subprocess_info *) infoB; if (info == NULL) return -ENOMEM; call_usermodehelper_setfns(info, init, cleanup, data); printk(KBUILD_MODNAME ":b: pid %d\n", infoB->pid); // this must be called first, before infoB->pid is populated (by __call_usermodehelperB): ret = call_usermodehelper_exec(info, wait); // assign global pid here, so rest of the code has it: callmodule_pid = infoB->pid; printk(KBUILD_MODNAME ":c: pid %d\n", callmodule_pid); return ret; } static inline int call_usermodehelperB(char *path, char **argv, char **envp, int wait) //enum umh_wait wait) { return call_usermodehelper_fnsB(path, argv, envp, wait, NULL, NULL, NULL); } /* This is run by khelper thread */ static void __call_usermodehelperB(struct work_struct *work) { struct subprocess_infoB *sub_infoB = container_of(work, struct subprocess_infoB, work); int wait = sub_infoB->wait; // enum umh_wait wait = sub_info->wait; pid_t pid; struct subprocess_info *sub_info; // hack - declare function pointers, to use for wait_for_helper/____call_usermodehelper int (*ptrwait_for_helper)(void *data); int (*ptr____call_usermodehelper)(void *data); // assign function pointers to verbatim addresses as obtained from /proc/kallsyms ptrwait_for_helper = (void *)0xc1065b60; ptr____call_usermodehelper = (void *)0xc1065ed0; sub_info = (struct subprocess_info *)sub_infoB; /* CLONE_VFORK: wait until the usermode helper has execve'd * successfully We need the data structures to stay around * until that is done. */ if (wait == UMH_WAIT_PROC) pid = kernel_thread((*ptrwait_for_helper), sub_info, //(wait_for_helper, sub_info, CLONE_FS | CLONE_FILES | SIGCHLD); else pid = kernel_thread((*ptr____call_usermodehelper), sub_info, //(____call_usermodehelper, sub_info, CLONE_VFORK | SIGCHLD); printk(KBUILD_MODNAME ": : pid %d\n", pid); // grab and save the pid here: sub_infoB->pid = pid; switch (wait) { case UMH_NO_WAIT: call_usermodehelper_freeinfo(sub_info); break; case UMH_WAIT_PROC: if (pid > 0) break; /* FALLTHROUGH */ case UMH_WAIT_EXEC: if (pid < 0) sub_info->retval = pid; complete(sub_info->complete); } } /** * call_usermodehelper_setup - prepare to call a usermode helper */ struct subprocess_infoB *call_usermodehelper_setupB(char *path, char **argv, char **envp, gfp_t gfp_mask) { struct subprocess_infoB *sub_infoB; sub_infoB = kzalloc(sizeof(struct subprocess_infoB), gfp_mask); if (!sub_infoB) goto out; INIT_WORK(&sub_infoB->work, __call_usermodehelperB); sub_infoB->path = path; sub_infoB->argv = argv; sub_infoB->envp = envp; out: return sub_infoB; } // <<<<<<<<<<<<<<<<<<<<<< static int __init callmodule_init(void) { int ret = 0; char userprog[] = "/path/to/mytest"; char *argv[] = {userprog, "2", NULL }; char *envp[] = {"HOME=/", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; struct task_struct *p; struct task_struct *par; struct task_struct *pc; struct list_head *children_list_head; struct list_head *cchildren_list_head; char *state_str; printk(KBUILD_MODNAME ": > init %s\n", userprog); /* last parameter: 1 -> wait until execution has finished, 0 go ahead without waiting*/ /* returns 0 if usermode process was started successfully, errorvalue otherwise*/ /* no possiblity to get return value of usermode process*/ // note - only one argument allowed for print_symbol print_symbol(KBUILD_MODNAME ": symbol @ 0xc1065b60 is %s\n", 0xc1065b60); // shows wait_for_helper+0x0/0xb0 print_symbol(KBUILD_MODNAME ": symbol @ 0xc1065ed0 is %s\n", 0xc1065ed0); // shows ____call_usermodehelper+0x0/0x90 ret = call_usermodehelperB(userprog, argv, envp, UMH_WAIT_EXEC); if (ret != 0) printk(KBUILD_MODNAME ": error in call to usermodehelper: %i\n", ret); else printk(KBUILD_MODNAME ": everything all right; pid %d\n", callmodule_pid); // find the task: // note: sometimes p may end up being NULL here, causing kernel oops - // just exit prematurely in that case rcu_read_lock(); p = pid_task(find_vpid(callmodule_pid), PIDTYPE_PID); rcu_read_unlock(); if (p == NULL) { printk(KBUILD_MODNAME ": p is NULL - exiting\n"); return 0; } // p->comm should be the command/program name (as per userprog) // (out here that task is typically in runnable state) state_str = (p->state==-1)?"unrunnable":((p->state==0)?"runnable":"stopped"); printk(KBUILD_MODNAME ": pid task a: %pc: %sp: [%d] s: %s\n", p, p->comm, p->pid, state_str); // find parent task: // parent task could typically be: c: kworker/u:1 p: [14] s: stopped par = p->parent; if (par == NULL) { printk(KBUILD_MODNAME ": par is NULL - exiting\n"); return 0; } state_str = (par->state==-1)?"unrunnable":((par->state==0)?"runnable":"stopped"); printk(KBUILD_MODNAME ": parent task a: %pc: %sp: [%d] s: %s\n", par, par->comm, par->pid, state_str); // iterate through parent's (and our task's) child processes: rcu_read_lock(); // read_lock(&tasklist_lock); list_for_each(children_list_head, &par->children){ p = list_entry(children_list_head, struct task_struct, sibling); printk(KBUILD_MODNAME ": - %s [%d] \n", p->comm, p->pid); // note: trying to print "%p",p here results with oops/segfault: // printk(KBUILD_MODNAME ": - %s [%d] %p\n", p->comm, p->pid, p); if (p->pid == callmodule_pid) { list_for_each(cchildren_list_head, &p->children){ pc = list_entry(cchildren_list_head, struct task_struct, sibling); printk(KBUILD_MODNAME ": - - %s [%d] \n", pc->comm, pc->pid); } } } rcu_read_unlock(); //~ read_unlock(&tasklist_lock); return 0; } static void __exit callmodule_exit(void) { printk(KBUILD_MODNAME ": < exit\n"); } module_init(callmodule_init); module_exit(callmodule_exit); MODULE_LICENSE("GPL"); /* NOTES: // assign function pointers to verbatim addresses as obtained from /proc/kallsyms: // ( cast to void* to avoid "warning: assignment makes pointer from integer without a cast", // see also https://stackoverflow.com/questions/3941793/what-is-guaranteed-about-the-size-of-a-function-pointer ) // $ sudo grep 'wait_for_helper\|____call_usermodehelper' /proc/kallsyms // c1065b60 t wait_for_helper // c1065ed0 t ____call_usermodehelper // protos: // static int wait_for_helper(void *data) // static int ____call_usermodehelper(void *data) // see also: // http://www.linuxforu.com/2012/02/function-pointers-and-callbacks-in-c-an-odyssey/ // from include/linux/kmod.h: //~ 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 * / //~ }; // however, note: // /usr/src/linux-headers-2.6.38-16-generic/include/linux/kmod.h: // #define UMH_NO_WAIT 0 ; UMH_WAIT_EXEC 1 ; UMH_WAIT_PROC 2 ; UMH_KILLABLE 4 ! // those defines end up here, regardless of the enum definition above // (NB: 0,1,2,4 enumeration starts from kmod.h?v=3.4 on lxr.free-electrons.com !) // also, note, in "generic" include/, prototypes of call_usermodehelper(_fns) // use int wait, and not enum umh_wait wait ... // seems these cannot be used from a module, nonetheless: //~ extern int wait_for_helper(void *data); //~ extern int ____call_usermodehelper(void *data); // we probably would have to (via http://www.linuxconsulting.ro/pidwatcher/) // edit /usr/src/linux/kernel/ksyms.c and add: //EXPORT_SYMBOL(wait_for_helper); // but that is kernel re-compilation... // https://stackoverflow.com/questions/19360298/triggering-user-space-with-kernel // You should not be using PIDs to identify processes within the kernel. The process can exit and a different process re-use that PID. Instead, you should be using a pointer to the task_struct for the process (rather than storing current->pid at registration time, just store current) # reports task name from the pid (pid_task(find_get_pid(..)): http://tuxthink.blogspot.dk/2012/07/module-to-find-task-from-its-pid.html // find the task: //~ rcu_read_lock(); // uprobes uses this - but find_task_by_pid is not exported for modules: //~ p = find_task_by_pid(callmodule_pid); //~ if (p) //~ get_task_struct(p); //~ rcu_read_unlock(); // see: [http://www.gossamer-threads.com/lists/linux/kernel/1260996 find_task_by_pid() problem | Linux | coreel] // https://stackoverflow.com/questions/18408766/make-a-system-call-to-get-list-of-processes // this macro loops through *all* processes; our callmodule_pid should be listed by it //~ for_each_process(p) //~ pr_info("%s [%d]\n", p->comm, p->pid); // [https://lists.debian.org/debian-devel/2008/05/msg00034.html Re: problems for making kernel module] // note - WARNING: "tasklist_lock" ... undefined; because tasklist_lock removed in 2.6.1*: // "tasklist_lock protects the kernel internal task list. modulees have no business looking at it"; // https://stackoverflow.com/questions/13002444/list-all-threads-within-the-current-process // "all methods that loop over the task lists need to be wrapped in rcu_read_lock(); / rcu_read_unlock(); to be correct." // https://stackoverflow.com/questions/19208487/kernel-module-that-iterates-over-all-tasks-using-depth-first-tree // https://stackoverflow.com/questions/5728592/how-can-i-get-the-children-process-list-in-kernel-code // https://stackoverflow.com/questions/1446239/traversing-task-struct-children-in-linux-kernel // https://stackoverflow.com/questions/8207160/kernel-how-to-iterate-the-children-of-the-current-process // https://stackoverflow.com/questions/10262017/linux-kernel-list-list-head-init-vs-init-list-head // https://stackoverflow.com/questions/16230524/explain-list-for-each-entry-and-list-for-each-entry-safe "list_entry is just an alias for container_of" */