Я пытаюсь написать модуль ядра простого символьного устройства, который выделяет 100 байт памяти в пространстве ядра и имеет функции чтения, записи и поиска, которые работают с выделенными 100 байтами памяти ядра. Я попытался отобразить текст «Hello World» в соответствующем файле устройства, но получил ошибку «Недопустимый аргумент» и предупреждение о dmesg.
Файл драйвера устройства c с именем "kernelbuffer.c"
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <linux/seq_file.h>
#include <linux/cdev.h>
#include <asm/uaccess.h> /* copy_*_user */
static int major;
struct buffer_dev{
char data[100];
struct semaphore sem;
struct cdev cdev;
};
struct buffer_dev *buff_device;
static void buffer_open(struct inode *inode, struct file *filep){
struct buffer_dev *dev;
printk(KERN_NOTICE "Opening buffer.\n");
dev = container_of(inode->i_cdev, struct buffer_dev, cdev);
filep->private_data = dev;
printk(KERN_NOTICE "Buffer opened.\n");
return 0;
}
ssize_t buffer_read(struct file *filep, char __user *buf, size_t count,
loff_t *f_pos)
{
printk(KERN_NOTICE "Reading from buffer.\n");
struct buffer_dev *dev = filep->private_data;
ssize_t retval = 0;
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
if (*f_pos >= 100)
goto out;
if (*f_pos + count > 100)
count = 100 - *f_pos;
printk(KERN_NOTICE "First stage fine.\n");
if(copy_to_user(buf, dev->data + *f_pos, count)){
retval = -EFAULT;
goto out;
}
*f_pos += count;
retval = count;
out:
up(&dev->sem);
return retval;
}
ssize_t buffer_write(struct file *filep, char __user *buf, size_t count,
loff_t *f_pos)
{
printk(KERN_NOTICE "Writing to buffer.\n");
struct buffer_dev *dev = filep->private_data;
ssize_t retval = -ENOMEM; /* value used in "goto out" statements */
if (down_interruptible(&dev->sem))
return -ERESTARTSYS;
if (*f_pos >= 100)
goto out;
if (*f_pos + count > 100)
count = 100 - *f_pos;
if (copy_from_user(dev->data + *f_pos, buf, count)) {
retval = -EFAULT;
goto out;
}
printk(KERN_NOTICE "Write success.\n");
*f_pos += count;
retval = count;
out:
up(&dev->sem);
return retval;
}
loff_t buffer_llseek(struct file *filp, loff_t off, int whence)
{
struct buffer_dev *dev = filp->private_data;
loff_t newpos;
switch(whence) {
case 0: /* SEEK_SET */
newpos = off;
break;
case 1: /* SEEK_CUR */
newpos = filp->f_pos + off;
break;
case 2: /* SEEK_END */
newpos = 100 + off;
break;
default: /* can't happen */
return -EINVAL;
}
if (newpos < 0 || newpos >= 100) return -EINVAL;
filp->f_pos = newpos;
return newpos;
}
int buffer_release(struct inode *inode, struct file *filp)
{
return 0;
}
static struct file_operations buffer_fops = {
.owner = THIS_MODULE,
.llseek = buffer_llseek,
.read = buffer_read,
.write = buffer_write,
.open = buffer_open,
.release = buffer_release,
};
static void setup_cdev(struct buffer_dev *dev){
int err, devno = MKDEV(major, 0);
cdev_init(&dev->cdev, &buffer_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &buffer_fops;
err = cdev_add (&dev->cdev, devno, 1);
if (err)
printk(KERN_NOTICE "Error %d adding device\n", err);
}
void cleanup_module_buffer(void)
{
printk(KERN_NOTICE "Removing buffer device.\n");
dev_t devno = MKDEV(major, 0);
if(buff_device){
cdev_del(&buff_device->cdev);
kfree(buff_device);
}
unregister_chrdev_region(devno,1);
}
int module_init_buffer(void)
{
printk(KERN_NOTICE "Initializing buffer device.\n");
int result, i;
dev_t dev = 0;
result = alloc_chrdev_region(&dev, 0, 1,
"buffer");
major = MAJOR(dev);
if (result < 0) {
printk(KERN_WARNING "Can't get major %d\n", major);
return result;
}
buff_device = kmalloc(sizeof(struct buffer_dev), GFP_KERNEL);
if (!buff_device) {
result = -ENOMEM;
goto fail; /* Make this more graceful */
}
memset(buff_device, 0, sizeof(struct buffer_dev));
sema_init(&(buff_device->sem), 1);
setup_cdev(&buff_device);
return 0; /* succeed */
fail:
cleanup_module_buffer();
return result;
}
module_init(module_init_buffer);
module_exit(cleanup_module_buffer);
MODULE_AUTHOR("Gautam Ramakrishnan");
MODULE_LICENSE("GPL");
Makefile, используемый для его компиляции:
obj-m := kernelbuffer.o
KDIR := /lib/modules/$(shell uname -r)/build
all: $(MAKE) -C $(KDIR) M=$(shell pwd) modules
clean:
$(MAKE) -C $(KDIR) M=$(shell pwd) clean
$(RM) Module.markers modules.order
Я успешно запустил insmod для файла kernelbuffer.ko.
На ходу cat /proc/devices
он появляется с большим числом 247.
Затем я выполнил sudo mknod /dev/kbuffer c 247 0
, чтобы создать соответствующий файл драйвера.
При запуске echo "Hello World > /dev/kbuffer
в качестве суперпользователя я получаю ответ bash: /dev/kbuffer: Invalid argument
При запуске dmesg я получаю следующий вывод:
[ 583.814364] Opening buffer.
[ 583.814367] Buffer opened.
[ 583.814369] ------------[ cut here ]------------
[ 583.814392] WARNING: CPU: 0 PID: 2673 at /build/linux-dcxD3m/linux-4.4.0/fs/namei.c:3206 path_openat+0xb19/0x1330()
[ 583.814393] Modules linked in: kernelbuffer(OE) nls_utf8 isofs vboxsf(OE) crct10dif_pclmul crc32_pclmul snd_intel8x0 snd_ac97_codec ac97_bus aesni_intel joydev aes_x86_64 snd_pcm lrw gf128mul snd_seq_midi glue_helper ablk_helper snd_seq_midi_event input_leds vboxvideo(OE) cryptd snd_rawmidi ttm snd_seq serio_raw snd_seq_device drm_kms_helper snd_timer drm snd soundcore fb_sys_fops syscopyarea sysfillrect 8250_fintek sysimgblt i2c_piix4 vboxguest(OE) mac_hid parport_pc ppdev lp parport autofs4 hid_generic usbhid hid psmouse ahci libahci e1000 pata_acpi fjes video
[ 583.814427] CPU: 0 PID: 2673 Comm: bash Tainted: G OE 4.4.0-31-generic #50-Ubuntu
[ 583.814429] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
[ 583.814430] 0000000000000286 00000000c588cd8a ffff8800593ebcb8 ffffffff813f1143
[ 583.814433] 0000000000000000 ffffffff81cd8430 ffff8800593ebcf0 ffffffff81081102
[ 583.814435] ffff8800593ebdd0 0000000000008241 ffff8800593ebef4 00000000ffffffea
[ 583.814437] Call Trace:
[ 583.814442] [<ffffffff813f1143>] dump_stack+0x63/0x90
[ 583.814445] [<ffffffff81081102>] warn_slowpath_common+0x82/0xc0
[ 583.814447] [<ffffffff8108124a>] warn_slowpath_null+0x1a/0x20
[ 583.814449] [<ffffffff8121bfb9>] path_openat+0xb19/0x1330
[ 583.814452] [<ffffffff814f0a41>] ? copy_termios+0x71/0x80
[ 583.814455] [<ffffffff8121d9c1>] do_filp_open+0x91/0x100
[ 583.814457] [<ffffffff810ca961>] ? __raw_callee_save___pv_queued_spin_unlock+0x11/0x20
[ 583.814459] [<ffffffff8122b2d8>] ? __alloc_fd+0xc8/0x190
[ 583.814462] [<ffffffff8120c248>] do_sys_open+0x138/0x2a0
[ 583.814464] [<ffffffff81092230>] ? SyS_rt_sigaction+0xa0/0xd0
[ 583.814466] [<ffffffff8120c3ce>] SyS_open+0x1e/0x20
[ 583.814469] [<ffffffff8182db32>] entry_SYSCALL_64_fastpath+0x16/0x71
[ 583.814470] ---[ end trace ad5061abc2861b8c ]---
Мне не удалось найти решение этой проблемы. Было бы очень полезно, если бы кто-нибудь мог указать причину, по которой мой код не работает, и любые исправления, если это возможно.