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

前言

前边给出了字符设备的框架,内核和用户空间进行交互的时候,离不开数据的交换;内核实现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 可以实现内核和用户空间的数据交换。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值