Как я могу выполнить / вызвать определенную в пространстве пользователя функцию из модуля пространства ядра Linux? - PullRequest
1 голос
/ 28 октября 2019

Я разрабатываю модуль Linux, который хочу использовать для запуска моей программы на C из режима ядра .

Моя проблема здесь, в функции read() модуля мне нужноиспользовать функцию с именем eval_keycode(), которая определена в моей программе пространства пользователя.

Когда я пытаюсь скомпилировать мой модуль, возникает эта ошибка:

ошибка: неявное объявлениефункция '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;
}

Это мойпользовательское пространство 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;
    }
}

Как можно вызвать функцию eval_keycode из пользовательского пространства для решения этой проблемы?

Спасибо.

Ответы [ 3 ]

2 голосов
/ 31 октября 2019

Можно, но это действительно плохая идея. Вам нужно установить указатель на функцию пользовательского режима, организовать процесс, содержащий эту функцию, для запуска (в ядре) при ее вызове. Это большая работа, и она в основном вредоносная из-за дыр в безопасности, которые она создает. Кроме того, в безумном стремлении запереть дверь в теперь пустой сарай вслед за призраком и др. В новых процессорах развертываются новые уровни хакерских атак, чтобы сделать это еще сложнее.

Другой подход:

В исходном запросе вы запускаете этот драйвер как «тройник»;то есть вы берете ввод, полученный от устройства, передаете копию вызывающей стороне и вызываете 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);
    }
}

, если вы можете организовать все события для подачи в этот файл. С этой настройкой ваша проблема становится более сложной, чем обновление ядра. Пользователь создает pipe / socket / fifo / ... и передает конец записи вашему модулю ядра (yay больше ioctl () s). Ваш модуль ядра может затем осторожно использовать kernel_write () (или vfs_write, если вы застряли в прошлом), чтобы сделать эти события доступными для обработчика пользователя. Он хочет быть осторожным с тем, где находятся его блокирующие точки.

Вы можете расширить это, чтобы работать как преобразование;именно здесь ваш драйвер преобразует события через обработчик пользовательского режима;но в этот момент вы действительно можете считать FUSE лучшим решением.

1 голос
/ 28 октября 2019

Не существует традиционного (в том смысле, как работает библиотека) способа "вызова" функции "пространства пользователя".

Ваш код пространства пользователя должен выполняться в своем собственном процессе (или другом пространстве пользователя). процесс), в котором вы реализуете связь (через разделяемую память, межпроцессные вызовы [IPC], файлы устройств, прерывания ...), где вы обрабатываете обмен данными и воздействуете на данные (например, вызываете функцию eval_keycode).

0 голосов
/ 03 ноября 2019

Вы в основном хотите upcall. Вы можете найти некоторое объяснение этого здесь , но не похоже на то, что Linux имеет официальный API upcall.

Однако, как уже упоминали другие, этоне очень хороший дизайн. Upcalls полезны для серверов, реализованных в ядре.

Если ваш exer_read() вызывается только для вашего собственного кода (для ваших файлов, для которых вы реализуете драйвер), то, возможно, inotify было бы лучше.

Если ваш exer_read() может быть вызван для любого файла (например, вы хотите, чтобы любой файл, записываемый на машине для изменения состояния светодиода), то вы хотите, чтобы ваш процесс в пользовательском пространстве содержал eval_keycode() для опроса какого-либо символьного устройства, и вы хотите, чтобы ваш модуль записал код на это символьное устройство вместо вызова eval_keycode().

Если, однако, change_led_state() является синхронным, и вам действительно нужно чтение дляблокируйте, пока он не вернется, тогда вам советуют пересмотреть ваш дизайн ... но это действительный вариант использования для upcalls.

...