一个简单的Linux内核字符驱动程序编写

news2024/11/19 17:46:04

一、背景

为了了解设备驱动程序的框架,在此编写一个简单的字符驱动程序,以此来对驱动程序的框架进行一个简单的了解。

二、设备驱动程序

所谓设备驱动程序,其实就是计算机硬件与外部设备进行通信的接口。由于硬件设备各式各样,有了设备驱动程序,应用程序就可以不用在意设备的具体细节,而方便地与外部设备进行通信。从外部设备读取数据,或是将数据写入外部设备,即对设备进行控制。

三、设备驱动程序框架

设备的种类繁多是可想而知的,所以设备的驱动程序也是各式各样的。由此需要建立一个统一的规范:SVR4(Unix System V Rlease 4)提出了DDI/DKI(Driver-Device Interface/Driver-Kernel Interface)规范。这个SVR4是UNIX操作系统的一种内核标准。

规范分为以下三个部分:

  • 1、驱动程序与内核的接口
  • 2、驱动程序与设备的接口
  • 3、驱动程序与系统引导的接口

其中,驱动程序与内核的接口是通过数据结构file_opration完成的。驱动程序与设备的接口描述了驱动程序如何与设备交互,这与具体的设备是密切相关的。驱动程序与系统引导的接口其实就是驱动程序对设备进行初始化。

四、简单的字符驱动程序

我在这里直接将我编写的字符驱动程序展示出来,然后对其进行分析:

#include<linux/init.h>
#include<linux/module.h>
#include<linux/types.h>
#include<linux/fs.h>
#include<linux/mm.h>
#include<linux/sched.h>
#include<linux/cdev.h>
#include<asm/io.h>
#include<asm/switch_to.h>
#include<asm/uaccess.h>
#include<linux/kernel.h>

MODULE_LICENSE("GPL");

#define MYCDEV_MAJOR 231
#define MYCDEV_SIZE 1024

static int mycdev_open(struct inode *inode,struct file *fp)
{
  return 0;
}

static int mycdev_release(struct inode *inode,struct file *fp)
{
  return 0;
}

static ssize_t mycdev_read(struct file *fp,char __user *buf,size_t size,loff_t *pos)
{
  unsigned long p = *pos;
  unsigned int count = size;
  char kernel_buf[MYCDEV_SIZE] = "This is mycdev!";
  int i;

  if(p >= MYCDEV_SIZE)
    return -1;
  if(count > MYCDEV_SIZE)
    count = MYCDEV_SIZE - p;
  if(copy_to_user(buf,kernel_buf,count) != 0){
      printk("read error!\n");

      return -1;
    }

  printk("reader:%d bytes was read...\n",count);
  return count;
}

static ssize_t mycdev_write(struct file *fp,const char __user *buf,size_t size,loff_t *pos)
{
  return size;
}

static const struct file_operations mycdev_fops =
{
  .owner = THIS_MODULE,
  .read = mycdev_read,
  .write = mycdev_write,
  .open = mycdev_open,
  .release = mycdev_release,
};

static int __init mycdev_init(void)
{
  int ret;

  printk("mycdev module is starting..\n");

  ret = register_chrdev(MYCDEV_MAJOR,"my_cdev",&mycdev_fops);
  if(ret < 0)
  {
    printk("register failed..\n");
    return 0;
  }
  else
  {
    printk("register success..\n");
  }
  return 0;
}

static void __exit mycdev_exit(void)
{
  printk("mycdev module is leaving..\n");
  unregister_chrdev(MYCDEV_MAJOR,"my_cdev");
}

module_init(mycdev_init);
module_exit(mycdev_exit);

首先看一下file_operation结构体,此结构体是该驱动程序的核心。它给出了对文件操作函数的定义。当然,具体的实现函数是留给驱动程序编写的:

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 (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
 ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
 int (*readdir) (struct file *, void *, filldir_t);
 unsigned int (*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 *);
 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 (*aio_fsync) (struct kiocb *, 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 (*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);
 int (*setlease)(struct file *, long, struct file_lock **);
 long (*fallocate)(struct file *file, int mode, loff_t offset,
     loff_t len);
 int (*show_fdinfo)(struct seq_file *m, struct file *f);
};

此结构对文件操作的函数给出了定义。种类繁多。

我们这里的file_operation结构体进行初始化时仅初始化了4个函数。这些使用的函数在程序的前半部分已经给出了定义。

五、调试程序

由于编写的是内核模块,所以需要用make进行编译。这个我在之前的博客已经写过如何编写Makefile文件。编译完毕后将模块插入。

然后,通过cat /proc/devices来看系统中未使用的字符设备主设备号,我这里看到的是my_cdev,对应的是231号。

接下来使用mknod命令创建设备文件结点,然后用chmod命令修改权限为777。此时设备就可以使用了。

这里我们需要注意一下/proc/devices与/dev下的显示的设备的不同之处。

在/proc/devices下,显示的是驱动程序生成的设备及其主设备号。其中主设备号可用来让mknod作为参数。

在/dev下的设备是mknod生成的设备,其中,用户通过使用/dev下的设备名来使用设备。

资料直通车:最新Linux内核源码资料文档+视频资料icon-default.png?t=M85Bhttps://docs.qq.com/doc/DTmFTc29xUGdNSnZ2

内核学习地址:Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈icon-default.png?t=M85Bhttps://ke.qq.com/course/4032547?flowToken=1040236

六、编写用户态测试程序

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<unistd.h>

int main()
{
  int testdev;
  int i,ret;
  char buf[10];

  testdev = open("/dev/mycdev",O_RDWR);
  if(testdev == -1){
    printf("connot open file..\n");
    exit(1);
  }
  if((ret = read(testdev,buf,10)) <10){
    printf("read error!\n");
    exit(1);
  }
  for(i=0;i<10;i++)
    printf("%d\n",buf[i]);

  close(testdev);

  return 0;
}

这个程序没什么好说的。

七、运行结果

插入模块后查看日志信息:

运行测试程序:

这里我们看到,它输出了数组的前十个字节,对应着:This is my

查看日志信息:

这个我们看到,驱动也打印出了一段文字,十个字节被读取。

卸载模块:

八、总结

这里只是简单地介绍了一下设备驱动程序的框架,作为一个对驱动程序的简单了解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/112429.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Ctfer训练计划】——(四)

作者名&#xff1a;Demo不是emo 主页面链接&#xff1a;主页传送门创作初心&#xff1a;舞台再大&#xff0c;你不上台&#xff0c;永远是观众&#xff0c;没人会关心你努不努力&#xff0c;摔的痛不痛&#xff0c;他们只会看你最后站在什么位置&#xff0c;然后羡慕或鄙夷座右…

【kafka】学习笔记(三)

学习笔记七、Kafka-Eagle 监控7.1 环境准备7.2 Eagle 安装7.3、修改配置文件7.4、添加环境变量7.5、启动Eagle八、Kafka-Kraft 模式8.1、Kafka-Kraft 集群部署8.2、初始化集群数据目录8.3、启动 kafka 集群8.4、测试8.5、集群启动脚本九、SpringBoot集成Kafka七、Kafka-Eagle 监…

RabbitMQ 第一天 基础 4 RabbitMQ 的工作模式 4.3 Routing 路由模式

RabbitMQ 【黑马程序员RabbitMQ全套教程&#xff0c;rabbitmq消息中间件到实战】 文章目录RabbitMQ第一天 基础4 RabbitMQ 的工作模式4.3 Routing 路由模式4.3.1 模式说明4.3.2 代码编写4.3.3 小结第一天 基础 4 RabbitMQ 的工作模式 4.3 Routing 路由模式 4.3.1 模式说明 …

React 学习笔记总结(四)

文章目录1. 创建组件流程(以及脚手架环境流程)2. 样式 的模块化3. 常用快捷生成4. 通用性 组件编码流程5. React脚手架 配置代理5.1 React 引入 ajax库5.2 第一种配置代理方式(package.json)5.3 第二种代理方式(setupProxy.js)6. React List列表效果实现7. React 消息订阅与发布…

“内卷之王”vivo:成败即将见分晓

文丨熔财经 作者|XL 12月22日&#xff0c;随着年底收官之作S16系列正式发布&#xff0c;vivo完成了自己的年度答卷。2022年&#xff0c;vivo总体风头正盛&#xff0c;尤其在第三季度一马当先稳居国内出货量榜首&#xff0c;市占比提升到20.0%&#xff0c;领先第二名3%。这是一…

elementUI中el-table每行异常高度原因排查,累死

理论上不单独设置高度的话&#xff0c;表格每一个应该是默认的高度才对&#xff0c;我说的没错吧&#xff0c; 但是请看实际情况&#xff1a; 这是默认情况下的高度为48 还有两外一个表格&#xff0c;我也没有设置高度&#xff0c;但是但是&#xff1a;这个高度竟然达到了71&…

深入理解蓝牙BLE之“无线通信的调制解调”

FSK&#xff1a; Frequency Shift Keying&#xff0c;频移键控&#xff0c;即一种允许根据数字调制信号改变载波频率而进行数据传输的数字调制技术&#xff0c;比如在BFSK中&#xff0c;二进制1和二进制0期间传输不同频率的载波信号。由于这种调制解调方式容易实现&#xff0c…

我国水产养殖行业现状及趋势分析:不断推进产业机械化高质量发展

水产养殖业主要是人类利用适宜水域养殖水产经济动植物的生产事业&#xff0c;是渔业的重要组成部分。按养殖水域&#xff0c;水产养殖可分为淡水养殖、海水养殖、浅海滩涂养殖&#xff1b;按养殖对象&#xff0c;可分为鱼类养殖、贝类养殖、虾类养殖、蟹类养殖、藻类栽培&#…

AndroidR兼容性适配指南

AndroidR Android 11 基于 Android 早期版本构建&#xff0c;增加了多种功能和更新&#xff0c;以保障用户安全并提高透明度和可控性。所有开发者都应查看隐私功能并测试他们的应用。具体影响可能会因每个应用的核心功能、目标平台和其他因素而异。 Android 11介绍 Android 1…

day 10 模拟和高精度

P1328 [NOIP2014 提高组] 生活大爆炸版石头剪刀布 #include<bits/stdc.h> using namespace std; int n, na, nb, fa, fb;//f:得分 int a[205], b[205];void fun(int ta, int tb){if(ta 0 && tb 1) fb;if(ta 1 && tb 0) fa;if(ta 0 && tb …

学习记录-mybatis+vue+elementUi实现分页条件查询

因为前端并不难&#xff0c;这里前后端就一起实现。一共分为四个步骤 步骤一 ① SQL部分 分页条件查询其实就是在分页的基础上增加条件&#xff0c;这里有两个主要的函数需要去实现&#xff0c;一个是根据分页以及条件去查询数据&#xff0c;另一个是根据条件去统计数据。 明显…

结构体嵌套函数指针

这次来记录一下结构体嵌套函数指针 这个知识点想了2天终于搞懂了。 先看代码&#xff0c;试着理解一下&#xff0c;不理解再看我后面的解释。 解释&#xff1a; 首先&#xff0c;和平常创建一个结构体一样&#xff0c;唯独不同的就是里面的变量是一个函数指针&#xff0c;关…

Spring Bean作用域

目录 什么是作用域呢 ? 那什么又是Spring Bean的作用域呢 ? Spring框架默认Bean作用域是什么呢 ? Spring Bean的作用域都有哪些呢 ? 如何设置Bean作用域 什么是作用域呢 ? 在JavaSE中,作用域就是指一个变量可生效的范围. 就比如一个变量的作用域是方法的代码块的范围…

fpga实操训练(signal tap调试)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 编写软件的同学都知道&#xff0c;如果需要调试软件的话&#xff0c;除了要学会打印信息日志之外&#xff0c;另外一个很重要的方面&#xff0c;就…

http 库的服务端实现

前言 net/http 库的客户端实现(上) net/http 库的客户端实现(下) net/http 库的服务端实现 上两篇文章介绍了 http 客户端的实现&#xff0c;这篇文章看一下服务端的实现 服务端 使用 net/http 库可以快速搭建HTTP服务&#xff0c;HTTP服务端主要包含两部分&#xff1a; …

5G无线技术基础自学系列 | 5G网络切换问题分析

素材来源&#xff1a;《5G无线网络规划与优化》 一边学习一边整理内容&#xff0c;并与大家分享&#xff0c;侵权即删&#xff0c;谢谢支持&#xff01; 附上汇总贴&#xff1a;5G无线技术基础自学系列 | 汇总_COCOgsta的博客-CSDN博客 无线通信的最大特点在于其具有移动性&a…

提高搜狗PR最好的方法与搜狗PR权重在线查询

搜狗PR是什么? 搜狗PR值全称为搜狗PageRank(网页级别)&#xff0c;是搜狗用于用来标识网页的等级、重要性的一种方法&#xff0c;是搜狗用来衡量一个网站的好坏的重要标准之一。 搜狗在揉合了诸如Title标识和Keywords标识等所有其它因素之后&#xff0c;通过PageRank来…

react学习

1. React概述 1.1 什么是react&#xff1f; React 是一个用于构建用户界面的 JavaScript 库 用户界面:HTML页面(前端) React 主要用来写HTML页面&#xff0c;或构建Web应用如果从 MVC 的角度来看&#xff0c;React 仅仅是视图层(V)&#xff0c;也就是只负责视图的染&#xff0…

Day44——Dp专题

文章目录子序列问题27.最长递增子序列28、最长连续递增序列29、最长重复子数组30、最长公共子序列31、不相交的线32、最大子序和33、判断子序列34、不同的子序列35、两个字符串的删除操作36、编辑距离37、回文子串38、最长回文子序列动态规划总结篇背包问题系列股票系列子序列系…

java论坛贴子网站ssm论坛项目发帖子网站论坛系统论坛源码

ssm开发的论坛系统&#xff0c;用户注册后可以发布帖子&#xff0c;其他人可以评论回复点赞评论和点赞回复&#xff0c;用户可以在个人中心管理自己的帖子&#xff0c;以及查看自己对他人的回复&#xff0c;和他人对自己的回复。 演示视频&#xff1a; https://www.bilibili.c…