首页 > 系统相关 >Linux驱动开发笔记(五):驱动连接用户层与内核层的文件操作集原理和Demo

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

时间:2023-11-28 13:01:56浏览次数:46  
标签:struct int Demo loff misc file Linux 驱动 open

前言

  驱动写好后,用户层使用系统函数调用操作相关驱动从而实现与系统内核的关联,本篇主要就是理解清楚驱动如何让用户编程来实现与内核的交互。

<br>

杂项设备文件操作集

cd /usr/src/linux-headers-4.18.0-15
vi include/linux/fs.h

  搜索到(vi则直接使用“/”):   

struct file_operations {
        struct module *owner;
        loff_t (*llseek) (struct file *, loff_t, int);
        ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
        ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
        ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
        ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
        int (*iterate) (struct file *, struct dir_context *);
        int (*iterate_shared) (struct file *, struct dir_context *);
        __poll_t (*poll) (struct file *, struct poll_table_struct *);
        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
        long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
        int (*mmap) (struct file *, struct vm_area_struct *);
        unsigned long mmap_supported_flags;
        int (*open) (struct inode *, struct file *);
        int (*flush) (struct file *, fl_owner_t id);
        int (*release) (struct inode *, struct file *);
        int (*fsync) (struct file *, loff_t, loff_t, int datasync);
        int (*fasync) (int, struct file *, int);
        int (*lock) (struct file *, int, struct file_lock *);
        ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
        unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
        int (*check_flags)(int);
        int (*setfl)(struct file *, unsigned long);
        int (*flock) (struct file *, int, struct file_lock *);
        ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
        ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
        ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
                        loff_t, size_t, unsigned int);
        int (*setlease)(struct file *, long, struct file_lock **, void **);
        long (*fallocate)(struct file *file, int mode, loff_t offset,
                          loff_t len);
        void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
        unsigned (*mmap_capabilities)(struct file *);
#endif

        int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t,
                        u64);
        ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *,
                        u64);
} __randomize_layout;

  例如read函数,那么就是打开驱动使用系统read,打开这个设备驱动的句柄,那么就会调用read函数,其他的以此类推,还比较好理解。

<br>

Linux文件操作集的意义

概述

  Linux一切都是文件,都有对应的打开、关闭和读写等相关操作,而这些操作都是使用打开文件后的句柄来表示,那么函数再根据句柄的类型,如打开的是杂项设备驱动,就会去调用杂项设备操作文件字符集里面对应的函数来执行操作了。   在编程的时候会使用open打开一个设备节点(可以是文件打开,可以打开设备),这时候返回得到设备节点句柄标识fd(失败是-1),然后使用fd去read、write等各种操作则会相当于调用这个设备驱动里面文件操作集的read、write。   下面是常用的文件操作。

open函数(实现测试)

int (*open) (struct inode *, struct file *);

read函数(实现测试)

ssize_t (*read) (struct file *, char __user *, size_t, loff_t *)

write函数(实现测试)

ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

poll/select函数(本篇没写)

__poll_t (*poll) (struct file *, struct poll_table_struct *);

ioctl函数(本篇没写)

long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

close函数(实现测试)

int (*release) (struct inode *, struct file *);

<br>

驱动模板准备

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

cd ~/work/drive
cp -arf registerMiscDev testFileOpts
cd testFileOpts
make clean
mv registerMiscDev testFileOpts.c

  在这里插入图片描述

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

gedit Makefile  

  在这里插入图片描述

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

gedit testFileOpts.c

  在这里插入图片描述

#include <linux/init.h>
#include <linux/module.h>

#include <linux/miscdevice.h>
#include <linux/fs.h>

struct file_operations misc_fops = {
  .owner = THIS_MODULE,
};

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

static int registerMiscDev_init(void)
{ 
    int ret;
    // 在内核里面无法使用基础c库printf,需要使用内核库printk
    printk("Hello, I’m hongPangZi, registerMiscDev_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>

杂项设备添加常用操作集open函数Demo

  注意,要是调用的函数没有写,则不会报错也不会有其他操作反应,所以并不是所有函数都是必须写的。

步骤一:实现open函数

// 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)");
    return 0;
}

  在这里插入图片描述

步骤二:(关键)赋值到文件操作集指针

  在这里插入图片描述

步骤三:编译加载驱动

  先编译试试:   在这里插入图片描述

  然后加载驱动:

sudo insmod tesFileOpts.ko

  在这里插入图片描述

  在这里插入图片描述

  在这里插入图片描述

  这时候,设备节点注册成功了。

步骤四:在程序中调用打开设备节点open

  本步骤是c语言编程,使用linux系统函数打开设备节点:   新建文件:

vi test.c

  输入代码:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
  int fd;
  const char devPath[] = "/dev/register_hongPangZi_testFileOpt";
  fd = open(devPath, O_RDWR);
  if(fd < 0)
  {
    printf("fialed to open %s\n", devPath);
    return -1;
  } else{
    printf("Succeed to open %s\n", devPath);
  }
  return 0;
}

  在这里插入图片描述

  编译:

gcc test.c

  在这里插入图片描述

  默认输出就是a.out,下面运行一下:   在这里插入图片描述

  无法运行,是因为ubuntu对设备需要管理员权限,管理员权限运行:   在这里插入图片描述

  查看内核打印输出(这里出现没有打印输出,查看“入坑一”):   在这里插入图片描述

  至此,从用户编程层如何对设备结点,然后调用到内核层函数就基本清楚了。

<br>

补充其他函数Demo

补充read、write

#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_testFileOpt", // 设备节点名称
    .fops = &misc_fops,  // 这个变量记住,自己起的,步骤二使用
};

static int registerMiscDev_init(void)
{ 
    int ret;
    // 在内核里面无法使用基础c库printf,需要使用内核库printk
    printk("Hello, I’m hongPangZi, registerMiscDev_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);

修改test.c测试驱动源码

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

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

  const char devPath[] = "/dev/register_hongPangZi_testFileOpt";
  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);
  }

  read(fd, buf, sizeof(buf));
  write(fd, buf, sizeof(buf));

  close(fd);
  printf("exit\n");
  fd = -1;
  return 0;
}

查看输出

  在这里插入图片描述

<br>

入坑

入坑一:内核未打印open函数

问题

  程序打开设别节点,未打印open函数

原因

  打开函数没有赋值给文件操作集。

解决

  在这里插入图片描述

入坑二:dmesg少了close的release打印

问题

  在这里插入图片描述

测试

  在这里插入图片描述

  在这里插入图片描述

  研究了dmesg,就是没出来,这个不清楚了,后来问了驱动大佬,提醒是可能是换行的问题,后加上可以了。

解决方法

  在这里插入图片描述   在这里插入图片描述

标签:struct,int,Demo,loff,misc,file,Linux,驱动,open
From: https://blog.51cto.com/hongpangzi/8599584

相关文章

  • Linux 高级Shell脚本与用户管理(linux文件夹备份脚本)
    本文,我们将要学习Linux高级Shell脚本以及用户管理(重点)。下面,我们将开始探索几个使用的Shell脚本,然后介绍Linux中的用户管理。Shell脚本1创建动态目录首先,我们创建一个名为 create_directories.sh的脚本文件,这个将本将用于生成指定数量且具有动态名称的目录。以下是脚本......
  • linux的期末总结
    简介Linux是目前最流行的操作系统之一,在服务器、桌面、移动等领域都有广泛的应用。为了深入了解Linux内核的运行机制,我参加了学校的Linux内核分析课程。通过八周的学习,我对Linux内核有了比较深入的了解,并在以下几个方面取得了进步:掌握了Linux内核的基本概念和结构理解......
  • Linux 03
    第三章用户管理本章学习目标:1.了解用户和组的意义2.掌握创建、修改与删除用户/组的方法3.熟悉用户相关配置文件4.熟悉用户身份切换与提权一、用户/组概览Linux系统是多用户、多任务的分时操作系统,系统上每一个进程都有一个特定的文件,每个文件都被一个特定的所拥有。如果需......
  • Linux课堂知识总结
    这是学习Linux的第五节课,老师跟我们讲述了Linux的进程管理操作。程序(program)是一个普通文件,是为了完成特定任务而准备好的指令序列与数据的集合,这些指令和数据以“可执行映像”的格式保存在磁盘中。例如:hello.c源程序文件经过编译后产生a.out程序,其中a.out文件为可执行镜像格式......
  • Linux课堂知识总结
    这是学习Linux的第六节课,老师跟我们讲述了Linux的i/o重定向操作。cat:合并文件。sort:对文本行排序。uniq:报告或删除文件中重复的行。wc:打印文件中的换行符、字和字节的个数。grep:打印匹配行。head:输出文件的第一部分内容。tail:输出文件的最后一部分内容。tee:读取标准输入的数据,并......
  • 学习linux的好处
    1.引言Linux操作系统是一种开源、免费的操作系统,广泛应用于服务器、嵌入式系统和个人计算机等领域。学习Linux不仅仅是获取一项技能,更是一种对计算机科学世界的深入理解。本报告将探讨学习Linux的各种好处,从技术、职业和学术角度分析其价值。2.技术好处2.1开源理念Lin......
  • nodemon学习(一)简介、安装、配置、使用
    简介nodemon用来监视node.js应用程序中的任何更改并自动重启服务,非常适合用在开发环境中。以前,我们开发一个node后端服务时,每次更改文件,均需重启一下,服务才能生效。这使我们的开发效率降低了很多。nodemon的出现,可以随时监听文件的变更,自动重启服务,我们开发时只需关注代码即......
  • Linux--存储管理
    存储管理:硬盘在分区之后,系统通过分区表来管理硬盘的使用。存储方式(连接方式层面与工作原理层面)连接方式:存储分为以下3种类型。本地存储:直接插在服务器上的硬盘,系统文件存放在本地。本章主要介绍本地存储。外部存储:可以理解为平时使用的移动硬盘,不过移动硬盘用的是USB接口......
  • linux 文件共享盘以及备份机制
    NFS minio  glusterFile 1.NFSNFS服务器搭建  目录NFS服务器1、NFS简介2、NFS工作机制2.1.RPC3、/etc/exports文件的格式4、实操1)手动挂载2)自动挂载autofs NFS服务器1、NFS简介NFS(NetworkFileSystem)即网络文件系统,是FreeBSD支持的文件......
  • # linux系统读写速度检测
    前言记录一下在Linux下测试硬盘读写速度的命令和方法。测试的工具是linux系统自带的dd命令工具+time命令工具正文dd命令介绍dd命令是一个功能强大的Linux命令,用于复制文件、转换格式和计算文件和设备的吞吐量。它的基本语法如下:dd[选线]以下是dd命令中常用的选项:if=<输入......