linux内核模块编程(五)----应用程序调用字符设备驱动

先给自己打个广告,本人的微信公众号正式上线了,搜索:张笑生的地盘,主要关注嵌入式软件开发,足球等等,希望大家多多关注,有问题可以直接留言给我,一定尽心尽力回答大家的问题
在这里插入图片描述

一 why

字符设备驱动既然不能单独执行,那么他们存在的意义是什么呢?当然是应用程序提供相应的接口API,使其能够控制硬件或者能够访问内核空间的内容。
本文主要以内核空间和应用程序之间的数据交互为例,谈谈如何实现两者之间的数据交换

二 what

我们都知道,一般地,应用程序是不能访问kernel space的,它一般处在0~3G-1的地址空间,而kernel space一般是处在高地址空间,即3G—4G-1地址空间。那么应用程序如何实现和内核之间交换数据呢,主要是两个函数

  1. 内核接收来自应用程序的数据
static __always_inline unsigned long __must_check
copy_from_user(void *to, const void __user *from, unsigned long n)
  1. 内核将数据传给应用程序
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

  1. 编译并且加载内核
make
sudo insmod char_dev.ko
sudo chmod +x /dev/chardev0
  1. 编译应用程序并且运行,显然在应用程序中得到了内核变量的值
gcc app_char_test.c -o app_char_test
./app_char_test

在这里插入图片描述
3. 同时查看一下内核中的打印信息,显然应用程序修改了内核中变量的值
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值