我如何从 Execute/Call 内核 space 模块中 Execute/Call 用户 space 定义的函数?

How can I Execute/Call a user-space defined function from Linux kernel space module?

我正在开发一个 Linux 模块,我想在内核模式下将其用于 运行 我的 C 程序 .

我的问题在这里,在模块的函数 read() 中,我需要使用一个名为 eval_keycode() 的函数,该函数在我的用户 space 程序中定义。

当我尝试编译我的模块时,出现此错误:

error: implicit declaration of function ‘eval_keycode’

这证实了我的上述问题。

这是我模块的 read() 功能:

ssize_t exer_read(struct file *pfile, char __user *buffer, size_t length, loff_t *offset) {
    struct file *f = pfile->private_data;
    enum { MAX_BUF_SIZE = 4096 };
    size_t buf_size = 0;
    char *buf = NULL;
    ssize_t total = 0;
    ssize_t rc = 0;

    struct input_event  *ev;
    int yalv;

    /* Allocate temporary buffer. */
    if (length) {
        buf_size = min_t(size_t, MAX_BUF_SIZE, length);
        ev = kmalloc(buf_size, GFP_KERNEL);
        if (ev == NULL) {
            return -ENOMEM;
        }
    }

    /* Read file to buffer in chunks. */
    do {
        size_t amount = min_t(size_t, length, buf_size);

        rc = kernel_read(f, ev, amount, offset);
        if (rc > 0) {
            /* Have read some data from file. */
            if (copy_to_user(buffer, ev, rc) != 0) {
                /* Bad user memory! */
                rc = -EFAULT;
            } else {
                /* Update totals. */
                total += rc;
                buffer += rc;
                *offset += rc;
                length -= rc;

        for (yalv = 0; yalv < (int) (rc / sizeof(struct input_event)); yalv++) {
            if (ev[yalv].type == EV_KEY) {
                if (ev[yalv].value == 0)
                    eval_keycode(ev[yalv].code);
            }
        }


                if (rc < amount) {
                    /* Didn't read the full amount, so terminate early. */
                    rc = 0;
                }
            }
        }
    } 
    while (rc > 0 && length > 0);

    /* Free temporary buffer. */
    kfree(buf);

    if (total > 0) {
       return total;
    }
    return rc;
}

这是我的用户 space eval_keycode() 定义的函数:

void eval_keycode(int code)
{
    static int red_state = 0;
    static int green_state = 0;

    switch (code) {
    case 260:
        printf("BTN left pressed\n");

        /* figure out red state */
        red_state = red_state ? 0 : 1;

        change_led_state(LED_PATH "/" red "/brightness", red_state);
        break;

    case BTN_RIGHT:
        printf("BTN right pressed\n");

        /* figure out green state */
        green_state = green_state ? 0 : 1;

        change_led_state(LED_PATH "/" green "/brightness", green_state);
        break;
    }
}

如何从用户 space 调用 eval_keycode 函数来解决这个问题?

谢谢。

"call" 用户 space "function".

没有传统的(以图书馆的工作方式)方式

您的用户 space 代码应该 运行 在它自己的进程(或另一个用户 space 进程)中,您将在其中实现通信(通过共享内存、进程间调用[IPC]、设备文件、中断..)你处理数据交换的地方,并对数据采取行动(例如调用你的 eval_keycode 函数)。

你可以,但这是一个非常糟糕的主意。您需要建立一个指向您的用户模式函数的指针,在您调用它时将包含该函数的进程安排为 运行 (在内核中)。这是一项繁重的工作,并且由于它造成的安全漏洞,从根本上说它是恶意软件。此外,在 spectre et al 之后疯狂冲刺锁上现在空荡荡的谷仓的门,新的 hackery 层被部署在更新的 CPU 中,使这变得更加困难。

另一种方法:

在你原来的查询中,你是运行这个driver作为一个"tee";也就是说,您获取从设备接收到的输入,将副本提供给调用者,然后对每个输入调用 eval_keycode。 Eval_keycode 不修改数据,内核模块随后将其丢弃。所以 Eval_keycode 并不真的需要是一个函数;或者更确切地说,可能有一个用户函数:

void ProcessEvents(int fd) {
    struct input_event ev;
    while (read(fd, &ev, sizeof ev) == sizeof ev) {
        eval_keycode(&ev);
    }
}

如果您可以安排将所有事件输入该 fd。使用此设置,您的问题变得比内核更新更麻烦。用户创建一个 pipe/socket/fifo/... 并将写入端传递给您的内核模块(是的,更多的 ioctl()s)。然后,您的内核模块可以小心地使用 kernel_write()(如果您还停留在过去,则可以使用 vfs_write)使这些事件对用户处理程序可用。它要小心它的阻塞点在哪里。

您可以将其扩展为转换;这就是您的 driver 通过用户模式处理程序转换事件的地方;但到那时,您可能真的会认为 FUSE 是更好的解决方案。

您基本上是想上门拜访。你可以找到一些关于 here, but it doesn't seem 的解释,比如 Linux 有一个官方的 upcall API.

但是,正如其他人已经提到的,这不是很好的设计。 Upcalls 对在内核中实现的服务器很有用。

如果您的 exer_read() 只为您自己的代码调用(在您为其实现驱动程序的文件上),那么 inotify 可能是更好的设计。

如果您的 exer_read() 可以为任何文件调用(例如,您希望在机器上写入任何文件以更改 LED 状态),那么您希望包含 eval_keycode() 的用户空间进程轮询一些字符设备,并且您希望模块将代码写入此字符设备而不是调用 eval_keycode().

但是,如果 change_led_state() 是同步的,并且您实际上需要阻塞读取直到 returns,那么建议您重新考虑您的设计...但这是一个有效的用例电话。