简单内核模块中的 ioctl() 问题

简单内核模块中的 ioctl() 问题

我正在尝试构建一个简单的内核模块。以下是其中涉及的文件的内容:

模块.c:

    #include <linux/init.h>
    #include <linux/fs.h>
    #include <linux/device.h>
    #include <linux/kernel.h>
    #include "header.h"






    static int device_open(struct inode *inode, struct file *file)
    {
    printk("\n Open \n");
    return 0;
    }

    static int device_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long args)
    {
    switch(cmd)
    {
    case IOCTL_CMD:
    printk(KERN_ALERT "\n %s \n", (char *)args);
    break;
    }
    return 0;
    }


    static int device_release(struct inode *inode, struct file *file)
    {
    printk("\n Release \n");
    return 0;
    }

    static struct class *my_class;

    static struct file_operations fops={
    .open = device_open,
    .release = device_release,
    .compat_ioctl = device_ioctl
    };

    static int hello_init(void)
    {
    major_no = register_chrdev(0, DEVICE_NAME, &fops);
    printk("\n Major_no : %d", major_no);

    my_class = class_create(THIS_MODULE, DEVICE_NAME);
    device_create(my_class, NULL, MKDEV(major_no,0), NULL, DEVICE_NAME);
    printk("\n Device Initialized in kernel ....!!!");
    return 0;
    }




    static void hello_exit(void)
    {
    printk("\n Device is Released or closed \n");
    device_destroy(my_class,MKDEV(major_no,0));
    class_unregister(my_class);
    class_destroy(my_class);
    unregister_chrdev(major_no, DEVICE_NAME);
    printk("\n===============================================================\n");
    }



    module_init(hello_init);
    module_exit(hello_exit);

    MODULE_LICENSE("GPL");

应用程序

    #include <stdio.h>
    #include <fcntl.h>
    #include <string.h>

    #include "header.h"

    int main()
    {
    int fd;
    char * msg = "yahoooo";
    fd = open(DEVICE_PATH, O_RDWR);
    ioctl(fd, IOCTL_CMD, msg);
    printf("ioctl executed\n");
    close(fd);
    return 0;
    }

标头.h:

    #include <linux/ioctl.h>
    #include <linux/kdev_t.h> /* for MKDEV */

    #define DEVICE_NAME "my_dev"
    #define DEVICE_PATH "/dev/my_dev"
    #define WRITE 0
    static int major_no;
    #define MAGIC_NO '4'
    /* 
     * Set the message of the device driver 
     */
    #define IOCTL_CMD _IOR(MAGIC_NO, 0, char *)

我的模块加载完美(我可以在 hello_init() 函数中看到消息)。但是当我运行 appln.c 程序时,即使它进行了 ioctl() 调用,我也看不到它的结果。有人能告诉我为什么模块忽略了我的 ioctl 调用吗?

谢谢,

答案1

一些东西:

  • 您想使用“unlocked_ioctl”而不是“compat_ioctl”。
  • “device_ioctl”的函数接口是错误的(参见include/linux/fs.h),应该是:

        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
    
  • appln.c 不检查错误代码(打开读写控制)。

修复该问题后,代码就可以正常工作了。

答案2

最小可运行示例

在完全可重现的 QEMU + Buildroot 环境中进行了测试,因此可能有助于其他人完成他们的ioctl工作。GitHub 上游: 内核模块| 共享标头| 用户空间

最令人恼火的是了解到一些低 ID 被劫持:https://stackoverflow.com/questions/10071296/ioctl-is-not-called-if-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;
}

相关内容