簡體   English   中英

如何從 Linux kernel 空間模塊執行/調用用戶空間定義的 function?

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

我正在開發一個 Linux 模塊,我想用它來從 kernel 模式運行我的 C 程序

我的問題在這里,在模塊的 function read()中,我需要使用一個名為eval_keycode()的 function ,它是在我的用戶空間程序中定義的。

當我嘗試編譯我的模塊時,會發生此錯誤:

錯誤:function 'eval_keycode' 的隱式聲明

這證實了我上面描述的問題。

這是我模塊的read() function:

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;
}

這是我的用戶空間eval_keycode()定義的 function:

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;
    }
}

如何從用戶空間調用eval_keycode function 來解決這個問題?

謝謝你。

你可以,但這是一個非常糟糕的主意。 您需要建立一個指向您的用戶模式 function 的指針,安排包含該 function 的進程在您調用它時運行(在內核中)。 這是一項繁重的工作,而且由於它造成的安全漏洞,基本上是惡意軟件。 此外,在 Spectre 等人的襲擊之后,為了鎖上現在空谷倉的門而瘋狂沖刺,新的黑客層被部署在更新的 CPU 中,使這變得更加困難。

另一種方法:

在您的原始查詢中,您將此驅動程序作為“三通”運行; 也就是說,您獲取從設備接收到的輸入,將副本提供給調用者,然后使用每個輸入調用 eval_keycode。 eval_keycode 不修改數據,之后 kernel 模塊將其丟棄。 所以 Eval_keycode 並不需要是 function; 或者更確切地說,可能有一個用戶 function:

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

如果您可以安排將所有事件輸入該 fd。 使用此設置,您的問題變得比 kernel 翻新更麻煩。 用戶創建一個管道/套接字/fifo/...並將寫入端傳遞給您的 kernel 模塊(更多的 ioctl()s)。 然后,您的 kernel 模塊可以小心地使用 kernel_write() (或 vfs_write 如果您過去遇到問題)使這些事件可用於用戶處理程序。 它想小心它的阻塞點在哪里。

您可以將其擴展為轉換; 那是您的驅動程序通過用戶模式處理程序轉換事件的地方; 但到那時,您可能真的認為 FUSE 是一個更好的解決方案。

沒有傳統的(以庫的工作方式)“調用”用戶空間“函數”的方式。

您的用戶空間代碼應該在其自己的進程(或另一個用戶空間進程)中運行,您將在其中實現通信(通過共享 memory、進程間調用 [IPC]、設備文件、中斷......),您可以在其中處理交換數據,並對數據采取行動(例如調用您的 eval_keycode 函數)。

你不能。

此時,您有兩個選擇:

  1. 在用戶空間中完成這一切,這將保持向后兼容性並變得更加穩定。

  2. 將您的用戶空間代碼復制到 kernel 代碼中並進行更改,例如從printf移動到printk以及從使用抽象切換到使用 kernel API。

  3. 做一些別的事情來分散自己的注意力,因為你需要做上述兩個中的一個來解決問題是徒勞的。

做出明智的選擇。

你基本上想要一個upcall。 你可以在這里找到一些解釋,但 Linux 似乎沒有官方的上調 API。

然而,正如其他人已經提到的那樣,這不是很好的設計。 Upcalls 對於在 kernel 中實現的服務器很有用。

如果您的exer_read()僅針對您自己的代碼(在您正在為其實現驅動程序的文件上)調用,那么inotify可能是一個更好的設計。

如果您的exer_read()可以為任何文件調用(例如,您希望在機器上寫入任何文件以更改 LED 狀態),那么您希望包含eval_keycode()的用戶空間進程輪詢某些字符設備,並且您希望您的模塊將代碼寫入此字符設備,而不是調用eval_keycode()

但是,如果change_led_state()是同步的,並且您實際上需要阻止讀取直到它返回,那么建議您重新考慮您的設計......但這是向上調用的有效用例。

在 linux 上,用戶空間代碼使用文件 API 與 kernel 代碼交互。 通常在不移動數據時使用 yocto 系統調用。

因此,您需要實現一個字符設備。 使用 shell 中的 mknod 添加新文件名,以將設備添加到 /dev 目錄。

然后,您的用戶代碼使用 open API 打開設備並使用 ioctl 調用進行交互。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM