使用call_usermodehelper在Linux核心中直接運行使用者空間程式)

來源:互聯網
上載者:User
系統初始化時kernel_init在核心態建立和運行應用程式以完成系統初始化.  核心剛剛啟動時,只有核心態的代碼,後來在init過程中,在核心態運行了一些初始化系統的程式,才產生了工作在使用者空間的進程。


/* This is a non __init function. Force it to be noinline otherwise gcc
736 * makes it inline to init() and it becomes part of init.text section
737 */
738static noinline int init_post(void)
739{
740 /* need to finish all async __init code before freeing the memory */
741 async_synchronize_full();
742 free_initmem();
743 mark_rodata_ro();
744 system_state = SYSTEM_RUNNING;
745 numa_default_policy();
746
747
748 current->signal->flags |= SIGNAL_UNKILLABLE;
749
750 if (ramdisk_execute_command) {
751 run_init_process(ramdisk_execute_command);
752 printk(KERN_WARNING "Failed to execute %s\n",
753 ramdisk_execute_command);
754 }
755
756 /*
757 * We try each of these until one succeeds.
758 *
759 * The Bourne shell can be used instead of init if we are
760 * trying to recover a really broken machine.

從核心裡發起系統調用,執行使用者空間的應用程式。這些程式自動以root許可權運行。

 761         */
762 if (execute_command) {
763 run_init_process(execute_command);
764 printk(KERN_WARNING "Failed to execute %s. Attempting "
765 "defaults...\n", execute_command);
766 }
767 run_init_process("/sbin/init");
768 run_init_process("/etc/init");
769 run_init_process("/bin/init");
770 run_init_process("/bin/sh");
771
772 panic("No init found. Try passing init= option to kernel. "
773 "See Linux Documentation/init.txt for guidance.");
774}

        這裡,核心以此運行使用者空間程式,從而產生了第一個以及後續的使用者空間程式。一般使用者空間的init程式,會啟動一個shell,供使用者登入系統用。這樣,這裡啟動的使用者空間的程式永遠不會返回。也就是說,正常情況下不會到panic這一步。系統執行到這裡後,Linux Kernel的初始化就完成了。

此時,中斷和中斷驅動的進程調度機制,調度著各個線程在各個CPU上的運行。中斷處理常式不時被觸發。作業系統上,一些核心線程在核心態運行,它們永遠不會進入使用者態。它們也根本沒有使用者態的記憶體空間。它的線性地址空間就是共用核心的線性地址空間。一些使用者進程通常在使用者態運行。有時因為系統調用而進入核心態,調用核心提供的系統調用處理函數。

       但有時,我們的核心模組或者核心線程希望能夠調用使用者空間的進程,就像系統啟動之初init_post函數做的那樣。

       如,一個驅動從核心得到了主從裝置號,然後需要使用mknod命令建立相應的裝置檔案,以供使用者調用該裝置。

       如,一個核心線程想神不知鬼不覺地偷偷運行個有特權的後門程式。等等之類的需求。

call_usermodehelper函數

          Linux  Kernel提供了call_usermodehelper函數,讓我們能夠異常方便地在核心中直接建立和運行使用者空間程式,並且該程式具有root許可權。

call_usermodehelper函數源碼include/linux/kmod.h標頭檔
 105static inline int
106call_usermodehelper(char *path, char **argv, char **envp, enum umh_wait wait)
107{
108 return call_usermodehelper_fns(path, argv, envp, wait,
109 NULL, NULL, NULL);
110}
111


50enum umh_wait {
51 UMH_NO_WAIT = -1, /* don't wait at all */
52 UMH_WAIT_EXEC = 0, /* wait for the exec, but not the process */
53 UMH_WAIT_PROC = 1, /* wait for the process to complete */
54};
55
56struct subprocess_info {
57 struct work_struct work;
58 struct completion *complete;
59 char *path;
60 char **argv;
61 char **envp;
62 enum umh_wait wait;
63 int retval;
64 int (*init)(struct subprocess_info *info);
65 void (*cleanup)(struct subprocess_info *info);
66 void *data;
67};
68
kernel/kmod.c實現檔案
377/**
378 * call_usermodehelper_exec - start a usermode application
379 * @sub_info: information about the subprocessa 子進程的資訊
380 * @wait: wait for the application to finish and return status.等待使用者空間子進程的完成,並返回結果。
381 * when -1 don't wait at all, but you get no useful error back when
382 * the program couldn't be exec'ed. This makes it safe to call
383 * from interrupt context.


-1表示根本不等待子進程的結束。 但這樣你就無法對程式出錯進行處理。
如果使用中斷上下文,那麼應該使用-1。

 384 *
385 * Runs a user-space application. The application is started
386 * asynchronously if wait is not set, and runs as a child of keventd.
387 * (ie. it runs with full root capabilities).


call_usermodehelper_exec函數,啟動一個使用者模式應用程式。
如果不設定wait,那麼使用者空間應用程式會被非同步啟動。  它在root許可權下運行。是keventd進程的子進程。

 388 */
389int call_usermodehelper_exec(struct subprocess_info *sub_info,
390 enum umh_wait wait)
391{
392 DECLARE_COMPLETION_ONSTACK(done);
393 int retval = 0;
394
395 helper_lock();
396 if (sub_info->path[0] == '\0')
397 goto out;
398
399 if (!khelper_wq || usermodehelper_disabled) {
400 retval = -EBUSY;
401 goto out;
402 }
403
404 sub_info->complete = &done;
405 sub_info->wait = wait;
406把使用者空間進程掛到一個核心工作隊列。
407 queue_work(khelper_wq, &sub_info->work);
408 if (wait == UMH_NO_WAIT) /* task has freed sub_info */
409 goto unlock;


如果等待子進程完成,那麼執行等待完成的  事件通知和喚醒。就是說當前進程sleep。
 

410        wait_for_completion(&done);
411 retval = sub_info->retval;
412
413out:
414 call_usermodehelper_freeinfo(sub_info);
415unlock:
416 helper_unlock();
417 return retval;
418}
419EXPORT_SYMBOL(call_usermodehelper_exec);
420
421void __init usermodehelper_init(void)
422{
423 khelper_wq = create_singlethread_workqueue("khelper");
424 BUG_ON(!khelper_wq);
425}

 

     call_usermodeheler函數建立的新程式,實際上作為keventd核心線程的子進程運行,因此具有root許可權。  新程式被扔到核心工作隊列“khelper”中進行執行。

      如果使用UMH_NO_WAIT,那麼因為沒有在事件隊列上等待和喚醒的過程,因此可以在中斷上下文中使用。 它的傳回值是新程式的傳回值。

 

call_usermodeheler函數的參數用法和execve函數一致 

#include<unistd.h>

intexecve(const char *filename, char *const argv[],
char*const
 envp[]);

execve函數使用sys_execve系統調用,建立並運行一個程式。

argv是字串數組,是將被傳輸到新程式的參數。

envp是字串數組,格式是key=value,是傳遞給新程式的環境變數。

argv和envp都必須以NULL字串結束。以此來實現對字串數組的大小統計。 

     這就意味著,argv的第一個參數也必須是程式名。也就是說,新程式名要在execve函數的參數中傳遞兩次。

 

     這和main函數傳入的參數格式也是一致的。


#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
//#include<linux/config.h>

#include <linux/kernel.h>/*printk()*/
#include <linux/sched.h>

MODULE_LICENSE("GPL");


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

result = call_usermodehelper(cmd_path, cmd_argv, cmd_envp, UMH_WAIT_PROC);
printk(KERN_DEBUG "test driver init exec! there result of call_usermodehelper is %d\n", result);
printk(KERN_DEBUG "test driver init exec! the process is \"%s\", pid is %d.\n",current->comm, current->pid);
return result;
}


static __exit void test_driver_exit(void)
{
int result = 0;
char cmd_path[] = "/bin/rm";
char* cmd_argv[] = {cmd_path,"/touchX.txt",NULL};
char* cmd_envp[] = {"HOME=/", "PATH=/sbin:/bin:/usr/bin", NULL};

result = call_usermodehelper(cmd_path, cmd_argv, cmd_envp, UMH_WAIT_PROC);
printk(KERN_DEBUG "test driver exit exec! the result of call_usermodehelper is %d\n", result);
printk(KERN_DEBUG "test driver exit exec! the process is \"%s\",pidis %d \n", current->comm, current->pid);
}

module_init(test_driver_init);
module_exit(test_driver_exit);


相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.