Linux驱动开发笔记(六):用户层与内核层进行数据传递的原理和Demo

若该文为原创文章,转载请注明原文出处

本文章博客地址:https://hpzwl.blog.csdn.net/article/details/135384355

红胖子网络科技博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中...

Linux系统移植和驱动开发专栏

上一篇:《Linux驱动开发笔记(五):驱动连接用户层与内核层的文件操作集原理和Demo

下一篇:敬请期待...

前言

  驱动作为桥梁,用户层调用预定义名称的系统函数与系统内核交互,而用户层与系统层不能直接进行数据传递,进行本篇主要就是理解清楚驱动如何让用户编程来实现与内核的数据交互传递。

温故知新

  • 设备节点是应用层(用户层)与内核层交互;
  • 使用预先的结构体进行操作,如系统open函数对应了驱动中文件操作及的open指针结构体:struct file_operations;
  • 文件操作集结构体,填充结构体对应指针,填充自己使用到的就行了,多余的可以不填充,调用也不会崩溃或返回错误,会返回0;
      

  那么如何将应用层的输入写入进去可用,如何将内核层的数据通过read返回出来,就是本篇学习了。

驱动模板准备

  首先复制之前的testFileOpts的驱动,改个名字为:testFileOpts:

shell 复制代码
cd ~/work/drive/
ls
cp -arf 003_testFileOpts 004_testReadWrite
cd 004_testReadWrite/
make clean
ls
mv testFileOpts.c testReadWrite.c
vi Makefile 
ls

  

  其中修改makefile里面的模块名称(obj-m模块名称),模板准备好了

shell 复制代码
gedit Makefile 

  

  下面基于testReadWrite.c文件进行注册杂项设备,修改.c文件:

shell 复制代码
gedit testReadWrite.c

  

  

c 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>

// int (*open) (struct inode *, struct file *);
int misc_open(struct inode * pInode, struct file * pFile)
{
    printk("int misc_open(struct inode * pInode, struct file * pFile)\n");
    return 0;
}

// int (*release) (struct inode *, struct file *);
int misc_release(struct inode * pInde, struct file * pFile)
{
    printk("int misc_release(struct inode * pInde, struct file * pFile)\n");
    return 0;
}

// ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t misc_read(struct file * pFile, char __user * pUser, size_t size, loff_t *pLofft)
{
    printk("ssize_t misc_read(struct file * pFile, char __user * pUser, size_t size, loff_t *pLofft)\n");
    return 0;
}

// ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t misc_write(struct file * pFile, const char __user * pUser, size_t size, loff_t *pLofft)
{
    printk("ssize_t misc_write(struct file * pFile, const char __user * pUser, size_t size, loff_t *pLofft)\n");
    return 0;
}

struct file_operations misc_fops = {
  .owner = THIS_MODULE,
  .open = misc_open,
  .release = misc_release,
  .read = misc_read,
  .write = misc_write,
};

struct miscdevice misc_dev = {
    .minor = MISC_DYNAMIC_MINOR, // 这个宏是动态分配次设备号,避免冲突
    .name = "register_hongPangZi_testReadWrite", // 设备节点名称
    .fops = &misc_fops,  // 这个变量记住,自己起的,步骤二使用
};

static int registerMiscDev_init(void)
{ 
    int ret;
    // 在内核里面无法使用基础c库printf,需要使用内核库printk
    printk("Hello, I'm hongPangZi, registeraMiscDev_init\n");	
    ret = misc_register(&misc_dev);
    if(ret < 0)
    {
        printk("Failed to misc_register(&misc_dev)\n");	
        return -1;
    } 
    return 0;
}

static void registerMiscDev_exit(void)
{
    misc_deregister(&misc_dev);
    printk("bye-bye!!!\n");
}

MODULE_LICENSE("GPL");
module_init(registerMiscDev_init);
module_exit(registerMiscDev_exit);

概述

  内核层和用户层不能中是不能直接与用户数据交互,需要使用内核函数copy_to_user和copy_from_user。

  在内核中可以使用printk,memset,memcpy,strlen等函数。

内核函数

  头文件是:linux/uaccess.h(我们这是ubuntu,不是arm)

  可以在内核根目录下搜索下:

shell 复制代码
find . -type f -exec grep -l "copy_to_user(void" {} \;

  

  

copy_from_user函数:从用户层复制到内核层

c 复制代码
static __always_inline unsigned long __must_check
copy_from_user(void *to, const void __user *from, unsigned long n)

  简化下:

c 复制代码
static unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)

  参数分别是,复制到的地址(内核空间),从什么地址复制(用户空间),复制长度;

copy_to_user函数:从内核层复制到用户层

c 复制代码
static __always_inline unsigned long __must_check
copy_to_user(void __user *to, const void *from, unsigned long n)

  简化下:

c 复制代码
static unsigned long copy_to_user(void __user *to, const void *from, unsigned long n)

  参数分别是,复制到的地址(用户空间),从什么地址复制(内核空间),复制长度;

杂项设备驱动添加数据传递函数Demo

步骤一:加入头文件和定义static缓存区

  

c 复制代码
#include <linux/uaccess.h>      // Demo_004 add
static char kBuf[256] = {0x00};  // Demo_004 add

步骤二:初始化缓存区

  

c 复制代码
// int (*open) (struct inode *, struct file *);
int misc_open(struct inode * pInode, struct file * pFile)
{
    printk("int misc_open(struct inode * pInode, struct file * pFile)\n");
    memcpy(kBuf, "init kBuf", sizeof("init kBuf"));
    printk("kBuf = %s\n", kBuf); 

    return 0;
}

步骤三:在驱动函数read中,添加从内核层到用户层的函数

  

c 复制代码
// ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t misc_read(struct file * pFile, char __user * pUser, size_t size, loff_t *pLofft)
{
    printk("ssize_t misc_read(struct file * pFile, char __user * pUser, size_t size, loff_t *pLofft)\n");
    if(copy_to_user(pUser, kBuf, strlen(kBuf)) != 0)
    {
        printk("Failed to copy_to_user(pUser, kBuf, strlen(kBuf)\n");
        return -1;
    }
    return 0;
}

步骤四:在驱动函数wirte中,添加从用户层到内核层的函数

  

c 复制代码
// ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t misc_write(struct file * pFile, const char __user * pUser, size_t size, loff_t *pLofft)
{
    printk("ssize_t misc_write(struct file * pFile, const char __user * pUser, size_t size, loff_t *pLofft)\n");
    if(copy_from_user(kBuf, pUser, size) != 0)
    {
        printk("Failed to copy_from_user(kBuf, pUser, size)\n");
        return -1;
    }
    return 0;
}

步骤五:在程序中读取、写入、再读取

  

c 复制代码
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
  int fd = -1;
  char buf[32] = {0};
  int ret = -1;

  const char devPath[] = "/dev/register_hongPangZi_testReadWrite";
  fd = open(devPath, O_RDWR);
  if(fd < 0)
  {
    printf("Failed to open %s\n", devPath);
    return -1;
  }else{
    printf("Succeed to open %s\n", devPath);
  }
  // 读取
  ret = read(fd, buf, sizeof(buf) < 0);
  if(ret < 0)
  {
    printf("Failed to read %s\n", devPath);
    close(fd);
    return 0;
  }else{
    printf("Succeed to read [%s]\n", buf);
  }
  // 修改内容
  memset(buf, 0x00, sizeof(buf));
  memcpy(buf, "Get you content", strlen("Get you content"));
  // 写入
  ret = write(fd, buf, sizeof(buf));
  if(ret < 0)
  {
    printf("Failed to write %s\n", devPath);
    close(fd);
    return 0;
  }else{
    printf("Succeed to write [%s]\n", buf);
  }
  // 读取
  ret = read(fd, buf, sizeof(buf) < 0);
  if(ret < 0)
  {
    printf("Failed to read %s\n", devPath);
    close(fd);
    return 0;
  }else{
    printf("Succeed to read [%s]\n", buf);
  }

  close(fd);

  printf("exit\n");

  fd = -1;


  return 0;
}

步骤六:编译加载驱动

  

shell 复制代码
make
sudo insmod testReadWrite.ko

步骤七:编译程序运行结果

shell 复制代码
gcc test.c
sudo ./a.out

  

  测试结果与预期相同

入坑

入坑一:测试程序读取与预期不同

问题

  

原因

  

解决

  

上一篇:《Linux驱动开发笔记(五):驱动连接用户层与内核层的文件操作集原理和Demo

下一篇:敬请期待...

本文章博客地址:https://hpzwl.blog.csdn.net/article/details/135384355

相关推荐
AlfredZhao6 小时前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户97183563346612 小时前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪14 小时前
linux 拷贝文件或目录到指定的位置
linux
摇滚侠1 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush41 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5201 天前
Linux 11 动态监控指令top
linux
不会C语言的男孩1 天前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言
古城小栈1 天前
Unix 与 Linux 异同小叙
linux·服务器·unix
凡人叶枫1 天前
Effective C++ 条款42:了解 typename 的双重意义
java·linux·服务器·c++
2601_961875241 天前
决战申论100题2026|最新|范文
linux·容器·centos·debian·ssh·fabric·vagrant