Как мне использовать ioctl() для управления моим модулем ядра?

StackOverflow https://stackoverflow.com/questions/2264384

Вопрос

Итак, я пытаюсь написать модуль ядра, который использует файл linux / timer.h.Я заставил его работать только внутри модуля, и теперь я пытаюсь заставить его работать из пользовательской программы.

Вот мой модуль ядра:

//Necessary Includes For Device Drivers.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <linux/timer.h>
#include <linux/ioctl.h>

#define DEVICE_NAME "mytimer"
#define DEVICE_FILE_NAME "mytimer"
#define MAJOR_NUM 61
#define MINOR_NUM 0

MODULE_LICENSE("Dual BSD/GPL");

static struct timer_list my_timer;

struct file_operations FileOps = 
{
    //No File Operations for this timer.
};

//Function to perform when timer expires.
void TimerExpire(int data)
{
    printk("Timer Data: %d\n", data);
}

//Function to set up timers.
void TimerSetup(void)
{
    setup_timer(&my_timer, TimerExpire, 5678);
    mod_timer(&my_timer, jiffies + msecs_to_jiffies(5000));
}

//Module Init and Exit Functions.
int init_module(void)
{
    int initResult = register_chrdev(MAJOR_NUM, "mytimer", &FileOps);

    if (initResult < 0)
    {
        printk("Cannot obtain major number %d\n", MAJOR_NUM);

        return initResult;
    }

printk("Loading MyTimer Kernel Module...\n");


return 0;
}
void cleanup_module(void)
{
    unregister_chrdev(MAJOR_NUM, "mytimer");
    printk("Unloading MyTimer Kernel Module...\n");
}

Более конкретно, я хочу, чтобы моя пользовательская программа вызывала функцию TimerSetup().Я знаю, что мне нужно будет использовать ioctl(), но я не уверен, как указать в моем ФАЙЛЕ МОДУЛЯ, что TimerSetup() должна вызываться через ioctl().

Кроме того, мой второй вопрос:Я смог внести изменения в свой модуль, а также ввести mknod в /dev/mytimer с правильным основным номером.Но когда я попытался открыть () его, чтобы получить из него файловый дескриптор, он продолжал возвращать значение -1, что, как я предполагаю, неверно.Я убедился, что с разрешениями все в порядке (на самом деле, я сделал это 777, просто чтобы быть уверенным)...Это все еще не работает...Есть ли что-то, чего я не понимаю?

Вот пользовательская программа на всякий случай:

#include <stdio.h>

int main(int argc, char* argv[])
{
    int fd = open("/dev/mytimer", "r");
    printf("fd: %d\n", fd);

    return 0;
}
Это было полезно?

Решение

Пример кода, который вам нужен, можно найти в drivers/watchdog/softdog.c (из Linux 2.6.33 на момент написания этой статьи), которая иллюстрирует правильные файловые операции, а также то, как разрешить пользовательской области заполнять структуру с помощью ioctl().

На самом деле это отличное рабочее руководство для всех, кому нужно написать тривиальные драйверы символьных устройств.

Я проанализировал интерфейс ioctl softdog, когда отвечая на мой собственный вопрос, что может быть полезно для вас.

Вот его суть (хотя и далеко не исчерпывающая) ...

В softdog_ioctl() вы видите простую инициализацию struct watchdog_info, которая рекламирует функциональность, версию и информацию об устройстве:

    static const struct watchdog_info ident = {
            .options =              WDIOF_SETTIMEOUT |
                                    WDIOF_KEEPALIVEPING |
                                    WDIOF_MAGICCLOSE,
            .firmware_version =     0,
            .identity =             "Software Watchdog",
    };

Затем мы рассмотрим простой случай, когда пользователь просто хочет получить эти возможности:

    switch (cmd) {
    case WDIOC_GETSUPPORT:
            return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;

...который, конечно же, заполнит соответствующее пользовательское пространство watchdog_info указанными выше инициализированными значениями.Если copy_to_user() завершается с ошибкой, возвращается значение -EFAULT, что приводит к тому, что соответствующий вызов ioctl() в пользовательском пространстве возвращает значение -1 с установленным значимым значением errno.

Обратите внимание, что магические запросы фактически определены в linux / watchdog.h , так что ядро и пользовательское пространство совместно используют их:

#define WDIOC_GETSUPPORT        _IOR(WATCHDOG_IOCTL_BASE, 0, struct watchdog_info)
#define WDIOC_GETSTATUS         _IOR(WATCHDOG_IOCTL_BASE, 1, int)
#define WDIOC_GETBOOTSTATUS     _IOR(WATCHDOG_IOCTL_BASE, 2, int)
#define WDIOC_GETTEMP           _IOR(WATCHDOG_IOCTL_BASE, 3, int)
#define WDIOC_SETOPTIONS        _IOR(WATCHDOG_IOCTL_BASE, 4, int)
#define WDIOC_KEEPALIVE         _IOR(WATCHDOG_IOCTL_BASE, 5, int)
#define WDIOC_SETTIMEOUT        _IOWR(WATCHDOG_IOCTL_BASE, 6, int)
#define WDIOC_GETTIMEOUT        _IOR(WATCHDOG_IOCTL_BASE, 7, int)
#define WDIOC_SETPRETIMEOUT     _IOWR(WATCHDOG_IOCTL_BASE, 8, int)
#define WDIOC_GETPRETIMEOUT     _IOR(WATCHDOG_IOCTL_BASE, 9, int)
#define WDIOC_GETTIMELEFT       _IOR(WATCHDOG_IOCTL_BASE, 10, int)

WDIOC, очевидно, означающий "Сторожевой пес ioctl"

Вы можете легко сделать еще один шаг вперед, попросив вашего драйвера что-то сделать и поместить результат этого чего-то в структуру и скопировать его в пользовательское пространство.Например, если в структуре watchdog_info также был член __u32 result_code.Примечание, __u32 это просто версия ядра для uint32_t.

С помощью ioctl() пользователь передает адрес объекта, будь то структура, целое число, что угодно, ядру, ожидая, что ядро запишет свой ответ в идентичный объект и скопирует результаты на указанный адрес.

Второе, что вам нужно будет сделать, это убедиться, что ваше устройство знает, что делать, когда кто-то открывает, читает с него, записывает на него или использует перехват, подобный ioctl(), который вы можете легко увидеть, изучив softdog.

Представляющий интерес является:

static const struct file_operations softdog_fops = {
        .owner          = THIS_MODULE,
        .llseek         = no_llseek,
        .write          = softdog_write,
        .unlocked_ioctl = softdog_ioctl,
        .open           = softdog_open,
        .release        = softdog_release,
};

Куда , как вы видите , направляется обработчик unlocked_ioctl ...вы уже догадались, softdog_ioctl().

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

Для таймера, я согласен, ioctl() - это кратчайший путь к здравомыслию.

Другие советы

Вам не хватает .open указатель на функцию в вашем file_operations структура, указывающая функцию, которая будет вызываться при попытке процесса открыть файл устройства.Вам нужно будет указать .ioctl указатель функции также для вашей функции ioctl.

Попробуйте дочитать до конца Руководство по программированию модуля ядра Linux, в частности, главы 4 (Файлы символьных устройств) и 7 (Общение с файлами устройств).

Глава 4 знакомит с file_operations структура, которая содержит указатели на функции, определенные модулем / драйвером, которые выполняют различные операции, такие как open или ioctl.

Глава 7 предоставляет информацию об обмене данными с модулем / дисководом через ioctls.

Драйверы устройств Linux, Третье издание это еще один хороший ресурс.

Минимальный выполнимый пример

Протестировано в полностью воспроизводимой среде QEMU + Buildroot, поэтому может помочь другим получить свои ioctl работаю.Восходящий поток GitHub:модуль ядра | общий заголовок | пользовательская территория.

Самой раздражающей частью было понимание того, что некоторые низкие идентификаторы перехвачены: ioctl не вызывается , если cmd = 2 , вы должны использовать _IOx макросы.

Модуль ядра:

#include <asm/uaccess.h> /* copy_from_user, copy_to_user */
#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/printk.h> /* printk */

#include "ioctl.h"

MODULE_LICENSE("GPL");

static struct dentry *dir;

static long unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long argp)
{
    void __user *arg_user;
    union {
        int i;
        lkmc_ioctl_struct s;
    } arg_kernel;

    arg_user = (void __user *)argp;
    pr_info("cmd = %x\n", cmd);
    switch (cmd) {
        case LKMC_IOCTL_INC:
            if (copy_from_user(&arg_kernel.i, arg_user, sizeof(arg_kernel.i))) {
                return -EFAULT;
            }
            pr_info("0 arg = %d\n", arg_kernel.i);
            arg_kernel.i += 1;
            if (copy_to_user(arg_user, &arg_kernel.i, sizeof(arg_kernel.i))) {
                return -EFAULT;
            }
        break;
        case LKMC_IOCTL_INC_DEC:
            if (copy_from_user(&arg_kernel.s, arg_user, sizeof(arg_kernel.s))) {
                return -EFAULT;
            }
            pr_info("1 arg = %d %d\n", arg_kernel.s.i, arg_kernel.s.j);
            arg_kernel.s.i += 1;
            arg_kernel.s.j -= 1;
            if (copy_to_user(arg_user, &arg_kernel.s, sizeof(arg_kernel.s))) {
                return -EFAULT;
            }
        break;
        default:
            return -EINVAL;
        break;
    }
    return 0;
}

static const struct file_operations fops = {
    .owner = THIS_MODULE,
    .unlocked_ioctl = unlocked_ioctl
};

static int myinit(void)
{
    dir = debugfs_create_dir("lkmc_ioctl", 0);
    /* ioctl permissions are not automatically restricted by rwx as for read / write,
     * but we could of course implement that ourselves:
     * https://stackoverflow.com/questions/29891803/user-permission-check-on-ioctl-command */
    debugfs_create_file("f", 0, dir, NULL, &fops);
    return 0;
}

static void myexit(void)
{
    debugfs_remove_recursive(dir);
}

module_init(myinit)
module_exit(myexit)

Общий заголовок:

#ifndef IOCTL_H
#define IOCTL_H

#include <linux/ioctl.h>

typedef struct {
    int i;
    int j;
} lkmc_ioctl_struct;
#define LKMC_IOCTL_MAGIC 0x33
#define LKMC_IOCTL_INC     _IOWR(LKMC_IOCTL_MAGIC, 0, int)
#define LKMC_IOCTL_INC_DEC _IOWR(LKMC_IOCTL_MAGIC, 1, lkmc_ioctl_struct)

#endif

Пользовательская территория:

#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include "../ioctl.h"

int main(int argc, char **argv)
{
    int fd, arg_int, ret;
    lkmc_ioctl_struct arg_struct;

    if (argc < 2) {
        puts("Usage: ./prog <ioctl-file>");
        return EXIT_FAILURE;
    }
    fd = open(argv[1], O_RDONLY);
    if (fd == -1) {
        perror("open");
        return EXIT_FAILURE;
    }
    /* 0 */
    {
        arg_int = 1;
        ret = ioctl(fd, LKMC_IOCTL_INC, &arg_int);
        if (ret == -1) {
            perror("ioctl");
            return EXIT_FAILURE;
        }
        printf("arg = %d\n", arg_int);
        printf("ret = %d\n", ret);
        printf("errno = %d\n", errno);
    }
    puts("");
    /* 1 */
    {
        arg_struct.i = 1;
        arg_struct.j = 1;
        ret = ioctl(fd, LKMC_IOCTL_INC_DEC, &arg_struct);
        if (ret == -1) {
            perror("ioctl");
            return EXIT_FAILURE;
        }
        printf("arg = %d %d\n", arg_struct.i, arg_struct.j);
        printf("ret = %d\n", ret);
        printf("errno = %d\n", errno);
    }
    close(fd);
    return EXIT_SUCCESS;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top