During system initialization, kernel_init creates and runs applications in the kernel state to complete system initialization. when the kernel was just started, only the kernel-state code was available. Later, some initialization system programs were run in the kernel-State during the INIT process to generate processes that work in the user space.
/* 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.
Initiate a system call from the kernel to execute user space applications. These programs run automatically with the root permission.
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}
Here, the kernel runs the user space program to generate the first and subsequent user space programs. Generally, the INIT program of the user space starts a shell for the user to log on to the system. In this way, the user space program started here will never return. That is to say, under normal circumstances, it will not be in the panic step. After the system runs this command, the Linux kernel Initialization is complete.
In this case, the process scheduling mechanism of interrupt and interrupt drivers schedules the running of each thread on each CPU. The interrupt handler is triggered from time to time. On the operating system, some kernel threads run in the kernel state and they will never enter the user State. They also have no user-mode memory space. Its linear address space is the linear address space of the shared kernel. Some user processes are usually run in user mode. Sometimes the system calls the kernel and calls the system call processing function provided by the kernel.
But sometimes, our kernel module or kernel thread wants to be able to call user space processes, just as the init_post function did at the beginning of the system startup.
For example, a driver obtains the Master/Slave Device number from the kernel, and then needs to use the mknod command to create a device file for the user to call the device.
For example, a kernel thread wants to secretly run a privileged backdoor program. And so on.
Call_usermodehelper Function
Linux Kernel provides the call_usermodehelper function, which allows us to directly create and run a user space program in the kernel with exception and convenience, and the program has the root permission.
Call_usermodehelper function source code include/Linux/kmod. h header file
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 implementation file
377 /**
378 * call_usermodehelper_exec-start a usermode Application
379 * @ sub_info: information about the subprocessa sub-process information
380 * @ wait: Wait for the application to finish and return status. Wait for the completion of the sub-process in the user space and return the result.
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 indicates that the child process is not waiting for completion. However, you cannot handle program errors.
-1 should be used if the interrupt context is used.
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 function to start a user-mode application.
If wait is not set, the user space application is started asynchronously. It runs under the root permission. Is a child process of the keventd process.
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 maid ();
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 mount a user space process to a kernel working queue.
407 queue_work (khelper_wq, & sub_info-> work );
408 if (wait = umh_no_wait)/* task has freed sub_info */
409 goto unlock;
If you wait for the sub-process to complete, execute the Event Notification and wake up. That is to say, the current process 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}
The new program created by the call_usermodeheler function is actually run as a sub-process of the keventd kernel thread, so it has the root permission. The new program is thrown into the kernel working queue "khelper" for execution.
If umh_no_wait is used, it can be used in the interrupt context because there is no process of waiting and awakening in the event queue. The return value is the return value of the new program.
The parameter usage of the call_usermodeheler function is consistent with that of the execve function.
# Include <Unistd. h>
Intexecve (const char *Filename, Char * const Argv[],
Char * const Envp[]);
ExecveFunction usageSys_execveSystem Call to create and run a program.
Argv is a string array and a parameter that will be transmitted to the new program.
Envp is a string array in the format of key = value. It is the environment variable passed to the new program.
Both argv and envp must end with a null string. To measure the size of the string array.
This means that,ArgvThe first parameter must also be the program name. That is to say, the new program name must beExecveThe function parameters are passed twice.
This is also consistent with the parameter format passed in by the main function.
#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);