第24章 互斥锁实验(iTOP-RK3568开发板驱动开发指南 )

news2024/11/29 20:54:55

在上一章节中对信号量进行了学习,而本章节要学习的互斥锁可以说是“量值”为 1 的信号量,最终实现的效果相同,既然有了信号量,那为什么还要有互斥锁呢,带着疑问,让我们来进行本章节的学习吧!

24.1 互斥锁

在上一章节中,将信号量量值设置为1,最终实现的就是互斥效果,与本章节要学习的互斥锁功能相同,虽然两者功能相同但是具体的实现方式是不同的,但是使用互斥锁效率更高、更简洁,所以如果使用到的信号量“量值”为 1,一般将其修改为使用互斥锁实现。

当有多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制。线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。互斥锁为资源引入一个状态:锁定或者非锁定。某个线程要更改共享数据时,先将其锁定,此时资源的状态为“锁定”,其他线程不能更改;直到该线程释放资源,将资源的状态变成“非锁定”,其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性,能够保证多个线程访问共享数据不会出现资源竞争及数据错误。

为了方便大家理解,这里举个例子来说明。比如公司部门里,我在使用着打印机打印东西的同时(还没有打印完),别人刚好也在此刻使用打印机打印东西,如果不做任何处理的话,打印出来的东西肯定是错乱的。那么怎么解决这种情况呢?只要我在打印着的时候别人是不允许打印的,只有等我打印结束后别人才允许打印。这个过程有点类似于,把打印机放在一个房间里,给这个房间安把锁,这个锁默认是打开的。当 A 需要打印时,他先过来检查这把锁有没有锁着,没有的话就进去,同时上锁在房间里打印。而在这时,刚好 B 也需要打印,B 同样先检查锁,发现锁是锁住的,他就在门外等着。而当 A 打印结束后,他会开锁出来,这时候 B 才进去上锁打印。看了这个例子,相信大家已经理解了互斥锁。

互斥锁会导致休眠,所以在中断里面不能用互斥锁。同一时刻只能有一个线程持有互斥锁,并且只有持有者才可以解锁,并且不允许递归上锁和解锁。

内核中以mutex结构体来表示互斥体,定义在“内核源码/include/linux/mutex.h”文件中,如下所示:

struct mutex {
     atomic_long_t       owner;
     spinlock_t      wait_lock;
 #ifdef CONFIG_MUTEX_SPIN_ON_OWNER
     struct optimistic_spin_queue osq; /* Spinner MCS lock */
 #endif
     struct list_head    wait_list;
 #ifdef CONFIG_DEBUG_MUTEXES
     void            *magic;
 #endif
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
     struct lockdep_map  dep_map;
 #endif
 };

一些和互斥体相关的API函数也定义在mutex.h文件中,常用API函数如下(表24-1)所示:

函数描述
DEFINE_MUTEX(name)定义并初始化一个 mutex 变量。
void mutex_init(mutex *lock)初始化 mutex。
void mutex_lock(struct mutex *lock)获取 mutex,也就是给 mutex 上锁。如果获取不到就进休眠。
void mutex_unlock(struct mutex *lock)释放 mutex,也就给 mutex 解锁。
int mutex_is_locked(struct mutex *lock)判断 mutex 是否被获取,如果是的话就返回1,否则返回 0。

表 24-1

至此,关于互斥体相关的知识就讲解完成了,在下一小节的实验中会对上述API函数进行运用。

24.2 实验程序的编写

24.2.1 驱动程序编写

本实验对应的网盘路径为:iTOP-RK3568开发板【底板V1.7版本】\03_【iTOP-RK3568开发板】指南教程\02_Linux驱动配套资料\04_Linux驱动例程\19\module。

本小节实验将使用互斥体对19章的并发与竞争实验进行改进,由于互斥体在同一时间内只允许一个任务对共享资源进行,所以除了在atomic_init()函数内加入初始化互斥锁函数之外,只需要在open()函数中加入互斥锁加锁函数,在release()函数中加入互斥锁解锁函数即可。

编写完成的mutex.c代码如下所示

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/mutex.h>

struct mutex mutex_test;//定义mutex类型的互斥锁结构体变量mutex_test
static int open_test(struct inode *inode,struct file *file)
{
	printk("\nthis is open_test \n");
	mutex_lock(&mutex_test);//互斥锁加锁
	return 0;
}

static ssize_t read_test(struct file *file,char __user *ubuf,size_t len,loff_t *off)
{
	int ret;
	char kbuf[10] = "topeet";//定义char类型字符串变量kbuf
	printk("\nthis is read_test \n");
	ret = copy_to_user(ubuf,kbuf,strlen(kbuf));//使用copy_to_user接收用户空间传递的数据
	if (ret != 0){
		printk("copy_to_user is error \n");
	}
	printk("copy_to_user is ok \n");
	return 0;
}
static char kbuf[10] = {0};//定义char类型字符串全局变量kbuf
static ssize_t write_test(struct file *file,const char __user *ubuf,size_t len,loff_t *off)
{
	int ret;
	ret = copy_from_user(kbuf,ubuf,len);//使用copy_from_user接收用户空间传递的数据
	if (ret != 0){
		printk("copy_from_user is error\n");
}
	if(strcmp(kbuf,"topeet") == 0 ){//如果传递的kbuf是topeet就睡眠四秒钟
		ssleep(4);
	}
	else if(strcmp(kbuf,"itop") == 0){//如果传递的kbuf是itop就睡眠两秒钟
		ssleep(2);
	}
	printk("copy_from_user buf is %s \n",kbuf);
	return 0;
}
static int release_test(struct inode *inode,struct file *file)
{
	mutex_unlock(&mutex_test);//互斥锁解锁
printk("\nthis is release_test \n");
	return 0;
}

struct chrdev_test {
       dev_t dev_num;//定义dev_t类型变量dev_num来表示设备号
       int major,minor;//定义int类型的主设备号major和次设备号minor
       struct cdev cdev_test;//定义struct cdev 类型结构体变量cdev_test,表示要注册的字符设备
       struct class *class_test;//定于struct class *类型结构体变量class_test,表示要创建的类
};
struct chrdev_test dev1;//创建chrdev_test类型的
struct file_operations fops_test = {
      .owner = THIS_MODULE,//将owner字段指向本模块,可以避免在模块的操作正在被使用时卸载该模块
      .open = open_test,//将open字段指向open_test(...)函数
      .read = read_test,//将read字段指向read_test(...)函数
      .write = write_test,//将write字段指向write_test(...)函数
      .release = release_test,//将release字段指向release_test(...)函数
};

static int __init atomic_init(void)
{
	mutex_init(&mutex_test);//对互斥体进行初始化
	if(alloc_chrdev_region(&dev1.dev_num,0,1,"chrdev_name") < 0 ){//自动获取设备号,设备名chrdev_name
		printk("alloc_chrdev_region is error \n");
	}
	printk("alloc_chrdev_region is ok \n");
	dev1.major = MAJOR(dev1.dev_num);//使用MAJOR()函数获取主设备号
	dev1.minor = MINOR(dev1.dev_num);//使用MINOR()函数获取次设备号
	printk("major is %d,minor is %d\n",dev1.major,dev1.minor);
	cdev_init(&dev1.cdev_test,&fops_test);//使用cdev_init()函数初始化cdev_test结构体,并链接到fops_test结构体
	dev1.cdev_test.owner = THIS_MODULE;//将owner字段指向本模块,可以避免在模块的操作正在被使用时卸载该模块
	cdev_add(&dev1.cdev_test,dev1.dev_num,1);//使用cdev_add()函数进行字符设备的添加
	dev1.class_test = class_create(THIS_MODULE,"class_test");//使用class_create进行类的创建,类名称为class_test
	device_create(dev1.class_test,0,dev1.dev_num,0,"device_test");//使用device_create进行设备的创建,设备名称为device_test
	return 0;
}

static void __exit atomic_exit(void)
{
	device_destroy(dev1.class_test,dev1.dev_num);//删除创建的设备
	class_destroy(dev1.class_test);//删除创建的类
	cdev_del(&dev1.cdev_test);//删除添加的字符设备cdev_test
	unregister_chrdev_region(dev1.dev_num,1);//释放字符设备所申请的设备号
	printk("module exit \n");
}
module_init(atomic_init);
module_exit(atomic_exit)
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("topeet");

24.2.2 编写测试 APP

本实验应用程序对应的网盘路径为:iTOP-RK3568开发板【底板V1.7版本】\03_【iTOP-RK3568开发板】指南教程\02_Linux驱动配套资料\04_Linux驱动例程\19\app。

本测试app代码和上一章节相同,需要输入两个参数,第一个参数为对应的设备节点,第二个参数为“topeet”或者“itop”,分别代表向设备写入的数据,编写完成的应用程序app.c内容如下所示:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
 #include <unistd.h>
int main(int argc, char *argv[])
{
	int fd;//定义int类型的文件描述符
	char str1[10] = {0};//定义读取缓冲区str1
	fd = open(argv[1],O_RDWR);//调用open函数,打开输入的第一个参数文件,权限为可读可写
	if(fd < 0 ){
		printf("file open failed \n");
		return -1;
	}
	/*如果第二个参数为topeet,条件成立,调用write函数,写入topeet*/    
	if (strcmp(argv[2],"topeet") == 0 ){
		write(fd,"topeet",10);
	}
	/*如果第二个参数为itop,条件成立,调用write函数,写入itop*/  
	else if (strcmp(argv[2],"itop") == 0 ){
		write(fd,"itop",10);
	}
	close(fd); 
	return 0;
}

24.3 运行测试

24.3.1 编译驱动程序

在上一小节中的mutex.c代码同一目录下创建 Makefile 文件,Makefile 文件内容如下所示:

export ARCH=arm64#设置平台架构
export CROSS_COMPILE=aarch64-linux-gnu-#交叉编译器前缀
obj-m += mutex.c    #此处要和你的驱动源文件同名
KDIR :=/home/topeet/Linux/linux_sdk/kernel    #这里是你的内核目录                                                                                                                            
PWD ?= $(shell pwd)
all:
    make -C $(KDIR) M=$(PWD) modules    #make操作
clean:
    make -C $(KDIR) M=$(PWD) clean    #make clean操作

对于Makefile的内容注释已在上图添加,保存退出之后,来到存放mutex.c和Makefile文件目录下,如下图(图24-2)所示:

img

图 24-2

然后使用命令“make”进行驱动的编译,编译完成如下图(图24-3)所示:

img

图24-3

编译完生成mutex.ko目标文件,如下图(图 24-4)所示:

img

图 24-4

至此驱动模块就编译成功了,下面进行应用程序的编译。

24.3.2 编译应用程序

来到应用程序app.c文件的存放路径如下图(图 24-5)所示:

img

图 24-5

然后使用以下命令对app.c进行交叉编译,编译完成如下图(图 24-6)所示:

aarch64-linux-gnu-gcc -o app app.c -static

img

图 24-6

生成的app文件就是之后放在开发板上运行的可执行文件,至此应用程序的编译就完成了。

24.3.3 运行测试

开发板启动之后,使用以下命令进行驱动模块的加载,如下图(图24-7)所示:

insmod mutex.ko

img

图 24-7

可以看到申请的主设备号和次设备号就被打印了出来,然后使用以下代码对自动生成的设备节点device_test进行查看,如下图(图24-8)所示:

ls /dev/device_test

img

图 24-8

可以看到device_test节点已经被自动创建了,然后使用以下命令运行测试app,运行结果如下图(图24-9)所示:

./app /dev/device_test topeet

img

图 24-9

可以看到传递的buf值为topeet,然后输入以下命令在后台运行两个app,来进行竞争测试,运行结果如下图(图24-10)所示:

./app /dev/device_test topeet &

./app /dev/device_test itop 

img

图 24-10

​ 与23章实验测试现象相同,两个app被同时运行,最终打印信息正常,证明数据被正确传递了,没有发生共享资源的竞争,证明互斥量就起到了作用。

最后可以使用以下命令进行驱动的卸载,如下图(图24-11)所示:

rmmod mutex.ko

img

图 24-11

至此,互斥体实验就完成了。

【最新驱动资料(文档+例程)】

链接 https://pan.baidu.com/s/1M4smUG2vw_hnn0Hye-tkog

提取码:hbh6

【B 站配套视频】

https://b23.tv/XqYa6Hm

【RK3568 购买链接】

https://item.taobao.com/item.htm?spm=a1z10.5-c-s.w4002-2245

图(图24-11)所示:

rmmod mutex.ko

[外链图片转存中…(img-wN8EGjVm-1694222715660)]

图 24-11

至此,互斥体实验就完成了。

【最新驱动资料(文档+例程)】

链接 https://pan.baidu.com/s/1M4smUG2vw_hnn0Hye-tkog

提取码:hbh6

【B 站配套视频】

https://b23.tv/XqYa6Hm

【RK3568 购买链接】

https://item.taobao.com/item.htm?spm=a1z10.5-c-s.w4002-2245

2452613.11.2fec74a6elWNeA&id=669939423234

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

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

相关文章

古尔曼表示不服?郭明錤:苹果可能不会在10月发布M3芯片的机型

9月9日消息&#xff0c;据天风证券分析师郭明錤所言&#xff0c;苹果可能不会在今年发布搭载M3芯片的MacBook Air/Pro机型。这一说法与此前彭博社的马克古尔曼所透露的消息有所不同。根据古尔曼的消息&#xff0c;苹果最快在10月会发布M3款苹果MacBook Air和Pro电脑。他表示&am…

美国封锁激励中国制造业数字化转型的崛起 | 百能云芯

上海在近日公布了第二批工赋链主培育企业名单&#xff0c;共有15家企业入选。这些被称为“链主”的企业在上海制造业数字化转型的过程中扮演着关键角色&#xff0c;类似于领头大雁&#xff0c;它们是上海制造业的数字化网络中的关键节点。 中新社的报道指出&#xff0c;“数字技…

软件源码开发,网络中的“摄像头”:运维监控系统

在日常生活中&#xff0c;我们不管是在大街小巷&#xff0c;还是在商场大厦都可以见到一个圆形或是方形带有镜片的“小盒子”&#xff0c;这个“小盒子”就是摄像头&#xff0c;摄像头作为一个能实时录制记录它能照到范围内的视频图像的工具&#xff0c;可以在丢失物品、抓捕坏…

判断动物知识竞猜答案正误

判断动物知识竞猜答案正误 教学目标 1&#xff0e; 知识与技能&#xff1a; 结合实例&#xff0c;理解选择结构。掌握if语句的基本格式&#xff0c;掌握关系运算符。 过程与方法&#xff1a; 学会使用if编程解决实际生活中的一些问题。 情感态度与价值观&#xff1a; 教…

通讯软件018——分分钟学会UaExpert OPC UA Client配置

本文介绍如何配置UaExpert OPC UA Client&#xff0c;通过本文可以对OPC UA的基本概念有所了解&#xff0c;掌握OPC UA的本质。相关软件请登录网信智汇(wangxinzhihui.com)。 创建OPC UA 连接 这里需要掌握一下OPC UA的安全机制。 1&#xff09;安全模式&#xff1a; OPC UA安…

史上最详细的PyCharm安装教程,小白建议收藏!

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。PyCharm是由JetBrains公司开发的一款Python开发工具&#xff0c;在Windows、Mac OS和Linux操作系统中都可以使用&#xff0c;它具有语法高亮显示、Project&#xff08;项目&#xff09;管理、代码跳转、智能提示、自动完成…

初识集合框架 -Java

目录 一、集合框架的概念 二、集合框架的重要性 三、涉及的数据结构和算法 3.1 什么是数据结构 3.2 集合框架&#xff08;容器&#xff09;背后对应的数据结构 3.3 相关的Java知识 3.4 什么是算法 3.5 如何学好数据结构和算法 一、集合框架的概念 Java 集合框架&#xff0c;…

【图卷积神经网络】1-入门篇:为什么使用图神经网络(下)

为什么使用图神经网络? 在本书中,我们将重点介绍图学习技术中的深度学习家族,通常称为图神经网络。GNNs是一种新的深度学习架构类别,专门设计用于处理图结构化数据。与主要用于文本和图像的传统深度学习算法不同,GNNs明确地用于处理和分析图数据集(见图1.4)。 图1.4 - …

Vue3+Ts+Vite项目(第一篇)——使用Vite创建Vue3项目

概述 保姆级详解&#xff0c;带你使用 Vite 创建 Vue3 项目&#xff0c;全程cv即可 文章目录 概述一、 安装 Vite二、 创建项目2.1 运行上述命令后&#xff0c;会让我们输入项目名称。可以写一个 vue3-study2.2 选择项目模板&#xff0c;此处选择 Vue&#xff0c;然后回车确定…

无涯教程-JavaScript - IMPOWER函数

描述 IMPOWER函数以x yi或x yj文本格式返回加到幂的复数。求幂的复数的计算方法如下- $$(x yi)^ n r ^ ne ^ {n \theta} r ^ n \cos n \theta ir ^ n sin n \theta $$ 哪里- $$r \sqrt {x ^ 2 y ^ 2} \:\:和\:\:\theta \tan ^ {-1} \left(\frac {y} {x} \right)\:…

雅思写作 三小时浓缩学习顾家北 笔记总结(四)

目录 The company should provide maternity leave and other assistance to female employees with children. Community redevelopment provides opportunities for offenders to acquire vocational skills. The law should classify drunk driving as a criminal offens…

JavaScript对象方法

在 JavaScript 中&#xff0c;对象可以包含方法&#xff0c;即函数作为它的属性。这些被称为对象函数或方法。 例如&#xff1a; const ITshareArray {firstname: "张三",secondname: "二愣子",birthYear: "1996",job: "程序员",fri…

多元共进|科技促进艺术发展,助力文化传承

科技发展助力文化和艺术的传播 融合传统与创新&#xff0c;碰撞独特魅力 一起来了解 2023 Google 开发者大会上 谷歌如何依托科技创新 推动艺术与文化连接 传承和弘扬传统文化 自 2011 年成立以来&#xff0c;谷歌艺术与文化致力于提供体验艺术和文化的新方式&#xff0c;从生成…

SpringAOP面向切面编程

文章目录 一. AOP是什么&#xff1f;二. AOP相关概念三. SpringAOP的简单演示四. SpringAOP实现原理 一. AOP是什么&#xff1f; AOP&#xff08;Aspect Oriented Programming&#xff09;&#xff1a;面向切面编程&#xff0c;它是一种编程思想&#xff0c;是对某一类事情的集…

热迁移技术-QEMU

社区有言Talk is cheep, show me the code&#xff0c;我们尽量低纬度描述技术。 代码和版本&#xff1a; Qemu-5.0 #热迁移技术的实现者 Kernel-4.19 #提供kvm实现 热迁移的演进 Qemu有加载保存vm的功能&#xff0c;这是两个互补的操作。保存状态就是为每个vm中运行的设备保存…

不同的二叉搜索树【动态规划】

不同的二叉搜索树 给你一个整数 n &#xff0c;求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种&#xff1f;返回满足题意的二叉搜索树的种数。 class Solution {//testpublic int numTrees(int n) {//初始化 dp 数组int[] dp new int[n 1];//初始化…

像素空间文生图之Imagen原理详解

论文:Photorealistic Text-to-Image Diffusion Models with Deep Language Understanding项目地址:https://imagen.research.google/代码(非官方):https://github.com/deep-floyd/IF模型权重:https://huggingface.co/DeepFloyd/IF-I-XL-v1.0🤗关注公众号 funNLPer 白嫖…

初识网络的发展史、通信基础和原理

目录 一.网络的发展史 二.网络通信基础 2.1IP地址 2.2端口号 2.3认识协议 2.3.1协议是什么&#xff1f; 2.3.2为什么需要协议&#xff1f; 2.3.3OSI模型和TCP/IP体系结构 三.网络通信的原理 总结 &#x1f381;个人主页&#xff1a;tq02的博客_CSDN博客-C语言,Java,J…

如何在海外通过A/B测试来优化应用

在App Store和Google Play上&#xff0c;视觉资产是应用商店优化(ASO)的重要组成部分。我们的游戏图标、屏幕截图和预览视频具有很高的转化潜力。 1、将应用页面元素的一个或多个变体与默认元素进行竞争。 借助A/B测试&#xff0c;我们可以轻松、客观地发现访问者的偏好。这样…

PPT架构师架构技能图

PPT架构师架构技能图 目录概述需求&#xff1a; 设计思路实现思路分析1.软素质2.核心输出&#xff08;office输出&#xff09; 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,ma…