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

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

时间:2024-01-04 14:37:50浏览次数:40  
标签:__ struct Demo misc user file Linux size 内核

前言

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

<br>

温故知新

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

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

<br>

驱动模板准备

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

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模块名称),模板准备好了

gedit Makefile 

  在这里插入图片描述

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

gedit testReadWrite.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);

<br>

概述

  内核层和用户层不能中是不能直接与用户数据交互,需要使用内核函数copy_to_user和copy_from_user。   在内核中可以使用printk,memset,memcpy,strlen等函数。

<br>

内核函数

  头文件是:linux/uaccess.h(我们这是ubuntu,不是arm)   可以在内核根目录下搜索下:

find . -type f -exec grep -l "copy_to_user(void" {} \;

  在这里插入图片描述

  在这里插入图片描述

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

static __always_inline unsigned long __must_check
copy_from_user(void *to, const void __user *from, unsigned long n)

  简化下:

static unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)

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

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

static __always_inline unsigned long __must_check
copy_to_user(void __user *to, const void *from, unsigned long n)

  简化下:

static unsigned long copy_to_user(void __user *to, const void *from, unsigned long n)

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

<br>

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

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

  在这里插入图片描述

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

步骤二:初始化缓存区

  在这里插入图片描述

// 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中,添加从内核层到用户层的函数

  在这里插入图片描述

// 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中,添加从用户层到内核层的函数

  在这里插入图片描述

// 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;
}

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

  在这里插入图片描述

#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;
}

步骤六:编译加载驱动

  在这里插入图片描述

make
sudo insmod testReadWrite.ko

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

gcc test.c
sudo ./a.out

  在这里插入图片描述

  测试结果与预期相同

<br>

入坑

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

问题

  在这里插入图片描述

原因

  在这里插入图片描述

解决

  在这里插入图片描述

标签:__,struct,Demo,misc,user,file,Linux,size,内核
From: https://blog.51cto.com/hongpangzi/9100601

相关文章

  • linux下的sql server日常管理
    环境:OS:Centos7DB:sqlserver2017 1.数据存放目录:[root@cdc-henan-cdhworker02data]#pwd/var/opt/mssql/data 2.登录查询(必须输入go):sqlcmd-Slocalhost-Usa-P'Aa123456'1>SELECTNamefromsys.databases;2>go1>select@@VERSION2>go 3.查看是否安......
  • Linux中tcp连接数过多会造成什么影响?
    TCP是Internet协议族中的一种连接方式,在Linux系统中TCP连接数限制是非常重要的参数,不同版本的Linux内核TCP连接数限制的默认值也不同。那么Linux中tcp连接数过多会造成什么影响?具体请看下文。TCP连接数过多可能导致程序挂掉。当一个程序处理大量的TCP连接时,它可能会消耗系......
  • Linux中tcp连接数过多会造成什么影响?
    TCP是Internet协议族中的一种连接方式,在Linux系统中TCP连接数限制是非常重要的参数,不同版本的Linux内核TCP连接数限制的默认值也不同。那么Linux中tcp连接数过多会造成什么影响?具体请看下文。TCP连接数过多可能导致程序挂掉。当一个程序处理大量的TCP连接时,它可能会消耗系......
  • 使用CMakeLists.txt创建一个动态库工程Demo给main程序使用
    主要需求是把hello程序编译动态库,再main程序或者第三方程序执行的时候动态加载。工程目录如下:$ls-al*-rw-r--r--1neuti197609352Oct2019:30CMakeLists.txtinclude:total1drwxr-xr-x1neuti1976090Oct2019:30./drwxr-xr-x1neuti1976090Oct201......
  • Linux软件安装(Ⅰ)
    常见的软件包封装类型有哪些?文件类型保存目录rpm软件包扩展名".rpm"rpmyumdeb软件包扩展名".deb"dpkgapt-get源代码软件包一般为".tar.gz"、".tar.bz2"等格式的压缩包包含程序的原始代码绿色免安装的软件包在压缩包内提供已编译好的执行程序文......
  • Linux 系统启动过程
      原创:厦门微思网络  【微思2002年成立,专业IT认证培训21年!】linux启动时我们会看到许多启动信息。Linux系统的启动过程并不是大家想象中的那么复杂,其过程可以分为5个阶段:内核的引导。运行init。系统初始化。建立终端。用户登录系统。init程序的类型:SysV: init,CentOS5之......
  • Cisco Secure Client 5.0.03072 (macOS, Linux, Windows & iOS, Andrord)
    CiscoSecureClient5.0.03072(macOS,Linux,Windows&iOS,Andrord)思科安全客户端(包括AnyConnect)作者主页:sysin.orgCiscoSecureClient(includingAnyConnect)思科安全客户端(包括AnyConnect)安全访问只是开始您的团队需要轻松访问公司资源和私有应用程序。您需要确保您的......
  • Cisco AnyConnect Secure Mobility Client 4.10.07062 (macOS, Linux, Windows)
    CiscoAnyConnectSecureMobilityClient4.10.07062(macOS,Linux,Windows)CiscoSecureClient(包括AnyConnect)作者主页:sysin.org新版已发布:CiscoSecureClient5.0.03072(macOS,Linux,Windows&iOS,Andrord)CiscoSecureClient(includingAnyConnect)思科安全客户端(......
  • 如何禁止 Firefox 自动更新 (macOS, Linux, Windows)
    作者主页:sysin.org如何禁用MozillaFireFox自动更新如何禁用MicrosoftEdge自动更新如何禁用GoogleChrome自动更新FirefoxformacOS方法一:配置策略文件获取最新或者对应版本的策略模板:https://github.com/mozilla/policy-templates/releases自动更新策略描述如下:{"polic......
  • JetBrains GoLand 2023.2 (macOS, Linux, Windows) 发布 - Go Full Stack
    JetBrainsGoLand2023.2(macOS,Linux,Windows)发布-GoFullStack作者主页:sysin.orgJetBrainsGoLand-GoFullStackGo语言全栈IDE,对JavaScript、TypeScript和数据库提供扩展支持为何选择GoLand强大的代码洞察GoLand使读取、写入和更改Go代码变得非常容易实时错误......