Linux-设备驱动概述

article/2025/11/10 1:14:21

文章目录

  • Linux设备驱动概述
    • 1. 设备驱动的作用
    • 2. 无操作系统的设备驱动
    • 3. 有操作系统时的设备驱动
    • 4. Linux设备驱动
      • 4.1 设备的分类及特点
      • 4.2 Linux设备驱动与整个软硬件系统的关系
      • 4.3 Linux设备驱动的重难点
    • 5. 源代码阅读
    • 6. 设备驱动:LED驱动
      • 6.1 无操作系统的LED驱动
      • 6.2 Linux下的LED驱动

Linux设备驱动概述

1. 设备驱动的作用

  对设备驱动的最通俗解释就是驱动硬件设备行动,驱动和底层硬件设备直接打交道,按照硬件设备的具体工作方式,读写设备的寄存器,完成设备的轮询,中断处理,DMA通信,进行物理内存向虚拟内存的映射等,最终让通信设备能够收发数据,让显示设备能够显示文字和画面,让存储设备能记录文件和数据。
  无操作系统的情况下,工程师可以根据硬件设备的特点自信定义接口,有操作系统的情况下,驱动的结构则由相应的操作系统定义,驱动工程师必须按照相应的架构设计驱动,这样,驱动才能整合入操作系统的内核中。

2. 无操作系统的设备驱动

  并不是任何一个计算机系统都一定要有操作系统,很多情况下,操作系统都不必存在,对于功能比较单一,控制不复杂的系统,ASIC内部,公交车的刷卡机,电冰箱,微波炉等,并不需要多任务调度,文件系统,内部管理等复杂功能,单任务架构完全可以良好的支持它们的工作。一个无限循环中夹杂着对设备中断的检测或者对设备的轮询是这种系统中软件的典型架构。

int main(int argc, char* argv[])
{while(1){if(serialInt == 1){//有串口中断ProcessSerialInt()//处理串口中断serialInt = 0;//中断标志变量清0}if(keyInt == 1){//有按键中断ProcessKeyInt();//处理按键中断keyInt = 0;//中断标志变量清0}status = CheckXXX();switch(status){//...}//...}
}

  在无操作系统的系统中,虽然不存在操作系统,但是设备驱动则无论如何都必须存在,一般情况下,每种设备驱动都会定义一个软件模块,包含.h文件和.c文件,前者定义该设备驱动的数据结构并声明外部函数,后者负责进行驱动的具体实现,如下:

//无操作系统情况下串口的驱动
/****************
*serial.h文件
*****************/extern void SerialInit(void);
extern void SerialSend(const char buf*, int count);
extern void SerialRecv(char buf*, int count);/****************
*serial.c文件
*****************///初始化串口
void SerialInit(void)
{//...
}
//串口发送
void SerialSend(const char buf*, int count)
{//...
}
//串口接收
void SerialRecv(char buf*, int count)
{//...
}
//串口中断处理函数
void SerialIsr(void)
{//...serialInt = 1;
}

  其他模块想要使用这个设备的时候,只需要包含设备驱动的头文件serial.h,然后调用其中的外部接口函数,如果要从串口上发送“hello world”字符串,使用语句SerialSend(“hello world”, 11)即可。
  无操作系统下硬件,设备驱动与引用软件的关系:
在这里插入图片描述
  两种不合理的设计:
在这里插入图片描述

3. 有操作系统时的设备驱动

  当系统中存在操作系统的时候,驱动变成了连接硬件和内核的桥梁,操作系统的存在势必要求设备驱动附加更多的代码和功能,把单一的“驱使设备硬件行动”变成了操作系统内与硬件交互的模块,对外呈现操作系统的API,不在给应用软件工程师提供接口。

操作系统的作用:
  1.一个复杂的软件系统需要处理多个并发的任务,没有操作系统,想完成多任务并发时很困难的。
  2.操作系统给我们提供了内存管理机制,对于多数含MMU的32位处理器而言,Windows,Linux等操作系统可以让每个进程都独立地访问4GB的内存空间。
  3.操作系统通过给驱动设定统一的接口形式来使得上层的应用程序可以使用统一的系统调用接口来访问各种设备。

4. Linux设备驱动

4.1 设备的分类及特点

  计算机系统的硬件主要由CPU,存储器和外设组成,因为IC制作的发展,芯片集成度更高了,往往在CPU内部就集成了存储器和外设适配器。
  驱动针对的对象事存储器和外设(包括CPU内部继承的存储器和外设),而不是针对CPU内核,Linux将存储器和外设分为3个基础大类:
  1.字符设备:指那些必须以串行顺序依次进行访问的设备,如触摸屏,磁带驱动器,鼠标等。块设备可以按任意顺序进行访问,以块为单位进行操作。
  2.块设备:字符设备和块设备的驱动设计有很大的差异,但是对于用户而言,字符设备和块设备都只需要使用文件系统的操作接口open(),close(),read(),write()等进行访问
  3.网络设备:主要是为了面向数据包的接收和发送而设计的,网络设备的通信方式和前两者完全不同,主要是使用的套接字接口

4.2 Linux设备驱动与整个软硬件系统的关系

  Linux的块设备有两种访问的方式:一种是类似dd命令对应的原始块设备,另一种是在块设备上建立FAT、EXT4、BTRFS等文件系统,然后以文件路径的方式访问。
  在Linux中,针对NOR、NAND等提供了独立的内存技术设备子系统,其上运行具备擦除和负载均衡能力的文件系统,针对磁盘或者Flash设备上的FAT,EXT4等文件系统定义了文件和目录在存储介质上的组织。
在这里插入图片描述

4.3 Linux设备驱动的重难点

  1.编写Linux设备驱动要求工程师有非常好的硬件基础,懂得SRAM,Flash,SDRAM,磁盘的读写方式,UART,I2C,USB等设备的接口以及轮询,中断,DMA的原理,PCI总线的工作方式以及CPU的内存管理单元MMU等。
  2.编写Linux设备驱动要求工程师有非常好的C语言基础,能灵活运用结构体,指针,函数指针以及内存动态申请和释放等。
  3.要求有Linux内核基础,明白驱动与内核的接口,尤其是块设备,网络设备,Flash设备,串口设备等。
  4.需要多任务并发控制和同步的基础,因为驱动中会大量使用自旋锁,互斥,信号量,等待队列等并发与同步机制。

5. 源代码阅读

  1.window上阅读Linux源代码的工具SourceInsight
  2.类似https://elixir.bootlin.com/linux/latest/source/Documentation提供了Linux内核源代码的交叉索引
  3.Linux上通常是vim+cscope或者vim+ctags,cscope和ctags可建立代码索引

6. 设备驱动:LED驱动

6.1 无操作系统的LED驱动

  在嵌入式系统的设计中,LED一般直接由CPU和GPIO(通用可编程IO)口控制,GPIO一般是由两组寄存器控制,一组控制寄存器和一组数据寄存器。控制寄存器设置GPIO工作方式为输入还是输出。当设置为输出时,向寄存器的对应位写入1和0会分别在引脚上产生高电平和低电平,当设置为输入时,读取数据寄存器的对应位可获得引脚上的电平为高或低。
  屏蔽CPU差异,GPIO_REG_CTRL物理地址中控制寄存器处的第n位写入1可设置GPIO口为输出,在地址GPIO_REG_DATA物理地址中数据寄存器的第n位写入1或者0可以引脚上产生高或低电平。无操作系统下设备驱动清单:

#define reg_gpio_ctrl *(volatile int *)(ToVirtual(GPIO_REG_CTRL))
#define reg_gpio_data *(volatile int *)(ToVirtual(GPIO_REG_CTRL))
//初始化LED
void LigthInit(void)
{reg_gpio_ctrl |= (1 << n);//设置GPIO为输出
}
//点亮LED
void LightOn(void)
{reg_gpio_data |= (1 << n);//在GPIO上输出高电平
}
//熄灭LED
void LightOff(void)
{reg_gpio_data &= ~(1 << n);//在GPIO上输出低电平
}

  LigthInit(),LightOn(),LightOff()都是外部的接口函数。程序中ToVirtual()的作用是当系统启动了硬件MMU之后,根据物理地址和虚拟地址的映射关系,将寄存器的物理地址转化为虚拟地址。

6.2 Linux下的LED驱动

  位于drivers/leds/leds-gpio.c中,目录

https://elixir.bootlin.com/linux/latest/source/drivers/leds/leds-gpio.c

  内核中实现了一个提供sysfs节点的GPIO LED驱动,操作硬件的LightInit(),LightOn(),Light Off()函数仍然需要,但是,遵循Linux编程命名习惯,改为light_init(),light_on(),light_off(),这些函数将被LED设备驱动中独立于设备并针对内核的接口进行调用。

#include ...//包含多个头文件//设备结构体
struct light_dev {struct cdev cdev;//字符设备cdev结构体unsigned char value;//LED亮时为1,熄灭为0,用户可读写该值
};struct ligth_dev* light_devp;
int light_major = LIGHT_MAJOR;MODULE_AUTHOR("Barry Song <xxx@gmail.com>");
MODULE_LICENSE("Dual BSD/GPL");
//打开和关闭函数
int light_open(struct inode* inode, struct file* filp)
{struct light_dev* dev;//获得设备结构体指针dev = container_of(inode->i_cdev, struct light_dev, cdev);//让设备结构体作为设备的私有信息filp->private_data = dev;return 0;
}int light_release(struct inode* inode, struct file* filp)
{return 0;
}//读写设备:可以不需要
ssize_t light_read(struct file *filp, char __user *buf, size_t count, loff_t* f_pos)
{struct light_dev *dev = filp->private_data;//获得设备结构体if(copy_to_user(buf, &(dev->value), 1))return -EFAULT;return 1;
}ssize_t light_write(struct file *filp, char __user *buf, size_t count,
loff_t* f_pos)
{struct light_dev* dev = filp->private_data;if(copy_from_user(&(dev->value), buf, 1)return -EFAULT;//根据写入的值点亮和熄灭LEDif(dev->value == 1)ligth_on();elselight_off();return 1;
}//ioctl函数
int light_ioctl(struct inode* inode, struct file* filp, unsigned int cmd, usigned long arg)
{struct light_dev* dev = filp->private_data;switch(cmd){case LIGHT_ON:dev->value = 1;light_on();break();case LIGHT_OFF:dev-value = 0;light_off();break;default:return -EFAULT;}return 0;
}struct file_operations light_fops = {.owner = THIS_MODULE,.read = light_read,.write = light_write,.ioctl = light_ioctl,.open = light_open,.release = light_release,
};//设置字符设备cdev结构体
static void light_setip_cdev(struct light_dev* dev, int index)
{int err, devno = MKDEV(light_major, index);cdev_init(&dev->cdev, &light_fops);dev->cdev.owner = THIS_MODULE;dev->cdev.ops = &light_fops;err = cdev_add(&dev->cdev, devno, 1);if(err)printk(KERN_NOTICE "Error %d adding LED%d", err, index);
}//模块加载函数
int light_init(void)
{int result;dev_t dev = MKDEV(light_major, 0);//申请字符设备号if(light_major)result = register_chrdev_region(dev, 1, "LED");else {result = alloc_chrdev_region(&dev, 0, 1, "LED");light_major = MAJOR(dev);}if(result < 0)return result;//分配设备结构体的内存light_devp = kmalloc(sizeof(struct light_dev), GFP_KERNEL);if(!light_devp){result = -ENOMEM;goto fail_malloc;}memset(light_devp, 0, sizeof(struct light_dev));light_setup_cdev(light_devp, 0);light_gpio_init();return 0;fail_malloc:unregister_chrdev_region(dev, light_devp);return result;
}//模块卸载函数
void light_cleanup(void)
{cdev_del(&light_devp->cdev);//删除字符设备结构体kfree(light_devp);//释放在light_init中分配的内存unregister_chrdev_region(MKDEV(light_major, 0), 1);//删除字符设备
}module_init(light_init);
module_exit(light_cleanup);

  上述暂时陌生的元素都是Linux内核字符设备定义的,以实现驱动与内核的接口而定义的,Linux对各类设备的驱动都定义了类似的数据结构和函数。


http://chatgpt.dhexx.cn/article/f96D2cx7.shtml

相关文章

Linux设备驱动和设备匹配过程

Linux设备驱动和设备匹配过程 1. 设备驱动匹配简述2. 重点结构体介绍2.1 struct device2.2 struct platform_device2.3 struct platform_driver2.4 struct device_driver 3. device端发起匹配&#xff1a;3.1 流程图3.2 start_kernel时候解析设备树3.2.1 start_kernel3.2.2 set…

Linux设备驱动之字符设备驱动

一、linux系统将设备分为3类&#xff1a;字符设备、块设备、网络设备。 应用程序调用的流程框图&#xff1a; 三种设备的定义分别如下&#xff0c; 字符设备&#xff1a;只能一个字节一个字节的读写的设备&#xff0c;不能随机读取设备内存中的某一数据&#xff0c;读取数据需…

Linux设备驱动基础01:Linux设备驱动概述

目录 1. 设备驱动的作用 2. 有无操作系统时的设备驱动 2.1 无操作系统 2.1.1 硬件、驱动和应用程序的关系 2.1.2 单任务软件典型架构 2.2 有操作系统 2.2.1 硬件、驱动、操作系统和应用软件的关系 3. Linux设备分类 3.1 常规分类法 3.1.1 字符设备 3.1.2 块设备 3.…

Linux设备驱动模型

目录 一、linux设备驱动模型简介1、什么是设备驱动模型2、为什么需要设备驱动模型3、驱动开发的2个点 二、设备驱动模型的底层架构1、kobject2、kobj_type3、kset 三、总线式设备驱动组织方式1、总线2、设备3、驱动4、类5、总结 四、platform平台总线工作原理1、何为平台总线2、…

【linux内核分析与应用-陈莉君】字符设备驱动

目录 1.什么是字符设备 2.如何来描述字符设备 3 struct cdev与const struct file_operations之间的关系 4.struct file_operations源码 5.字符设备驱动框架 6.编写字符设备驱动的步骤 7.字符设备结构 8.字符设备驱动程序的注册 9.从系统调用到驱动程序 10.用户空间与内…

微信小程序云数据库使用讲解

第一步&#xff1a;注册开通 单击云开发 第二步&#xff1a;创建数据库 选择数据库&#xff0c;并点击号创建一个集合 输入名字 创建完毕后点击添加记录即可添加数据 数据库获取&#xff1a; 查询&#xff1a; 查询指令&#xff1a;

微信小程序云开发入门-数据库插入数据(包含批量)

一、前言 文章将介绍如何在微信小程序云开发中向云开发数据库插入数据&#xff08;单条或批量&#xff09;。 写法有好几种&#xff0c;文章将会一一进行对比&#xff0c;看看每种写法之间有何优缺点&#xff0c;如何让代码看起来更优雅。 为了更加贴合实际的开发逻辑&#xf…

【微信小程序】如何获取微信小程序云开发数据库的数据并渲染到页面?

前言 上一篇博客我把微信小程序云开发数据库操作&#xff08;增删改查&#xff09;的实现方法都已经分享出来啦&#xff0c;可以戳链接进去阅读哦 【微信小程序】小程序云开发实现数据库增删改查(小白速度Get起来&#xff01;&#xff01;一步步教你如何实现) 基于微信小程序…

开发一个可以查询并显示数据库内容的微信小程序

使用微信开发者工具可以创建云数据库&#xff0c;并通过代码可以查询并在客户端显示数据库的内容。 附&#xff1a;小程序一个功能页面有wxml(客户端呈现)&#xff0c;js(功能函数)&#xff0c;json&#xff0c;wxss(个性化处理)&#xff0c;这些是局部的文件。还有全局的文件如…

微信小程序开发---连接云开发数据库,实现数据获取

之前几篇博客里面都详细交代了如何配置云函数&#xff0c;现在就讲一下关于云函数中数据库的使用&#xff0c;主要是讲如何从云开发平台的数据库中调取数据 我们直接来到需要调用数据库数据的页面的js文件&#xff0c;直接设置全局变量&#xff0c;来便于后续调用数据库 const …

微信小程序查询数据库

微信小程序云开发的官方例子&#xff1a; const db wx.cloud.database() //获取数据库的引用 const _ db.command //获取数据库查询及更新指令 db.collection("china") //获取集合china的引用.where({ //查询的条件指令wheregdp: _.gt(3000) …

微信小程序云数据库操作

微信小程序云数据库操作 1、云数据库简介1.1 数据类型Date地理位置Null 1.2 权限控制 2、云数据库操作2.1 查询数据2.1.1 通过collection.doc获取一条记录2.1.2 通过collection.get获取所有记录的数据2.1.3 通过document.get获取某一条记录的数据2.1.4 通过collection.count获取…

微信小程序取本地数据库数据(实测有图)

测试效果如下&#xff1a; 本实验主要分为如下几个步骤&#xff1a; 一、安装数据库 二、安装PHPApache 三、编辑微信小程序代码 前两项的简单介绍在如下连接&#xff1a; PHPApache 四、本文主要介绍第三项的内容 需要用到的文件如下&#xff1a; 1、新建微信小程序工程 2、…

微信小程序云开发(数据库)

开发者可以使用云开发开发微信小程序、小游戏&#xff0c;无需搭建服务器&#xff0c;即可使用云端能力。 云开发为开发者提供完整的云端支持&#xff0c;弱化后端和运维概念&#xff0c;无需搭建服务器&#xff0c;使用平台提供的 API 进行核心业务开发&#xff0c;即可实现快…

微信小程序云开发(云数据库篇)

微信小程序云开发[云数据库篇] 云数据库关系型数据库和 JSON 数据库对比数据类型数据库操作联表查询事务处理 云数据库 云开发提供了一个 JSON 数据库&#xff0c;顾名思义&#xff0c;数据库中的每条记录都是一个 JSON 格式的对象。 一个数据库可以有多个集合&#xff08;相当…

微信小程序云开发 1 - 数据库

微信小程序云开发最重要的有两点&#xff1a; 1、云数据库&#xff1b; 2、云函数&#xff1b; 学会这两点基本就能够进行微信小程序的云开发&#xff1b; 首先&#xff0c;我们先看微信小程序云数据库的基本操作&#xff1a; 1&#xff09;打开微 信开发者工具&#xff0…

微信小程序笔记 -- 数据库

6.15 学习微信小程序 -- 数据库 数据库1. 初始化2. 数据库操作2.1 数据类型2.2 增删查改2.2.1 增加/插入 数据&#xff08;add方法&#xff09;2.2.2 删除数据&#xff08;remove方法&#xff09;2.2.3 查看数据&#xff08;get&#xff0c;where&#xff09;2.2.4 更新数据&am…

nohup忽略SIGHUP信号

今天遇到一个问题&#xff1a;开启终端启动gunicorn进程后台运行&#xff0c;终端不关闭时&#xff0c;可以导入excel&#xff0c;关闭终端后&#xff0c;不能导入excel。原因是&#xff0c;xlrd模块需要向控制台输出内容&#xff0c;终端关闭后&#xff0c;控制台消失&#xf…

sighup问题

2019独角兽企业重金招聘Python工程师标准>>> SIGHUP会在以下3种情况下被发送给相应的进程&#xff1a; 1、终端关闭时&#xff0c;该信号被发送到session首进程以及作为job提交的进程&#xff08;即用 & 符号提交的进程&#xff09; 2、session首进程退出时&…

Linux——1、守护进程、SIGHUP与nohup

一、守护进程 脱离于终端并且在后台运行的进程&#xff0c;用于长期运行&#xff0c;守护自己的职责&#xff08;如&#xff1a;监听端口、服务等&#xff09;。 1、特点&#xff1a; 不受用户登录、注销影响。大多数Linux下的服务器都是利用守护进程实现的&#xff0c;如My…