设备错误的不适当ioctl

嘿, 我在try 构建内核模块时遇到了问题.这是一个名为Message_Slot的字符设备模块.根据说明,它应该支持最多256名未成年人(所有人都有相同的专业-235).每个辅助项代表一个不同的设备文件,最多可以有2^20个通道.为了做到这一点,我使用了一个二叉树列表,这样每个条目代表一个不同的设备 文件.

成功签发后: make个 (使用通用的Makefile,与我的同事和我的助教使用的Makefile相同)

sudo insmod message_slot.ko个 (我在模块init函数中有一条printk语句,在此命令之后我在kerenel日志(log)中看到了它,所以我知道注册成功了)

sudo mknod /dev/slot1 c 235 1

sudo chmod o+rw /dev/slot1

在使用-o sender编译名为Message_sender.c的用户程序之后 现在,我准备使用以下命令向设备文件(次要编号1)写入一条消息:

./sender /dev/slot1 1 message

(Message_sender.c main函数获得3个参数-设备文件、次要文件和要写入的消息)

发出后,我收到以下消息: Ioctl failed: Inappropriate ioctl for device

我真的不明白为什么.

附件是用户程序Message_sender.c、模块Message_slot.c和标题Message_slot.h的部分内容:

message_sender.c

    #include <fcntl.h>
    #include <unistd.h>
    #include <sys/ioctl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    #include "message_slot.h"


    int main(int argc, char** argv){
    int fd;
    unsigned int channel_num;
    char *msg;
    if (argc != 4){
        perror("Error- Invalid number of arguments");
        exit(1);
    }
    if ((fd = open(argv[1], O_RDWR) < 0)){
        perror("Couldnt open the specified file");
        exit(1);
    }
    channel_num = atoi(argv[2]);
    printf("channel num: %d", channel_num);
    if (ioctl(fd,MSG_SLOT_CHANNEL, channel_num) < 0){
        perror("Ioctl failed");
        exit(1);
    }
    msg = argv[3];
    if (write(fd, msg, strlen(msg)) != strlen(msg)){
        perror("Error occured while writing the message");
        exit(1);
    }
    close(fd);
    exit(0);
}

message_slot.c

#undef __KERNEL__
#define __KERNEL__
#undef MODULE
#define MODULE


#include <linux/kernel.h>  
#include <linux/module.h>   
#include <linux/fs.h>
#include <linux/init.h>      
#include <linux/uaccess.h>  
#include <linux/string.h> 
#include <linux/slab.h> 
#include "message_slot.h"
MODULE_LICENSE("GPL");

struct channel* search_channel(struct channel*, int);
void insert_channel(struct channel*, struct channel*);
void cleanup_trees(struct channel*);

static struct channel *minors[257];

struct channel* search_channel(struct channel* root, int x) {
    struct channel* curr = root;
    while (curr != NULL) {
        if (curr->channel_number == x) {
            return curr;  // Found the channel with the given channel_number
        } else if (x < curr->channel_number) {
            curr = curr->left;  // Go to the left subtree
        } else {
            curr = curr->right;  // Go to the right subtree
        }
    }
    return NULL;  // Channel with the given channel_number (x) not found
}

void insert_channel(struct channel* root, struct channel* node) {
    struct channel* curr;
    if (root == NULL) {
        // The tree is empty, make the node as the root
        root = node;
    } 
    else {
        curr = root;
        while (1) {
            if ((node->channel_number) < (curr->channel_number)) {
                if (curr->left == NULL) {
                    curr->left = node;  // Insert node as the left child
                    break;
                } 
                else {
                    curr = curr->left;  // Move to the left subtree
                }
            } 
            else {
                if (curr->right == NULL) {
                    curr->right = node;  // Insert node as the right child
                    break;
                } 
                else {
                    curr = curr->right;  // Move to the right subtree
                }
            }
        }
    }
}

// A clean up function, which will be called for each entry in minors when we exit the module

void cleanup_trees(struct channel* root) {
    if (root == NULL) {
        return;  // Base case: empty tree or leaf node
    }

    // Post-order traversal to delete nodes recursively
    cleanup_trees(root->left);
    cleanup_trees(root->right);

    // Delete the current node
    kfree(root);
}

//DEVICE FUNCTIONS:
static int device_open(struct inode* inode, struct file* file) {
    file->private_data = NULL; 
    return SUCCESS;
}


static long device_ioctl(struct file* file, unsigned int ioctl_command_id, unsigned long ioctl_param){
    struct channel *curr;
    struct channel *root;
    int minor;
    printk("in ioctl\n");
    if (ioctl_command_id != MSG_SLOT_CHANNEL || ioctl_param == 0){
        printk("Channel/ Command error\n");
        return -EINVAL;
    }
    minor = iminor(file->f_inode);
    root = minors[minor];
    if((curr = search_channel(root, ioctl_param)) == NULL){
        curr = kmalloc(sizeof(struct channel), GFP_KERNEL);
        curr->channel_number = ioctl_param;
        curr->left = NULL;
        curr->right = NULL;
        insert_channel(root, curr);
    }
    file->private_data = (void *)curr;
    return SUCCESS;
}

static ssize_t device_write(struct file *file, const char __user *buffer, size_t length, loff_t *offset){
    struct channel *curr_channel = (struct channel *)file->private_data;
    char *msg;
    int i;
    if (curr_channel == NULL){
        printk("No channel has been set for this file");
        return -EINVAL;
    }
    if (length == 0 || length > MAX_BUF_LEN){
        printk("Invalid message length");
        return -EMSGSIZE;
    }
    if (buffer == NULL){
        printk("Null buffer");
        return -EINVAL;
    }
    msg = curr_channel->message;
    for (i=0; i< length; i++){
        get_user(msg[i],&buffer[i]);
    }
    curr_channel->bytes = length;
    return length;
}

// REST OF DEVICE FUNCTIONS:
.
.
struct file_operations Fops =
    {
        .owner = THIS_MODULE,
        .read = device_read,
        .write = device_write,
        .open = device_open,
        .unlocked_ioctl = device_ioctl,
        .release = device_release,
};

static int __init ms_init(void){
    int rc;
    int i;
    if ((rc = register_chrdev(MAJOR_NUM,DEVICE_NAME,&Fops)) < 0){
        printk("Registration failed");
        return FAIL;
    }
    for (i=0 ; i < 257 ; i ++){
        minors[i] = NULL;
    }
    printk("Registration Successed\n");
    return SUCCESS;
}

//DEVICE RELEASE FUNCTION:
.
.
module_init(slot_init);
module_exit(slot_cleanup);

message_slot.h:

#ifndef MESSAGE_SLOT_H
#define MESSAGE_SLOT_H

#include <linux/ioctl.h>
#define MAJOR_NUM 235
#define MSG_SLOT_CHANNEL _IOW(MAJOR_NUM, 0, unsigned int)
#define DEVICE_NAME "message_slot"
#define MAX_BUF_LEN 128
#define SUCCESS 0
#define FAIL -1

// We will define the struct channel 
// Considering the fact that we keep all channels in a binary tree
// Also required: channel number, the current message

struct channel {
    int channel_number;
    char message[128];
    int bytes;
    struct channel *right;
    struct channel *left;
};
#endif

Device_ioctl函数is not printed to the kernel log中的printk("in ioctl\n");语句,避免了在用户发出ioctl之后,我们没有进入模块的ioctl_Device实现.

任何帮助我们都将不胜感激!

EDIT

下面是strace除以sender的输出:

student@OS23:~/ex3_XXXXXXX06$ strace ./sender /dev/slot1 1 message
execve("./sender", ["./sender", "/dev/slot1", "1", "message"], 0x7ffd541beed8 /* 22 vars */) = 0
brk(NULL)                               = 0x562632988000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=75005, ...}) = 0
mmap(NULL, 75005, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fe49a3a1000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0@>\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1905632, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe49a39f000
mmap(NULL, 1918592, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fe49a1ca000
mmap(0x7fe49a1ec000, 1417216, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x22000) = 0x7fe49a1ec000
mmap(0x7fe49a346000, 323584, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x17c000) = 0x7fe49a346000
mmap(0x7fe49a395000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1ca000) = 0x7fe49a395000
mmap(0x7fe49a39b000, 13952, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fe49a39b000
close(3)                                = 0
arch_prctl(ARCH_SET_FS, 0x7fe49a3a0540) = 0
mprotect(0x7fe49a395000, 16384, PROT_READ) = 0
mprotect(0x562630bb1000, 4096, PROT_READ) = 0
mprotect(0x7fe49a3de000, 4096, PROT_READ) = 0
munmap(0x7fe49a3a1000, 75005)           = 0
openat(AT_FDCWD, "/dev/slot1", O_RDWR)  = 3
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
brk(NULL)                               = 0x562632988000
brk(0x5626329a9000)                     = 0x5626329a9000
ioctl(0, _IOC(_IOC_WRITE, 0xeb, 0, 0x4), 0x1) = -1 ENOTTY (Inappropriate ioctl for device)
dup(2)                                  = 4
fcntl(4, F_GETFL)                       = 0x402 (flags O_RDWR|O_APPEND)
fstat(4, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
write(4, "Ioctl failed: Inappropriate ioct"..., 45Ioctl failed: Inappropriate ioctl for device
) = 45
close(4)                                = 0
write(1, "channel num: 1", 14channel num: 1)          = 14
exit_group(1)                           = ?
+++ exited with 1 +++

推荐答案

strace的输出中:

ioctl(0, _IOC(_IOC_WRITE, 0xeb, 0, 0x4), 0x1) = -1
      ^
      What's this?!?!?!

然后是这个代码:

if ((fd = open(argv[1], O_RDWR) < 0)){

该代码解析为

if ((fd = ( open(argv[1], O_RDWR) < 0 ) )){

因为open()调用每个strace输出返回3

openat(AT_FDCWD, "/dev/slot1", O_RDWR)  = 3

它的计算结果为

fd = ( 3 < 0 )

fd = 0

You are trying to run your ioctl() call on the standard input file descript或.

This构建不容易出现这样的错误:

int fd = open(argv[1], O_RDWR);
if ( fd < 0 ) {
    .
    .
    .

这里的教训是什么?不要把赋值塞进if条语句中--它很容易出错,你看不到这个错误.

在我发布这个答案的时候,有30+的浏览量,no one else spotted it either.

C++相关问答推荐

如何跨平台处理UTF-16字符串?

struct 上的OpenMP缩减

LONG_DOUBLE_T是否存在(标准C:C23)

轮询libusb_pollfd struct 列表的正确方式是什么?

GDB输出ARM助记符

GTK3按钮信号错误

在txt文件中找到指定的字符串,并从数字中减go 相同的值

在libwget中启用Cookie会导致分段故障

可变宏不能编译

按长度对argv中的单词进行排序

为什么我在C代码中得到一个不完整的类型?

程序如何解释变量中的值

为什么GCC不能在 struct 初始值设定项中以sizeof作为条件的三进制中处理复合文字的编译时求值?

使用邻接表创建图

全局变量 y0 与 mathlib 冲突,无法编译最小的 C 代码

Codewars Kata 掷骰子的不稳定行为

c 函数指针,另一种语法

我如何(合规地)从 tmpfile() 读取?

在 printf() 格式说明符中使用字段宽度变量

在 SDL 中将鼠标位置设置为 (0,0)