写长到 linux 字符设备驱动程序?
Write long to linux char device driver?
我正在尝试在 linux 中编写字符设备驱动程序。不幸的是,它不适用于任何大于 255 的数字。
我希望此驱动程序专门处理 long
类型的值。每当我输入大于 255 的值时,数字就会出错。 256 变为 0 等
我写了一个简单的字符设备驱动程序来显示问题,可能有很多未使用的包含语句,因为我复制了完整的驱动程序并删除了几乎所有内容:
chartest.c
#include <linux/init.h>
#include <linux/module.h> /* I mean this is a module after all! */
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/sched.h> /* For current task information */
#include <linux/fs.h> /* For file operations */
#include <linux/types.h> /* dev_t: device number data type */
#include <linux/cdev.h> /* cdev is the module data type that the kernel sees */
#include <asm/uaccess.h> /* For routines to copy data to/from user space */
#include <linux/uaccess.h>
#include <linux/slab.h> /* kmalloc/kfree */
MODULE_LICENSE("GPL");
#define DRIVER_NAME "chartest"
#define MAJOR_NUM 230
#define MINOR_NUM 0
struct cdev *cdev;
int test_device_open(struct inode *inode, struct file *fp) {
return 0;
}
int test_device_release(struct inode *inode, struct file *fp) {
return 0;
}
ssize_t test_read(struct file *fp, char __user *buffer, size_t count, loff_t *f_pos) {
return count;
}
ssize_t test_write(struct file *fp, const char __user *buffer, size_t count, loff_t *fpos) {
// We must validate the user's buffer and convert it to a long long
long userOperand;
unsigned char *userInput = NULL;
printk(KERN_NOTICE "Write Function Entered.\n");
printk(KERN_ALERT "Write count: %ld, Write fp: %lld\n", count, *fpos);
userInput = kmalloc(count, GFP_KERNEL);
get_user(*userInput, buffer);
printk(KERN_NOTICE "Value before cast: %ld\n", (long) *userInput);
userOperand = (long) *userInput;
printk(KERN_NOTICE "Value after cast: %ld\n", userOperand);
// Increment the file position pointer (in our case, always by 8)
*fpos += count;
kfree(userInput);
return count;
}
/*
* Declaration of function for open file operations
*/
static struct file_operations test_fops = {
.owner = THIS_MODULE,
.read = test_read,
.write = test_write,
.open = test_device_open,
.release = test_device_release,
};
// Initialization function
static int __init test_init(void)
{
// Register device number:
int err = 0;
dev_t device_number = MKDEV(MAJOR_NUM, MINOR_NUM);
err = register_chrdev_region(device_number, 1, DRIVER_NAME);
if (err < 0) {
printk(KERN_ALERT "Could not allocate device number.\n");
return err;
}
cdev = cdev_alloc();
cdev->owner = THIS_MODULE;
cdev->ops = &test_fops;
err = cdev_add(cdev, device_number, 1);
if (err) {
printk("Error allocating cdev.\n");
}
printk(KERN_ALERT "Test Initialized. Major Number: %d\n", MAJOR_NUM);
return 0;
}
// Exit function:
static void __exit test_exit(void)
{
dev_t device_number = MKDEV(MAJOR_NUM, MINOR_NUM);
// Remove char device */
cdev_del(cdev);
/* Unregister Device Number: */
unregister_chrdev_region(device_number, 1);
printk(KERN_ALERT "TestDriver %d destroyed.\n", MAJOR_NUM);
}
module_init(test_init);
module_exit(test_exit);
小测试程序:
maintest.c:
#include <unistd.h>
#include <fcntl.h>
int main(void) {
long input = 256;
int fd = open("/dev/chartest0", O_RDWR);
write(fd, &input, sizeof(long));
close(fd);
return 0;
}
printk
语句在给定输入 256 的情况下给出以下输出:
Write Eunction Entered.
Write count: 8, Write fp: 0
Value before cast: 0
Value after cast: 0
如果输入大小为 8 字节,copy_from_user
也会失败。当一次一个字节地遍历缓冲区并复制数据时,它也会失败。我什么都试过了。
如果您愿意提供帮助,请编译:
生成文件
ifeq ($(KERNELRELEASE),)
# Assume the source tree is where the running kernel was built
# You should set KERNELDIR in the environment if it's elsewhere
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
# The current directory is passed to sub-makes as argument
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
.PHONY: modules modules_install clean
else
# called from kernel build system: just declare what our modules are
obj-m := chartest.o
endif
然后在同一目录中:
sudo insmod chartest.ko
最后:
sudo mknod -m 777 /dev/chartest0 c 230 0
然后可以编译maindriver.c
和运行来测试
有人可以帮我解决这个问题吗?
你不能像你那样使用get_user
:
来自 get_user doc
This macro copies a single simple variable from user space to kernel space. It supports simple types like char and int, but not larger data types like structures or arrays.
ptr must have pointer-to-simple-variable type, and the result of dereferencing ptr must be assignable to x without a cast.
使用get_user
,您只会复制第一个字符。
你需要用到copy_from_user
,这个函数可以复制数组和结构体,不仅仅是简单的类型:
ssize_t test_write(struct file *fp, const char __user *buffer, size_t count, loff_t *fpos) {
// We must validate the user's buffer and convert it to a long long
long userOperand;
unsigned char *userInput = NULL;
userInput = kmalloc(count, GFP_KERNEL);
printk(KERN_NOTICE "Write Function Entered.\n");
printk(KERN_ALERT "Write count: %ld, Write fp: %lld\n", count, *fpos);
/* warning, here you should test that count is exactly sizeof userInput */
copy_from_user(userInput, buffer, count);
userOperand = *(long*)userInput;
printk(KERN_NOTICE "Value after cast: %ld\n", userOperand);
// Increment the file position pointer (in our case, always by 8)
*fpos += count;
kfree(userInput);
return count;
}
您还可以在 copy_from_user
中从 char *
复制到 long
(在这种情况下没有内存分配):
ssize_t test_write(struct file *fp, const char __user *buffer, size_t count, loff_t *fpos) {
// We must validate the user's buffer and convert it to a long long
long userOperand;
printk(KERN_NOTICE "Write Function Entered.\n");
printk(KERN_ALERT "Write count: %ld, Write fp: %lld\n", count, *fpos);
/* warning, here you should test that count is exactly sizeof userOperand */
copy_from_user(&userOperand, buffer, sizeof userOperand);
printk(KERN_NOTICE "Value after reading: %ld\n", userOperand);
// Increment the file position pointer (in our case, always by 8)
*fpos += count;
return count;
}
您使用宏 get_user
不正确。
它的第一个参数实际上应该是一个变量名,而不是它的地址。
它的第二个参数应该是 typed 指向用户 space 数据的指针。并且该指针的确切类型用于确定要读取的数据的大小。
正确:
long userOperand;
...
get_user(userOperand, (const long __user*)buffer);
printk(KERN_NOTICE "Value written: %ld\n", userOperand);
注意第二个参数的情况:读取 long
而不是 char
需要它(因为 buffer
参数是指向 char
的指针) .
请注意,上面的代码没有使用您的 userInput
变量,因此您不需要定义它,也不需要分配内存。
请注意,可以使用任何 count
参数调用 .write
方法,该参数表示从用户 传递 的字节数。由于 get_user
总是尝试读取预定义的字节数(在您的情况下,该数字等于 long
的大小),check count
等于该数字(或者至少不小于给定数字):
if (count != sizeof(long)) {
return -EINVAL;
}
我正在尝试在 linux 中编写字符设备驱动程序。不幸的是,它不适用于任何大于 255 的数字。
我希望此驱动程序专门处理 long
类型的值。每当我输入大于 255 的值时,数字就会出错。 256 变为 0 等
我写了一个简单的字符设备驱动程序来显示问题,可能有很多未使用的包含语句,因为我复制了完整的驱动程序并删除了几乎所有内容:
chartest.c
#include <linux/init.h>
#include <linux/module.h> /* I mean this is a module after all! */
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/sched.h> /* For current task information */
#include <linux/fs.h> /* For file operations */
#include <linux/types.h> /* dev_t: device number data type */
#include <linux/cdev.h> /* cdev is the module data type that the kernel sees */
#include <asm/uaccess.h> /* For routines to copy data to/from user space */
#include <linux/uaccess.h>
#include <linux/slab.h> /* kmalloc/kfree */
MODULE_LICENSE("GPL");
#define DRIVER_NAME "chartest"
#define MAJOR_NUM 230
#define MINOR_NUM 0
struct cdev *cdev;
int test_device_open(struct inode *inode, struct file *fp) {
return 0;
}
int test_device_release(struct inode *inode, struct file *fp) {
return 0;
}
ssize_t test_read(struct file *fp, char __user *buffer, size_t count, loff_t *f_pos) {
return count;
}
ssize_t test_write(struct file *fp, const char __user *buffer, size_t count, loff_t *fpos) {
// We must validate the user's buffer and convert it to a long long
long userOperand;
unsigned char *userInput = NULL;
printk(KERN_NOTICE "Write Function Entered.\n");
printk(KERN_ALERT "Write count: %ld, Write fp: %lld\n", count, *fpos);
userInput = kmalloc(count, GFP_KERNEL);
get_user(*userInput, buffer);
printk(KERN_NOTICE "Value before cast: %ld\n", (long) *userInput);
userOperand = (long) *userInput;
printk(KERN_NOTICE "Value after cast: %ld\n", userOperand);
// Increment the file position pointer (in our case, always by 8)
*fpos += count;
kfree(userInput);
return count;
}
/*
* Declaration of function for open file operations
*/
static struct file_operations test_fops = {
.owner = THIS_MODULE,
.read = test_read,
.write = test_write,
.open = test_device_open,
.release = test_device_release,
};
// Initialization function
static int __init test_init(void)
{
// Register device number:
int err = 0;
dev_t device_number = MKDEV(MAJOR_NUM, MINOR_NUM);
err = register_chrdev_region(device_number, 1, DRIVER_NAME);
if (err < 0) {
printk(KERN_ALERT "Could not allocate device number.\n");
return err;
}
cdev = cdev_alloc();
cdev->owner = THIS_MODULE;
cdev->ops = &test_fops;
err = cdev_add(cdev, device_number, 1);
if (err) {
printk("Error allocating cdev.\n");
}
printk(KERN_ALERT "Test Initialized. Major Number: %d\n", MAJOR_NUM);
return 0;
}
// Exit function:
static void __exit test_exit(void)
{
dev_t device_number = MKDEV(MAJOR_NUM, MINOR_NUM);
// Remove char device */
cdev_del(cdev);
/* Unregister Device Number: */
unregister_chrdev_region(device_number, 1);
printk(KERN_ALERT "TestDriver %d destroyed.\n", MAJOR_NUM);
}
module_init(test_init);
module_exit(test_exit);
小测试程序:
maintest.c:
#include <unistd.h>
#include <fcntl.h>
int main(void) {
long input = 256;
int fd = open("/dev/chartest0", O_RDWR);
write(fd, &input, sizeof(long));
close(fd);
return 0;
}
printk
语句在给定输入 256 的情况下给出以下输出:
Write Eunction Entered.
Write count: 8, Write fp: 0
Value before cast: 0
Value after cast: 0
如果输入大小为 8 字节,copy_from_user
也会失败。当一次一个字节地遍历缓冲区并复制数据时,它也会失败。我什么都试过了。
如果您愿意提供帮助,请编译: 生成文件
ifeq ($(KERNELRELEASE),)
# Assume the source tree is where the running kernel was built
# You should set KERNELDIR in the environment if it's elsewhere
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
# The current directory is passed to sub-makes as argument
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
.PHONY: modules modules_install clean
else
# called from kernel build system: just declare what our modules are
obj-m := chartest.o
endif
然后在同一目录中:
sudo insmod chartest.ko
最后:
sudo mknod -m 777 /dev/chartest0 c 230 0
然后可以编译maindriver.c
和运行来测试
有人可以帮我解决这个问题吗?
你不能像你那样使用get_user
:
来自 get_user doc
This macro copies a single simple variable from user space to kernel space. It supports simple types like char and int, but not larger data types like structures or arrays.
ptr must have pointer-to-simple-variable type, and the result of dereferencing ptr must be assignable to x without a cast.
使用get_user
,您只会复制第一个字符。
你需要用到copy_from_user
,这个函数可以复制数组和结构体,不仅仅是简单的类型:
ssize_t test_write(struct file *fp, const char __user *buffer, size_t count, loff_t *fpos) {
// We must validate the user's buffer and convert it to a long long
long userOperand;
unsigned char *userInput = NULL;
userInput = kmalloc(count, GFP_KERNEL);
printk(KERN_NOTICE "Write Function Entered.\n");
printk(KERN_ALERT "Write count: %ld, Write fp: %lld\n", count, *fpos);
/* warning, here you should test that count is exactly sizeof userInput */
copy_from_user(userInput, buffer, count);
userOperand = *(long*)userInput;
printk(KERN_NOTICE "Value after cast: %ld\n", userOperand);
// Increment the file position pointer (in our case, always by 8)
*fpos += count;
kfree(userInput);
return count;
}
您还可以在 copy_from_user
中从 char *
复制到 long
(在这种情况下没有内存分配):
ssize_t test_write(struct file *fp, const char __user *buffer, size_t count, loff_t *fpos) {
// We must validate the user's buffer and convert it to a long long
long userOperand;
printk(KERN_NOTICE "Write Function Entered.\n");
printk(KERN_ALERT "Write count: %ld, Write fp: %lld\n", count, *fpos);
/* warning, here you should test that count is exactly sizeof userOperand */
copy_from_user(&userOperand, buffer, sizeof userOperand);
printk(KERN_NOTICE "Value after reading: %ld\n", userOperand);
// Increment the file position pointer (in our case, always by 8)
*fpos += count;
return count;
}
您使用宏 get_user
不正确。
它的第一个参数实际上应该是一个变量名,而不是它的地址。
它的第二个参数应该是 typed 指向用户 space 数据的指针。并且该指针的确切类型用于确定要读取的数据的大小。
正确:
long userOperand;
...
get_user(userOperand, (const long __user*)buffer);
printk(KERN_NOTICE "Value written: %ld\n", userOperand);
注意第二个参数的情况:读取 long
而不是 char
需要它(因为 buffer
参数是指向 char
的指针) .
请注意,上面的代码没有使用您的 userInput
变量,因此您不需要定义它,也不需要分配内存。
请注意,可以使用任何 count
参数调用 .write
方法,该参数表示从用户 传递 的字节数。由于 get_user
总是尝试读取预定义的字节数(在您的情况下,该数字等于 long
的大小),check count
等于该数字(或者至少不小于给定数字):
if (count != sizeof(long)) {
return -EINVAL;
}