DSP

浅析request_module内核驱动直接引用用户空间程序/sbin/modprobe

2019-07-13 17:25发布

 
在soundcore_open打开/dev/dsp节点函数中会调用到下面的:
    request_module("sound-slot-%i", unit>>4);
函数,这表示,让linux系统的用户空间调用/sbin/modprobe函数加载名为sound-slot-0.ko模块
#define request_module(mod...) __request_module(true, mod)
#define request_module_nowait(mod...) __request_module(false, mod)

luther@gliethttp:~$ cat /proc/sys/kernel/modprobe
/sbin/modprobe
我们也可以向/proc/sys/kernel/modprobe添加新的modprobe应用程序路径,这里的/sbin/modprobe是内核默认路径.

/**
 * __request_module - try to load a kernel module       // 尝试加载一个ko模块[luther.gliethttp]
 * @wait: wait (or not) for the operation to complete
 * @fmt: printf style format string for the name of the module
 * @...: arguments as specified in the format string
 *
 * Load a module using the user mode module loader. The function returns
 * zero on success or a negative errno code on failure. Note that a
 * successful module load does not mean the module did not then unload
 * and exit on an error of its own. Callers must check that the service
 * they requested is now available not blindly invoke it.
 *
 * If module auto-loading support is disabled then this function
 * becomes a no-operation.
 */
int __request_module(bool wait, const char *fmt, ...)
{
    va_list args;
    char module_name[MODULE_NAME_LEN];
    unsigned int max_modprobes;
    int ret;
// char modprobe_path[KMOD_PATH_LEN] = "/sbin/modprobe";
    char *argv[] = { modprobe_path, "-q", "--", module_name, NULL };
    static char *envp[] = { "HOME=/",
                "TERM=linux",
                "PATH=/sbin:/usr/sbin:/bin:/usr/bin",
                NULL }; // 环境变量.
    static atomic_t kmod_concurrent = ATOMIC_INIT(0);
#define MAX_KMOD_CONCURRENT 50    /* Completely arbitrary value - KAO */
    static int kmod_loop_msg;

    va_start(args, fmt);
    ret = vsnprintf(module_name, MODULE_NAME_LEN, fmt, args);   // 生成module名,这里就是sound-slot-0
    va_end(args);
    if (ret >= MODULE_NAME_LEN)
        return -ENAMETOOLONG;

    /* If modprobe needs a service that is in a module, we get a recursive  // 对递归循环的一个硬性限制
     * loop.  Limit the number of running kmod threads to max_threads/2 or  // 最多同时运行max_modprobes个modprobe用户程序
     * MAX_KMOD_CONCURRENT, whichever is the smaller.  A cleaner method     // 如果超过,那么__request_module返回-ENOMEM
     * would be to run the parents of this process, counting how many times // [luther.gliethttp]
     * kmod was invoked.  That would mean accessing the internals of the
     * process tables to get the command line, proc_pid_cmdline is static
     * and it is not worth changing the proc code just to handle this case.
     * KAO.
     *
     * "trace the ppid" is simple, but will fail if someone's
     * parent exits.  I think this is as good as it gets. --RR
     */
    max_modprobes = min(max_threads/2, MAX_KMOD_CONCURRENT);    // 最多同时运行max_modprobes个modprobe用户程序
    atomic_inc(&kmod_concurrent);
    if (atomic_read(&kmod_concurrent) > max_modprobes) {
        /* We may be blaming an innocent here, but unlikely */
        if (kmod_loop_msg++ < 5)
            printk(KERN_ERR
                   "request_module: runaway loop modprobe %s ",
                   module_name);
        atomic_dec(&kmod_concurrent);                           // 消除前面atomic_inc产生的引用
        return -ENOMEM;                                         // 返回错误[luther.gliethttp]
    }

    ret = call_usermodehelper(modprobe_path, argv, envp,        // 执行用户空间的应用程序/sbin/modprobe
            wait ? UMH_WAIT_PROC : UMH_WAIT_EXEC);
    atomic_dec(&kmod_concurrent);                               // 消除atomic_inc产生的引用
    return ret;
}
EXPORT_SYMBOL(__request_module);