Как сделать схему связи в драйвере режима ядра Linux и в пользовательском режиме?
Я новичок в этой разработке драйверов Linux. Это моя первая программа, которая делает драйвер ядра. Я пытаюсь написать драйвер режима ядра и приложение пользовательского режима для чтения / записи этой зарезервированной области ядра. Я получил ошибку сегментации. Кто-нибудь может мне помочь?
Дело в том, что структура открытой функции ядра:
static int open(struct inode *inode, struct file *filp)
{
struct mmap_info *info;
char *data;
pr_info("open\n");
info = kmalloc(sizeof(struct mmap_info), GFP_KERNEL);
// allocating memory on the heap for the data
data = kcalloc(MY_MMAP_LEN, sizeof(char), GFP_KERNEL);
if (data == NULL)
{
printk(KERN_ERR "insufficient memory\n");
/* insufficient memory: you must handle this error! */
return ENOMEM;
}
info->data = data;
printk(KERN_INFO " > ->data: 0x%16p\n", info->data);
memcpy(info->data, "Initial entry on mapped memory by the kernel module", 52);
memcpy((info->data) + 0xf00, "Somewhere", 9);
memcpy((info->data) + 0xf000, "Somehow", 7);
printk(KERN_INFO " > ->data: %c%c%c\n", // the output here is correct
*(info->data + 0xf000 + 0), *(info->data + 0xf000 + 1), *(info->data + 0xf000 + 2));
filp->private_data = info;
return 0;
}
Но приложение пользовательского режима не имеет структуры данных filp->private_data->data. Как открыть с помощью правого указателя в приложении пользовательского режима. Вот часть кода открытия файла в приложении пользовательского режима:
fd = open("/dev/ace", O_RDWR);
if(fd < 0)
{
perror("Open call failed");
return -1;
}
address = mmap( NULL,
MY_MMAP_LEN,
PROT_READ|PROT_WRITE,
MAP_SHARED,
fd,
0);
if (address == MAP_FAILED)
{
perror("mmap operation failed");
return -1;
}
sbuff = "This is a ACE demo\n";
//memcpy(address, sbuff, 80);
//usleep(5000);
memcpy(sbuff, address,80);
Вся программа выглядит следующим образом: Исходный код режима ядра:
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h> /* min */
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h> /* copy_from_user, copy_to_user */
#include <linux/slab.h>
#include <linux/device.h>
#define DEV_MODULENAME "ace"
#define DEV_CLASSNAME "aceclass"
static int majorNumber;
static struct class *devClass = NULL;
static struct device *devDevice = NULL;
enum { BUFFER_SIZE = 4 };
struct mmap_info {
char *data;
};
/* After unmap. */
static void vm_close(struct vm_area_struct *vma)
{
pr_info("vm_close\n");
}
/* First page access. */
int (*fault)(struct vm_fault *vmf);
static int vm_fault(struct vm_fault *vmf)
{
struct page *page;
struct mmap_info *info;
pr_info("vm_fault\n");
info = (struct mmap_info *)vmf->vma->vm_private_data;
if (info->data) {
page = virt_to_page(info->data);
get_page(page);
vmf->page = page;
}
return 0;
}
/* Aftr mmap. TODO vs mmap, when can this happen at a different time than mmap? */
static void vm_open(struct vm_area_struct *vma)
{
pr_info("vm_open\n");
}
static struct vm_operations_struct vm_ops =
{
.close = vm_close,
.fault = vm_fault,
.open = vm_open,
};
static int mmap(struct file *filp, struct vm_area_struct *vma)
{
pr_info("mmap\n");
vma->vm_ops = &vm_ops;
vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
vma->vm_private_data = filp->private_data;
vm_open(vma);
return 0;
}
#define MY_MMAP_LEN 0x10000
static int open(struct inode *inode, struct file *filp)
{
struct mmap_info *info;
char *data;
pr_info("open\n");
info = kmalloc(sizeof(struct mmap_info), GFP_KERNEL);
// allocating memory on the heap for the data
data = kcalloc(MY_MMAP_LEN, sizeof(char), GFP_KERNEL);
if (data == NULL)
{
printk(KERN_ERR "insufficient memory\n");
/* insufficient memory: you must handle this error! */
return ENOMEM;
}
info->data = data;
printk(KERN_INFO " > ->data: 0x%16p\n", info->data);
memcpy(info->data, "Initial entry on mapped memory by the kernel module", 52);
memcpy((info->data) + 0xf00, "Somewhere", 9);
memcpy((info->data) + 0xf000, "Somehow", 7);
printk(KERN_INFO " > ->data: %c%c%c\n", // the output here is correct
*(info->data + 0xf000 + 0), *(info->data + 0xf000 + 1), *(info->data + 0xf000 + 2));
filp->private_data = info;
return 0;
}
static ssize_t read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{
struct mmap_info *info;
int ret;
pr_info("read\n");
info = filp->private_data;
ret = min(len, (size_t)BUFFER_SIZE);
if (copy_to_user(buf, info->data, ret)) {
ret = -EFAULT;
}
return ret;
}
static ssize_t write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
{
struct mmap_info *info;
pr_info("write\n");
info = filp->private_data;
if (copy_from_user(info->data, buf, min(len, (size_t)BUFFER_SIZE))) {
return -EFAULT;
} else {
return len;
}
}
static int release(struct inode *inode, struct file *filp)
{
struct mmap_info *info;
pr_info("release\n");
info = filp->private_data;
free_page((unsigned long)info->data);
kfree(info);
filp->private_data = NULL;
return 0;
}
static const struct file_operations fops = {
.mmap = mmap,
.open = open,
.release = release,
.read = read,
.write = write,
};
static int __init
_module_init (void)
{
int ret = 0;
// Try to dynamically allocate a major number for the device
majorNumber = register_chrdev(0, DEV_MODULENAME, &fops);
if (majorNumber < 0)
{
printk(KERN_ALERT "Failed to register a major number.\n");
return -EIO; // I/O error
}
// Register the device class
devClass = class_create(THIS_MODULE, DEV_CLASSNAME);
// Check for error and clean up if there is
if (IS_ERR(devClass))
{
printk(KERN_ALERT "Failed to register device class.\n");
ret = PTR_ERR(devClass);
goto goto_unregister_chrdev;
}
// Create and register the device
devDevice = device_create(devClass,
NULL, MKDEV(majorNumber, 0),
NULL,
DEV_MODULENAME);
// Clean up if there is an error
if (IS_ERR(devDevice))
{
printk(KERN_ALERT "Failed to create the device.\n");
ret = PTR_ERR(devDevice);
goto goto_class_destroy;
}
printk(KERN_INFO "Module registered.\n");
return ret;
// Error handling - using goto
goto_class_destroy: class_destroy(devClass);
goto_unregister_chrdev: unregister_chrdev(majorNumber, DEV_MODULENAME);
return ret;
}
static void __exit
_module_exit(void)
{
device_destroy(devClass, MKDEV(majorNumber, 0));
class_unregister(devClass);
class_destroy(devClass);
unregister_chrdev(majorNumber, DEV_MODULENAME);
printk(KERN_INFO "Module unregistered.\n");
}
module_init(_module_init);
module_exit(_module_exit);
MODULE_LICENSE("GPL");
и вот приложение пользовательского режима:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <time.h>
#include <stdint.h>
#define MY_MMAP_LEN (0x10000)
#define PAGE_SIZE 4096
static char *exec_name;
int main ( int argc, char **argv )
{
int fd;
void *address = NULL;
time_t t = time(NULL);
char *sbuff;
int i;
exec_name = argv[0];
sbuff = (char*) calloc(MY_MMAP_LEN,sizeof(char));
fd = open("/dev/ace", O_RDWR);
if(fd < 0)
{
perror("Open call failed");
return -1;
}
address = mmap( NULL,
MY_MMAP_LEN,
PROT_READ|PROT_WRITE,
MAP_SHARED,
fd,
0);
if (address == MAP_FAILED)
{
perror("mmap operation failed");
return -1;
}
sbuff = "This is a ACE demo\n";
//memcpy(address, sbuff, 80);
//usleep(5000);
memcpy(sbuff, address,80);
printf("Initial message: %s\n", sbuff);
//memcpy(sbuff, address+0xf00,80);
//printf("Initial message: %s\n", sbuff);
//memcpy(sbuff, address+0xf000,80);
//printf("Initial message: %s\n", sbuff);
usleep(5000);
if (munmap(address, MY_MMAP_LEN) == -1)
{
perror("Error un-mmapping the file");
}
close(fd);
return 0;
}