c - Linux 内核模块/IOCTL : inappropriate ioctl for device

标签 c linux linux-kernel linux-device-driver kernel-module

我正在编写用作伪驱动程序的 Linux 内核模块 (LKM) - 我无法弄清楚如何在 LKM 之间进行 IOCTL 调用 (wait.c)和用户级程序 (user.c)。

设备驱动程序的魔数(Magic Number)是0xBF - LKM 不与物理 block /字符设备通信,它只是一个练习。据我所知,对 KERN_IOCTL_CREATE_EVENT 的 IOCTL 调用格式不正确,魔数(Magic Number)也不正确。

我尝试使用的 IOCTL 调用是:

#include <sys/ioctl.h>
#define KERN_IOCTL_CREATE_EVENT   _IOWR(WAIT_DEVICE_MAGIC, 1, int)

int main(){
int ret;
int fd;
fd = open("/dev/wait", 0);
if(fd < 0){
    return -1;
}
ret = ioctl(fd, KERN_IOCTL_CREATE_EVENT, 0);

错误:

[fail]: KERN_IOCTL_CREATE_EVENT: Inappropriate ioctl for device

用户模式应用程序可以打开/关闭指向设备的文件描述符:/dev/wait 但是 case/switch语句不接受 IOCTL 调用。有什么建议吗?

这是 # uname -a 的输出

Linux vagrant-ubuntu-trusty-64 3.13.11.11+ #1 SMP Mon Dec 1 20:50:23 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

wait.c

#include <linux/miscdevice.h>
#include <linux/moduleparam.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <asm/uaccess.h>
#include <linux/sched.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/fs.h>

#include "wait.h"

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tyler Fisher <tyler@tylerfisher.org>");
MODULE_DESCRIPTION("In-kernel wait queue");

static unsigned long event_table_size = 50;
module_param(event_table_size, ulong, (S_IRUSR | S_IRGRP | S_IROTH));
MODULE_PARM_DESC(event_table_size, "Size of event table (i.e. how many processes can be blocking)");

/* IOCTL function headers */
static int wait_open(struct inode *, struct file *);
static int wait_close(struct inode *, struct file *);
static long wait_ioctl(struct inode *, struct file *, unsigned int, unsigned long);

/* other function headers */
static long event_open(int event_id);

/* file operations */
static struct file_operations wait_fops = {
    .owner = THIS_MODULE,
    .open = wait_open,
    .release = wait_close,
    .llseek = noop_llseek,
    .unlocked_ioctl = wait_ioctl
};

/* device handler */
static struct miscdevice wait_misc_device = {
  .minor = MISC_DYNAMIC_MINOR,
  .name = WAIT_DEVICE_NAME,
  .fops = &wait_fops
};

/* open wait device */
static int wait_open(struct inode *inode, struct file *file){
    dev_t node = iminor(inode);
    if(MINOR(node) != WAIT_DEVICE_MINOR){
        return -ENODEV;
    }
    return 0;
}

static int wait_close(struct inode *inode, struct file *file){
    return 0;
}

static long wait_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long sub_cmd){
switch(cmd){
    case KERN_IOCTL_CREATE_EVENT:
        printk(KERN_INFO "[wait device]: create event %lu\n", sub_cmd);
        return event_open(sub_cmd);

    default:
        return -ENOENT;
    }
}

static long event_open(int id){
    return 0;
}

static long __init wait_init(void){
    if(misc_register(&wait_misc_device) < 0){
        printk(KERN_ERR "[wait device] failed to register device\n");
        return -1;
    }
    printk(KERN_INFO "[wait device] has been registered\n");
    return 0;
}

static void __exit wait_exit(void){
    misc_deregister(&wait_misc_device);
    printk(KERN_INFO "[wait device] has been unregistered\n");
}

module_init(wait_init);
module_exit(wait_exit);

wait.h

#include <linux/ioctl.h>

#define WAIT_DEVICE_NAME    "wait"
#define WAIT_DEVICE_MAGIC   0xBF
#define WAIT_DEVICE_MAJOR   200
#define WAIT_DEVICE_MINOR   0

#define KERN_IOCTL_CREATE_EVENT     _IOWR(WAIT_DEVICE_MAGIC, 0x01, int)

#define MAX_WAITING 5

IOCTL调用的测试程序:

user.c

#include <sys/ioctl.h>
#include <fcntl.h>
#include <stdio.h>

#define WAIT_DEVICE_MAGIC   0xBF
#define KERN_IOCTL_CREATE_EVENT   _IOWR(WAIT_DEVICE_MAGIC, 0x01, int)
#define KERN_IOCTL_DESTROY_EVENT  _IOWR(WAIT_DEVICE_MAGIC, 0x02, int)
#define KERN_IOCTL_LOCK_EVENT     _IOWR(WAIT_DEVICE_MAGIC, 0x03, int)
#define KERN_IOCTL_UNLOCK_EVENT   _IOWR(WAIT_DEVICE_MAGIC, 0x04, int)

int main(){
    int fd;
    if(fd = open("/dev/wait", O_RDWR) < 0){
        perror("failed to open /dev/wait");
        return -1;
    }

    /* test IOCTL: event creation */
    if(ioctl(fd, KERN_IOCTL_CREATE_EVENT, 0) < 0){
        perror("[fail]: KERN_IOCTL_CREATE_EVENT");
        return -1;
    }
    return 0;
}

生成文件

obj-m += wait.o
CFLAGS_wait.o += -DDEBUG

all:
  make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
  make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

为了测试 LKM - 清除 dmesg,编译并执行 user.c w/GCC:

# dmesg -c > /dev/null 2>&1
# make
# rmmod wait.ko
# insmod wait.ko
# gcc user.c -o user && ./user

调试错误的数量令人尴尬。我对分享这个感到难过 - 并且意识到这可能会导致问题被关闭/否决以被遗忘。

# sh test.sh
[+] cleared dmesg
make -C /lib/modules/3.13.11.11+/build M=/home/vagrant/PROG40000-kernel-synchronization modules
make[1]: Entering directory `/home/vagrant/ubuntu-trusty'
  CC [M]  /home/vagrant/PROG40000-kernel-synchronization/wait.o
/home/vagrant/PROG40000-kernel-synchronization/wait.c:61:1: warning: initialization from incompatible pointer type [enabled by default]
 };
 ^
/home/vagrant/PROG40000-kernel-synchronization/wait.c:61:1: warning: (near initialization for ‘wait_fops.unlocked_ioctl’) [enabled by default]
In file included from include/linux/moduleparam.h:4:0,
                 from /home/vagrant/PROG40000-kernel-synchronization/wait.c:11:
/home/vagrant/PROG40000-kernel-synchronization/wait.c: In function ‘__inittest’:
include/linux/init.h:297:4: warning: return from incompatible pointer type [enabled by default]
  { return initfn; }     \
    ^
/home/vagrant/PROG40000-kernel-synchronization/wait.c:167:1: note: in expansion of macro ‘module_init’
 module_init(wait_init);
 ^
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/vagrant/PROG40000-kernel-synchronization/wait.mod.o
  LD [M]  /home/vagrant/PROG40000-kernel-synchronization/wait.ko
make[1]: Leaving directory `/home/vagrant/ubuntu-trusty'
[--dmesg--]
[13112.810008] [wait device] has been unregistered
[13112.819049] [wait device] has been registered
[-/dmesg--]
[+] compiled user-mode program
-----
[fail]: KERN_IOCTL_CREATE_EVENT: Inappropriate ioctl for device
[fail]: KERN_IOCTL_CREATE_EVENT: Inappropriate ioctl for device
[+] executed user-mode program
-----
[--dmesg--]
[13112.810008] [wait device] has been unregistered
[13112.819049] [wait device] has been registered
[13112.893049] SOMEONE DARE READ FROM ME!?
[13112.893057] [wait device] invalid magic number: 0:0:191
[13112.893535] [wait device] invalid magic number: 0:0:191
[-/dmesg--]

最佳答案

好的。所以。这是解决方案。

在 Linux 内核 2.6.x 中,_ioctl 调用的声明从

static long wait_ioctl(struct inode *, struct file *, unsigned int, unsigned long);

收件人:

static long wait_ioctl(struct file *, unsigned int, unsigned long);

因此修复是:

...
static long wait_ioctl(struct file *, unsigned int, unsigned long);
...
static long wait_ioctl(struct file *file, unsigned int cmd, unsigned long sub_cmd){
    if(_IOC_TYPE(cmd) != WAIT_DEVICE_MAGIC){
       printk(KERN_INFO "[wait device] invalid magic number: %u:%u:%u", _IOC_TYPE(cmd), cmd, WAIT_DEVICE_MAGIC);
       return -ENOTTY;
    }
 ....

关于c - Linux 内核模块/IOCTL : inappropriate ioctl for device,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27308901/

相关文章:

c++ - Main 无法调用函数 : function was not declared in this scope

c - 缓冲区困惑

c - 多次运行 C 程序,每次使用不同的输入

c - 如何在 C 中创建虚拟以太网设备?

linux - 如何从 Linux .so 文件中删除未使用的代码?

linux-kernel - 如何停止阻塞linux内核线程

c - 尝试在 C 中创建具有所有权限的文件

c - 如何解决Linux内核中C中的错误 "initializer element is not constant"

networking - Netfilter_queue 是否只提供控制包

c++ - 编译有效,启动不是 - "standard path"?