Linux驱动编程(驱动程序基石)(下)

news2025/1/22 21:43:00

一、中断的线程化处理

复杂、耗时的事情,尽量使用内核线程来处理。上节视频介绍的工作队列用起来挺简单,但是它有一个缺点:工作队列中有多个 work,前一个 work 没处理完会影响后面的 work。解决方法有很多种,比如干脆自己创建一个内核线程,不跟别的 work 凑在一块了。

对于中断处理,还有另一种方法:threaded irq,线程化的中断处理。中断的处理仍然可以认为分为上半部、下半部。上半部用来处理紧急的事情,下半部用一个内核线程来处理,这个内核线程专用于这个中断。
在这里插入图片描述你可以只提供 thread_fn,系统会为这个函数创建一个内核线程。发生中断时,系统会立刻调用 handler函数,然后唤醒某个内核线程,内核线程再来执行 thread_fn 函数。

8.1、内核机制

调用 request_threaded_irq 后内核的数据结构
在这里插入图片描述
request_threaded_irq

request_threaded_irq 函数,肯定会创建一个内核线程。

int request_threaded_irq(unsigned int irq, irq_handler_t handler,irq_handler_t thread_fn, unsigned long irqflags,const char *devname, void *dev_id)
{
	 // 分配、设置一个 irqaction 结构体
	action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
	if (!action)
		return -ENOMEM;
		
	action->handler = handler;
	action->thread_fn = thread_fn;
	action->flags = irqflags;
	action->name = devname;
	action->dev_id = dev_id;
	
	retval = __setup_irq(irq, desc, action); // 进一步处理
}

__setup_irq 函数代码如下(只摘取重要部分):

if (new->thread_fn && !nested) {
	ret = setup_irq_thread(new, irq, false);

setup_irq_thread 函数代码如下(只摘取重要部分):

if (!secondary) 
{
	t = kthread_create(irq_thread, new, "irq/%d-%s", irq,new->name);
} 
else 
{
	t = kthread_create(irq_thread, new, "irq/%d-s-%s", irq,new->name);
	param.sched_priority -= 1;
}
new->thread = t;

二、mmap

应用程序和驱动程序之间传递数据时,可以通过 read、write 函数进行。这涉及在用户态 buffer 和内核态 buffer 之间传数据,如下图所示:
在这里插入图片描述2.1、内存映射现象与数据结构

假设有这样的程序,名为 test.c:

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

int a;

int main(int argc, char **argv)
{
	if (argc != 2)
	{
		printf("Usage: %s <number>\n", argv[0]);
		return -1;
	}
	a = strtol(argv[1], NULL, 0);
	printf("a's address = 0x%lx, a's value = %d\n", &a, a);
	while (1)
	{
		sleep(10);
	}
	return 0;
}

在 PC 上如下编译(必须静态编译):

gcc -o test test.c -staitc

分别执行 test 程序 2 次,最后执行 ps,可以看到这 2 个程序同时存在,这 2 个程序里 a 变量的地址相同,但是值不同。如下图:
在这里插入图片描述
观察到这些现象:
① 2 个程序同时运行,它们的变量 a 的地址都是一样的:0x6bc3a0;
② 2 个程序同时运行,它们的变量 a 的值是不一样的,一个是 12,另一个是 123。
疑问来了:
① 这 2 个程序同时在内存中运行,它们的值不一样,所以变量 a 的地址肯定不同;
② 但是打印出来的变量 a 的地址却是一样的。
这里要引入虚拟地址的概念:CPU 发出的地址是虚拟地址,它经过 MMU(Memory Manage Unit,内存管理单元)映射到物理地址上,对于不同进程的同一个虚拟地址,MMU 会把它们映射到不同的物理地址。如下图:
在这里插入图片描述
当前运行的是 app1 时,MMU 会把 CPU 发出的虚拟地址 addr 映射为物理地址 paddr1,用 paddr1 去访问内存。
当前运行的是 app2 时,MMU 会把 CPU 发出的虚拟地址 addr 映射为物理地址 paddr2,用 paddr2 去访问内存。
MMU 负责把虚拟地址映射为物理地址,虚拟地址映射到哪个物理地址去?
可以执行 ps 命令查看进程 ID,然后执行“cat /proc/325/maps”得到映射关系。
每一个 APP 在内核里都有一个 tast_struct,这个结构体中保存有内存信息:mm_struct。而虚拟地址、物理地址的映射关系保存在页目录表中,如下图所示:
在这里插入图片描述

2.2、怎么给 APP 新建一块内存映射

1、mmap 调用过程

① 得到一个 vm_area_struct,它表示 APP 的一块虚拟内存空间;
APP 调用 mmap 系统函数时,内核就帮我们构造了一个 vm_area_stuct 结构体。里面含有虚拟地址的地址范围、权限。

② 确定物理地址
你想映射某个内核 buffer,你需要得到它的物理地址,这得由你提供。

③ 给 vm_area_struct 和物理地址建立映射关系
APP 里调用 mmap 时,导致的内核相关函数调用过程如下:
在这里插入图片描述
3、驱动程序要做的事

驱动程序要做的事情有 3 点:
① 确定物理地址
② 确定属性:是否使用 cache、buffer
③ 建立映射关系
参考 Linux 源文件,示例代码如下:
在这里插入图片描述
还有一个更简单的函数:
在这里插入图片描述
4、APP 编程

APP 怎么写?open 驱动、buf=mmap(……)映射内存,直接读写 buf 就可以了,代码如下:

/* 1. 打开文件 */
fd = open("/dev/hello", O_RDWR);

if (fd == -1)
{
	printf("can not open file /dev/hello\n");
	return -1;
}

/* 2. mmap
* MAP_SHARED : 多个 APP 都调用 mmap 映射同一块内存时, 对内存的修改大家都可以看到。
* 就是说多个 APP、驱动程序实际上访问的都是同一块内存
* MAP_PRIVATE : 创建一个 copy on write 的私有映射。
* 当 APP 对该内存进行修改时,其他程序是看不到这些修改的。
* 就是当 APP 写内存时, 内核会先创建一个拷贝给这个 APP,
* 这个拷贝是这个 APP 私有的, 其他 APP、驱动无法访问。
*/
buf = mmap(NULL, 1024*8, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

if (buf == MAP_FAILED)
{
	printf("can not mmap file /dev/hello\n");
	return -1;
}
printf("mmap address = 0x%x\n", buf);
printf("buf origin data = %s\n", buf); /* old */

/* 3. write */
strcpy(buf, "new");

/* 4. read & compare */
/* 对于 MAP_SHARED 映射: str = "new"
* 对于 MAP_PRIVATE 映射: str = "old"
*/
read(fd, str, 1024);
if (strcmp(buf, str) == 0)
{
	/* 对于 MAP_SHARED 映射,APP 写的数据驱动可见
	* APP 和驱动访问的是同一个内存块
	*/
	printf("compare ok!\n");
}
else
{
	/* 对于 MAP_PRIVATE 映射,APP 写数据时, 是写入另一个内存块(是原内存块的"拷贝")
	*/
	printf("compare err!\n");
	printf("str = %s!\n", str); /* old */
	printf("buf = %s!\n", buf); /* new */
}

5、驱动编程
① 分配一块 8K 的内存
使用哪一个函数分配内存?
在这里插入图片描述
② 提供 mmap 函数
关键在于 mmap 函数,代码如下:
在这里插入图片描述
要注意的是,remap_pfn_range 中,pfn 的意思是“Page Frame Number”。在 Linux 中,整个物理地址空间可以分为第 0 页、第 1 页、第 2 页,诸如此类,这就是 pfn。假设每页大小是 4K,那么给定物理地址phy,它的 pfn = phy / 4096 = phy >> 12。内核的 page 一般是 4K,但是也可以配置内核修改 page 的大小。所以为了通用,pfn = phy >> PAGE_SHIFT。

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

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

相关文章

String类的学习笔记(下):字符串拼接以及StringBuilder和StringBuffer的学习

本文介绍了String类对字符串进行拼接的方法 和拼接字符串的效率分析 以及能对字符串内容进行修改的StringBuilder和StringBuffer类其常用方法和区别 , 最后介绍了两个字符串经典面试题 StringBuilder和StringBuffer的学习 一.String类概括二.StringBuilder和StringBuffer1.字符…

是未来的超级计算机还是只是一场炒作?

随着科技的飞速发展和创新&#xff0c;量子计算技术逐渐成为了人们关注的热点话题。量子计算作为一种前沿的计算方式&#xff0c;具有超强的运算能力和突破性的创新潜力&#xff0c;因此备受瞩目。然而&#xff0c;随着各大公司和机构纷纷加入到这一领域的竞争中&#xff0c;一…

超详细github配置(仔细看,看完不会,你怪我)

github的重要性&#xff1a; 网络时代的程序员必备。 github的作用&#xff1a; 版本管理多人协作开源共享 常用方案&#xff1a; gitTortoiseGitgithub [Tortoise&#xff0c;程序员常称其为小乌龟&#xff0c;小海龟] 安装配置步骤 1.注册 GitHub: Where the world bu…

服务(第二十二篇)主从复制和读写分离

主从复制原理&#xff1a; 首先主节点会开启二进制日志&#xff0c;从节点会开启中继日志&#xff0c;从节点会开启io线程检测主节点是否有更新&#xff0c;如果更新了就会向主节点请求二进制事件&#xff0c;主会开启dump线程发送二进制事件&#xff0c;然后保存在从节点的中…

假如面试官让你十分钟完成双向循环链表

&#x1f48c; 博客内容&#xff1a;假如面试官让你十分钟完成双向循环链表&#xff0c;多一秒都不行 &#x1f600; 作  者&#xff1a;陈大大陈 &#x1f680; 个人简介&#xff1a;一个正在努力学技术的准前端&#xff0c;专注基础和实战分享 &#xff0c;欢迎私信&…

大前端技能讲解:NodeJS、Npm、Es6、Webpack

文章目录 1. 基础概述2. Nodejs2.1 Nodejs 了解和快速入门2.2 Nodejs 实现 Httpserver 服务&#xff08;实现请求响应&#xff09;2.3 Nodejs 操作 MySQL 数据库 3. ES63.1 ES6 的概述3.2 ES6 的语法&#xff1a;let 和 const 命令3.3 ES6 的语法&#xff1a;模板字符串3.4 ES6…

基于SSM的在线电影购票系统设计与实现【附源码】

基于SSM的在线电影购票系统设计与实现 互联网的不断迅猛发展&#xff0c;每个行业都在寻找新的机会&#xff0c;都在从传统的人工方式向先进的信息化过度。随着人民生活水平的提高伴随的精神文化层次的享受&#xff0c;而现代互联网时代人们的重要精神消费之一是电影行业&…

NAS +AList实现云盘映射(本地硬盘扩容大法)

准备工具&#xff1a; 1&#xff09;Alist的docker &#xff1a;xhofe/alist 2&#xff09;RailDrive软件 安装&#xff1a; 1&#xff09;安装alist的docker 注意一定要给读写权限&#xff0c;装载路径和我一样 端口一般和容器端口一致 环境变量 网络桥接就行 记得勾选自…

【Prompting】ChatGPT Prompt Engineering开发指南(1)

ChatGPT Prompt Engineering开发指南1 Prompting指南设置 提示原则策略1&#xff1a;使用分隔符清楚地指示输入的不同部分策略2&#xff1a;要求结构化输出策略3&#xff1a;让模型检查条件是否满足策略4: “Few-shot”提示 原则2&#xff1a;给模型时间“思考”策略1&#xff…

idea新建springboot项目并提交码云仓库

新建springboot项目 平常我们在使用联网方式新建springboot项目时总是会遇到连接失败等这种情况 IDEA创建项目&#xff0c;本质是从官网创建并下载项目&#xff0c;然后导入本地。 创建项目连接失败&#xff0c;一般是外国网站的原因导致连接超时&#xff0c;解决方式很简单&a…

C++linux高并发服务器项目实践 day11

Clinux高并发服务器项目实践 day11 线程同步互斥锁死锁读写锁读写锁相关操作函数 生产者消费者模型条件变量信号量 线程同步 线程的主要优势在于&#xff0c;能够通过全局变量来共享信息。不过&#xff0c;这种便捷的共享是有代价的:必须确保多个线程不会同时修改同一变量&…

LabVIEWCompactRIO 开发指南17 网络流

LabVIEWCompactRIO 开发指南17 网络流 网络流类似于队列函数&#xff0c;因为它们是基于FIFO的&#xff0c;但与队列函数不同的是&#xff0c;网络流具有网络作用域。它们是为通过以太网进行无损、高吞吐量数据通信而设计和优化的&#xff0c;并且它们具有增强的连接管理功能…

Springboot +Flowable,各种历史信息如何查询(三)

一.简介 正在执行的流程信息是保存在以 ACT_RU_ 为前缀的表中&#xff0c;执行完毕的流程信息则保存在以 ACT_HI_ 为前缀的表中&#xff0c;也就是流程历史信息表。 假设有一个流程&#xff0c;流程图如下&#xff1a; 当这个流程执行完毕后&#xff0c;以 ACT_RU_ 为前缀的…

学习新技术,争做新青年:请ChatGPT帮我写一篇计算机视觉分类算法论文

文章目录 学习新技术&#xff0c;争做新青年&#xff1a;你不会还不用 ChatGPT 吧&#xff1f;学习新技术请告诉我最好的图像分类模型是哪个请推荐最新的分类模型是哪个请详细介绍一下 Swin Transformer请给出Swin Transformer的论文链接请帮我分析一下Swin Transformer 的创新…

Java实现多线程操作多账户

前言 某公司一个面试题&#xff1a; 1.有二十个账户&#xff0c;每个账户初始余额10000元。 2.有十个转账线程&#xff0c;对二十个账户中的两个随机选取账户进行转账&#xff0c;转账额度100以内正整数随机数。 3.每个线程执行100次转账操作。 4.最后请打印出二十个账户的…

西门子PLC控制步进电机方法与接线(全)

一、步进驱动系统 步进驱动系统包含步进电动机和步进驱动器&#xff0c;前端由PLC发脉冲。 步进电机是将电脉冲信号转变为角位移或线位移以控制转子转动的开环控制电机&#xff08;可以通过安装编码器形成闭环系统&#xff09;。 它旋转是以固定的角度&#xff08;步距角&…

ThinkPHP6的控制器定义及控制器初使用

ThinkPHP6的控制器定义及控制器初使用 控制器定义 控制器文件通常放在controller下面&#xff0c;类名和文件名保持大小写一致&#xff0c;并采用驼峰命名&#xff08;首字母大写&#xff09;。 如果要改变controller目录名&#xff0c;需要在route.php(config/route.php)配…

redis从零开始(1)----五种基本类型:string/hash

认识redis NoSQL Nosql not only sql&#xff0c;泛指非关系型数据库&#xff0c;与之相对的是RDBMS(Relational Database Management System)&#xff0c;即关系型数据库 关系型数据库&#xff1a;列行&#xff0c;同一个表下数据的结构是一样的。 非关系型数据库&#xff…

原生js手动实现一个多级菜单效果(高度可过渡变化)

文章目录 学习链接效果图代码要点 学习链接 vue实现折叠展开收缩动画 - 自己的链接 elment-ui/plus不定高度容器收缩折叠动画组件 - 自己的链接 Vue transition 折叠类动画自动获取隐藏层高度以及手风琴效果实现 vue transition动画钩子- vue官网 vue transition 过渡动画…

vue基础入门

1. vue简介 1.1 什么是vue 官方概念&#xff1a;Vue&#xff08;读音/vju:/&#xff0c;类似于view&#xff09;是一套用于构建用户界面的前端框架 1.2 vue 的特性 vue 框架的特性&#xff0c;主要体现在如下两方面&#xff1a; ① 数据驱动视图 ② 双向数据绑定 数据驱动…