c - Linux中如何解决以/dev文件作为内核模块的读写操作?

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

显然,在经历了内核编程的许多麻烦之后,这是一个不足为奇的新手问题。我尝试启动一个程序,让/dev 文件夹中的驱动程序文件可用于某些读写(事实上,我意识到这是相当不安全的想法,但我需要强烈地继续所有这些经验)。我们来看一个模块源码:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <asm/uaccess.h>

MODULE_LICENSE("GPL");

int init_module(void); // driver file initialization as opening it
void cleanup_module(void); // exec files removal ahead of shutting driver file
static int device_open(struct inode *, struct file *); // driver file opening
static int device_release(struct inode *, struct file *); // return of system resource control
static ssize_t device_read(struct file *, char *, size_t, loff_t *); // reading from driver file
static ssize_t device_write(struct file *, const char *, size_t, loff_t *); // writing into driver file

#define SUCCESS 1
#define DEVICE_NAME "sample device"
#define BUF_LEN 80

static int Major; // device's major number
static int Device_Open = 0; // device access counter
static char message[BUF_LEN]; // buffer for both read and write operations
static char *message_ptr;

// list of basic operations executable by driver
static struct file_operations ops = {
        .read = device_read,
        .write = device_write,
        .open = device_open,
        .release = device_release
};

int init_module(void)
{
    Major = register_chrdev(0, DEVICE_NAME, &ops); // major number assignment

    // evaluate whether driver file is accessible
    if(Major < 0) {
        printk(KERN_ALERT "Device registration attempt failed\n");
        return Major;
    }

    return SUCCESS;
}

void cleanup_module(void)
{
    unregister_chrdev(Major, DEVICE_NAME); // cancelling driver registration in file system before exit
    printk(KERN_ALERT "Driver file of /dev/%s c %d 0 has been destroyed\n", DEVICE_NAME, Major);
    return;
}

static int device_open(struct inode * node, struct file * file)
{
    printk(KERN_INFO "Trying access /dev/%s c %d 0\n", DEVICE_NAME, Major);
    static int counter = 0; // access counter initializing

    // file control evaluation
    if(Device_Open)
        return -EBUSY;

    Device_Open++; // increment counter to avert driver's immanent running
    sprintf(message, "This sentence displayed %d times\n", counter++);
    message_ptr = message;

    try_module_get(THIS_MODULE);

    return SUCCESS;
}

static int device_release(struct inode * node, struct file * file)
{
    printk(KERN_INFO "Trying closure of /dev/%s c %d 0\n", DEVICE_NAME, Major);
    Device_Open--; // decrement counter to keep driver file removable as well
    module_put(THIS_MODULE);

    return SUCCESS;
}

static ssize_t device_read(struct file * file, char * ch, size_t num, loff_t * off)
{
    int read_bytes = 0; // output size
    printk(KERN_INFO "Trying read from /dev/%s c %d 0\n", DEVICE_NAME, Major);
    if(*message_ptr == 0)
        return 0;

    // loop-executed reading from file
    while(num && *message_ptr) {
        put_user(*(message_ptr++), ch++);
        num--;
        read_bytes++;
    }

    printk("%d bytes read, %d bytes to be handled", read_bytes, num);

    return read_bytes;
}


// updated stuff    
static ssize_t device_write(struct file *filp, const char *buff, size_t len, loff_t * off) 
{
    char message_from_user[BUF_LEN];    
    if(copy_from_user(message_from_user, buff, len)) return -EINVAL;
    printk(KERN_INFO "length of message:%d message:'%s'", (int)len,   message_from_user);
    return len; 
}

为了测试读/写,我使用以下代码:

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <linux/unistd.h>

extern int errno;

int main()
{
    int fd; // file descriptor id
    size_t cnt = 0; // input / output number of bytes
    size_t cnt_2 = 0;
    char inputBuffer[30] = "Device file is open"; // write operation buffer
    char outputBuffer[50]; // read operation buffer

    printf("Continuing with basics of Linux drivers...\n");

    // evaluate accessibility of driver file
    fd = open("/dev/dev", O_RDWR);
    if(fd == -1) {
        close(fd);
        printf("File opening isn't completed\n");
        return 1;
    }

    printf("Driver file is open now\n");

    // writing from file
    cnt = write(fd, inputBuffer, sizeof(inputBuffer));
    printf("Driver got written %d bytes\n", cnt);

    // read into file
    cnt = read(fd, outputBuffer, sizeof(outputBuffer));
    printf("Driver received %d bytes\n", cnt);

    int i = 0;

    // display an input message
    while(i < cnt) {
        printf("%c", outputBuffer[i]);
        printf("%s", "\n");
        i++;
    }

    close(fd); // wrap up driver connection and clear memory
    printf("Driver file is close\n");
    return 0;
}         

虽然模块是内置的并且开发文件是由 mknod 创建的(我在 Ubuntu 18.04 上运行它),但由于对用户/内核空间中的驱动程序调用的一些误解,我陷入了写操作。一旦我启动我的程序,输出如下:

继续了解 Linux 驱动程序的基础知识...
驱动程序文件现已打开
驱动程序已写入 -1 字节

随着最后一行输出,系统变得无法操作(直到我关闭电脑才响应)。我认为这种情况是内存控制的问题,或者最有可能是一些驱动程序文件属性的问题。然而,用户权限已被授予读/写/执行,实际上没有访问限制。希望能够指出此处发布的代码中的错误。

最佳答案

看到你的代码,你不处理写作部分。

static ssize_t device_write(struct file * file, const char * ch, size_t num, loff_t * off)
{
    printk(KERN_ALERT "Operation denied\n");
    return -EINVAL;
} 

因此您的模块不可能工作。

但是您的崩溃来自读取函数中的内存访问(使用 strace 检查这一点)。我让你了解你的问题。 dmesg 应该会有所帮助(或者如果您的系统出现紧急情况,您可以在重新启动系统后保留日志以进行调试)。

关于c - Linux中如何解决以/dev文件作为内核模块的读写操作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59152880/

相关文章:

c++ - C 编程调试断言错误

c++ - 阻塞流重定向的段错误

c - 为什么两个连续共享内存区域上的 mremap 会导致 SIGBUS?

linux - glibc: elf 文件操作系统 ABI 无效

linux-kernel - Linux USB 小工具自定义配置

c - 绑定(bind)驱动程序如何从从属接口(interface)获取 RX 数据包

linux - 设备驱动程序不工作

c - 为什么我们需要同时初始化或声明两个维度​​而不是在c中一个接一个

c - 2 的整数次幂的二进制表示

linux - 将 skb->data 复制到多个描述符