先给自己打个广告,本人的微信公众号正式上线了,搜索:张笑生的地盘,主要关注嵌入式软件开发,足球等等,希望大家多多关注,有问题可以直接留言给我,一定尽心尽力回答大家的问题
一 why
字符设备驱动既然不能单独执行,那么他们存在的意义是什么呢?当然是应用程序提供相应的接口API,使其能够控制硬件或者能够访问内核空间的内容。
本文主要以内核空间和应用程序之间的数据交互为例,谈谈如何实现两者之间的数据交换
二 what
我们都知道,一般地,应用程序是不能访问kernel space的,它一般处在0~3G-1的地址空间,而kernel space一般是处在高地址空间,即3G—4G-1地址空间。那么应用程序如何实现和内核之间交换数据呢,主要是两个函数
- 内核接收来自应用程序的数据
static __always_inline unsigned long __must_check
copy_from_user(void *to, const void __user *from, unsigned long n)
- 内核将数据传给应用程序
static __always_inline unsigned long __must_check
copy_to_user(void __user *to, const void *from, unsigned long n)
三 how
显然,上面的两个函数是在字符设备驱动中调用的,因此,我们先看驱动中的代码示例,主要是两个函数,分别是chardev_drv_read和chardev_drv_write
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/uaccess.h>
static int major;
static struct class *chardev_class;
static struct device *chardrv_class_dev;
static int kernel_value1 = 555;
static int kernel_value2;
static int chardev_drv_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "chardev_drv_open\n");
return 0;
}
static ssize_t chardev_drv_read(struct file *filp, char __user *buf,
size_t size, loff_t *ppos)
{
int ret = 0;
printk(KERN_INFO "chardev_drv_read\n");
ret = copy_to_user(buf, &kernel_value1, size);
if (ret > 0) {
printk(KERN_ERR "copy_to_user error\n");
return -EINVAL;
}
return 0;
}
static ssize_t chardev_drv_write(struct file *filp, const char __user *buf,
size_t size, loff_t *ppos)
{
int ret = 0;
printk(KERN_INFO "chardev_drv_write\n");
ret = copy_from_user(&kernel_value2, buf, size);
if (ret > 0) {
printk(KERN_ERR "copy_from_user error\n");
return -EINVAL;
}
printk("kernel_value2 = %d", kernel_value2);
return 0;
}
int chardev_drv_close(struct inode *inode, struct file *file)
{
printk(KERN_INFO "chardev_drv_close\n");
return 0;
}
static struct file_operations chardev_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = chardev_drv_open,
.read = chardev_drv_read,
.write = chardev_drv_write,
.release = chardev_drv_close,
};
static int __init chardev_drv_init(void)
{
printk(KERN_INFO "already chardev_drv_init\n");
major = register_chrdev(0, "chardev0", &chardev_drv_fops);
chardev_class = class_create(THIS_MODULE, "chardev0");
chardrv_class_dev = device_create(chardev_class, NULL, MKDEV(major, 0), NULL, "chardev0"); /* /dev/xyz */
return 0;
}
static void __exit chardev_drv_exit(void)
{
printk(KERN_INFO "new exit chardev0\n");
unregister_chrdev(major, "chardev0");
device_unregister(chardrv_class_dev);
class_destroy(chardev_class);
}
module_init(chardev_drv_init);
module_exit(chardev_drv_exit);
MODULE_LICENSE("GPL");
同样地,我们需要在应用程序,通过调用read和write函数来控制内核空间中的变量,如下
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
/* thirddrvtest
*/
int main(int argc, char **argv)
{
int fd;
unsigned int key_val = 0;
fd = open("/dev/chardev0", O_RDWR, S_IRUSR|S_IWUSR);
if (fd < 0)
{
perror("can't open!");
return -1;
}
read(fd, &key_val, 4);
printf("__USER__ key_val = %d\n", key_val);
key_val = 666;
write(fd, &key_val, 4);
close(fd);
return 0;
}
四 Test
- 编译并且加载内核
make
sudo insmod char_dev.ko
sudo chmod +x /dev/chardev0
- 编译应用程序并且运行,显然在应用程序中得到了内核变量的值
gcc app_char_test.c -o app_char_test
./app_char_test
3. 同时查看一下内核中的打印信息,显然应用程序修改了内核中变量的值