前言
前边给出了字符设备的框架,内核和用户空间进行交互的时候,离不开数据的交换;内核实现read、wriet 、ioctl是常用的交互手段。
正文
内核API
函数操作集file_operations 的成员 read、write、unlocked_ioctl,就进行着数据的交换
ssize_t (*read) (struct file * filp, char __user * buffer, size_t size, loff_t * loff);
设备驱动的read 函数,filp是文件结构体指针,buffer是用户空间的内存地址,size 是要读的长度,loff是读的位置相对于文件开头的偏移。
ssize_t (*write) (struct file *filp, const char __user * buffer, size_t size ,loff_t * loff);
设备驱动的write函数,filp是文件结构体指针,buffer是用户空间的内存地址,size 是要写的长度,loff表示写的位置相对于开头的偏移。
内核空间和用户空间的内存是不能直接进行访问的,需要借助 copy_from_user 完成用户空间数据到 内核空间的拷贝。 copy_to_user 完成内核空间到用户空间的拷贝。
函数原型:
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)
unsigned long copy_to_user(void *to, const void __user *from, unsigned long n)
用户空间的数据使用 __user进行修饰。函数返回不能被复制的字节数,成功返回0。
如果是简单的数据类型,比如int、char、long等,可以使用put_user、get_user
int val;
get_user(val,(int*)arg);// 用户-->内核 arg是用户空间的地址
put_user(val,(int*)arg);//内核 -->用户 arg是用户空间的地址
驱动程序
功能:write函数将用户传递的数据copy到kbuf中;read 函数将内核kbuf的数据返回给用户空间;使用buf_count解决kbuf中没有数据,却要read的问题;
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#define DEVICE_NAME "cdev_demo"
static struct cdev *pdev = NULL;
static int major = 0;
static int minor = 0;
static int count = 2;
#define BUF_SIZE (1024)
static char kbuf[BUF_SIZE];
static int buf_count = 0;
static int cdev_demo_open(struct inode * inode, struct file * file)
{
printk("%s,%d\n",__func__,__LINE__);
return 0;
}
static int cdev_demo_release(struct inode *inode, struct file * file)
{
printk("%s,%d\n",__func__,__LINE__);
return 0;
}
static ssize_t cdev_demo_read(struct file * file, char __user * buffer, size_t size, loff_t * loff)
{
printk("%s,%d\n",__func__,__LINE__);
if(0 == buf_count){
return -EAGAIN;
}
if(buf_count < size){
size = buf_count;
}
if(size == copy_to_user(buffer,kbuf,size)){
return -EAGAIN;
}
buf_count = 0;
return size;
}
static ssize_t cdev_demo_write(struct file * file, const char __user * buffer, size_t size, loff_t * loff)
{
printk("%s,%d\n",__func__,__LINE__);
printk("buffer=%s size=%d\n",buffer,size);
if(size >BUF_SIZE){
return -ENOMEM;
}
if(size == copy_from_user(kbuf,buffer,size)){
return -EAGAIN;
}
buf_count = size;
return size;
}
static struct file_operations fops ={
.owner = THIS_MODULE,
.open = cdev_demo_open,
.release = cdev_demo_release,
.read = cdev_demo_read,
.write = cdev_demo_write,
};
static int __init cdev_demo_init(void)
{
dev_t dev;
int ret;
printk("%s,%d\n",__func__,__LINE__);
pdev = cdev_alloc();
if(NULL == pdev){
printk("cdev_alloc failed.\n");
return -ENOMEM;
}
cdev_init(pdev,&fops);
ret = alloc_chrdev_region(&dev,minor,count,DEVICE_NAME);
if(ret){
printk("alloc_chrdev_region failed.\n");
goto ERROR_CDEV;
}
major = MAJOR(dev);
ret = cdev_add(pdev, dev,count);
if(ret) {
printk("cdev_add failed.\n");
goto ERROR_ADD;
}
return 0;
ERROR_ADD:
unregister_chrdev_region(dev,count);
ERROR_CDEV:
cdev_del(pdev);
return ret;
}
static void __exit cdev_demo_exit(void)
{
printk("%s,%d\n",__func__,__LINE__);
unregister_chrdev_region(MKDEV(major,minor),count);
cdev_del(pdev);
}
module_init(cdev_demo_init);
module_exit(cdev_demo_exit);
MODULE_LICENSE("GPL");
测试程序
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#define BUFFER_SIZE (1024)
int main(int argc,char* argv[])
{
char *dev = "/dev/cdev_demo";
int fd = open(dev,O_RDWR);
if(0 > fd){
perror("open\n");
return -1;
}
printf("open %s ",dev);
char buf[] = "string for read and write";
int ret = write(fd,buf,sizeof(buf));
printf(" write ret = %d,errno = %d\n",ret,errno);
char rbuf[BUFFER_SIZE];
memset(rbuf,0,BUFFER_SIZE);
ret = read(fd,rbuf,BUFFER_SIZE);
printf("read buf = %s \n",rbuf);
printf("read ret = %d,errno = %d\n",ret,errno);
close(fd);
return 0;
}
测试结果
[ 619.400454] cdev_demo_open,24
[ 619.400630] cdev_demo_write,56
[ 619.400678] buffer=string for read and write size=26
[ 619.400791] cdev_demo_read,34
[ 619.400860] cdev_demo_release,29
open /dev/cdev_demo write ret = 26,errno = 0
read buf = string for read and write
read ret = 26,errno = 0
总结
使用copy_to_user、copy_from_user、get_user、put_use 可以实现内核和用户空间的数据交换。