linux驱动开发之字符设备--内核和用户空间数据的交换(read write)

2019-07-13 00:09发布

class="markdown_views prism-atom-one-light">

前言

前边给出了字符设备的框架,内核和用户空间进行交互的时候,离不开数据的交换;内核实现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 #include #include #include #include #include #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 ",__func__,__LINE__); return 0; } static int cdev_demo_release(struct inode *inode, struct file * file) { printk("%s,%d ",__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 ",__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 ",__func__,__LINE__); printk("buffer=%s size=%d ",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 ",__func__,__LINE__); pdev = cdev_alloc(); if(NULL == pdev){ printk("cdev_alloc failed. "); return -ENOMEM; } cdev_init(pdev,&fops); ret = alloc_chrdev_region(&dev,minor,count,DEVICE_NAME); if(ret){ printk("alloc_chrdev_region failed. "); goto ERROR_CDEV; } major = MAJOR(dev); ret = cdev_add(pdev, dev,count); if(ret) { printk("cdev_add failed. "); 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 ",__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 #include #include #include #include #include #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 "); 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 ",ret,errno); char rbuf[BUFFER_SIZE]; memset(rbuf,0,BUFFER_SIZE); ret = read(fd,rbuf,BUFFER_SIZE); printf("read buf = %s ",rbuf); printf("read ret = %d,errno = %d ",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 可以实现内核和用户空间的数据交换。