第19章 并发与竞争实验(iTOP-RK3568开发板驱动开发指南 )

news2024/11/28 12:35:53

在前面章节的学习中,相信大家已经对用户空间与内核空间数据传递进行了实验,假如要传递的数据被存放在了全局变量,该数据就可以作为共享资源被多个任务共同读写,从而造成数据的错误传输,多个程序同时访问一个共享资源产生的问题就叫做竞争。竞争产生的根本原因就是Linux系统的并发访问。

在本章节中首先会对并发与并行的概念进行讲解,随后对竞争产生的原因进行总结,最后以一个实际的竞争实验加深大家的理解。下面就让我们开始本章节的学习吧。

19.1 并发与竞争

19.1.1并发

早期计算机大多只有一个CPU核心,一个CPU在同一时间只能执行一个任务,当系统中有多个任务等待执行时,CPU只能执行完一个再执行下一个。而计算机的很多指令会涉及I/O操作,执行速度远远低于CPU内高速存储器的存取速度,这就导致CPU经常处于空闲状态,只能等待I/O操作完成后才能继续执行后面的指令。为了提高CPU利用率,减少等待时间,提出了CPU并发工作理论。

所谓并发,就是通过算法将CPU资源合理地分配给多个任务,当一个任务执行 I/O 操作时,CPU可以转而执行其它的任务,等到 I/O 操作完成以后,或者新的任务遇到 I/O 操作时,CPU再回到原来的任务继续执行。

下图(图19-1)展示了两个任务并发执行的过程(为了容易理解,这里以两个任务并发执行为例,当然一个CPU核心并不仅仅只能两个任务并发):

img

图 19-1

虽然CPU在同一时刻只能执行一个任务,但是通过将CPU的使用权在恰当的时机分配给不同的任务,使得多个任务看起来是一起执行的(CPU的执行速度极快,多任务切换的时间也极短)。

至此关于并发的概念就讲解完成了。

19.1.2并行

并发是针对单核CPU提出的,而并行则是针对多核CPU提出的。和单核CPU不同,多核CPU真正实现了“同时执行多个任务”。多核CPU的每个核心都可以独立地执行一个任务,而且多个核心之间不会相互干扰。在不同核心上执行的多个任务,是真正地同时运行,这种状态就叫做并行。双核CPU的工作状态如下图(图19-2)所示:

img

图 19-2

双核CPU执行两个任务时,每个核心各自执行一个任务,和单核CPU在两个任务之间不断切换相比,它的执行效率更高。

至此对于并行的概念就讲解完成了。

19.1.3并发+并行

在并行的工作状态中,两个CPU分别执行两个任务,是一种理想状态。但是在实际场景中,处于运行状态的任务是非常多的,以实际办公电脑为例,windows系统在开机之后会运行几十个任务,而CPU往往只有4核、8核等,远远低于任务的数量,这个时候就会同时存在并发和并行两种情况,即所有核心在并行工作的同时,每个核心还要并发工作。

例如一个双核 CPU 要执行四个任务,它的工作状态如下图(图19-3)所示:

img

图 19-3

为了容易理解,这里是以两个任务并发执行为例,当然一个CPU核心并不仅仅只能两个任务并发,并发任务的数量和操作系统的分配方式、以及每个任务的工作状态有关系。

至此,对于并发+并行的概念讲解就结束了。

并发可以看作是并行的理想状态,为了便于讲解和避免产生歧义,之后的章节无论是并发还是并行,都会统称为并发。

19.1.4 竞争

并发可能会造成多个程序同时访问一个共享资源,这时候由并发同时访问一个共享资源产生的问题就叫做竞争。

竞争产生的原因如下所示:

(1)多线程的并发访问。由于Linux 是多任务操作系统,所以多线程访问是竞争产生的基本原因。

(2)中断程序的并发访问。中断任务产生后,CPU会立刻停止当前工作,从而去执行中断中的任务,如果中断任务对共享资源进行了修改,就会产生竞争。

(3)抢占式并发访问。linux2.6及更高版本引入了抢占式内核,高优先级的任务可以打断低优先级的任务。在线程访问共享资源的时候,另一个线程打断了现在正在访问共享资源的线程同时也对共享资源进行操作,从而造成了竞争。

(4)多处理器(SMP)并发访问。多核处理器之间存在核间并发访问。

19.1.5 共享资源的保护

竞争是由并发访问同一个共享资源产生的。为了防止“竞争”的产生就要对共享资源进行保护,这里提到的共享资源又是什么呢?

以实际生活中的共享资源为例,可以是公共电话,也可以是共享单车、共享充电宝等公共物品,以上都属于共享资源的范畴,以公共电话为例,每个人都可以对它进行使用,但在同一时间内只能由一个人进行使用,如果两个人都要对电话进行使用,则产生了竞争。而在实际的驱动的代码中,共享资源可以是全局变量,也可以是驱动中的设备结构体等,需要根据具体的驱动程序来进行分析。在下一小节的实验中,会以全局变量为例,进行并发与竞争实验。

19.2 实验程序的编写

19.2.1 驱动程序编写

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

本实验将编写并发与竞争的驱动代码,首先完善字符设备驱动框架,然后通过copy_from_user(…)函数接收用户空间传递到内核空间的数据并进行判断,如果接收到的字符串数据为“topeet”会在睡眠4秒钟后打印接收到的数据,如果接收到的字符串数据为“itop”会在睡眠2秒钟后打印接收到的数据。

编写完成的example.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>

static int open_test(struct inode *inode,struct file *file)
{
	printk("\nthis is open_test \n");
	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)
{
	//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)
{
	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");

对于重要逻辑部分已经加粗,后续章节的实验都是对上述并发与竞争实验的改进,以不同的方式来避免竞争的产生。

19.2.2 编写测试 APP

本实验应用程序对应的网盘路径为:iTOP-RK3568开发板【底板V1.7版本】\03_【iTOP-RK3568开发板】指南教程\02_Linux驱动配套资料\04_Linux驱动例程\14\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;
}

19.3 运行测试

19.3.1 编译驱动程序

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

export ARCH=arm64#设置平台架构
export CROSS_COMPILE=aarch64-linux-gnu-#交叉编译器前缀
obj-m += example.o    #此处要和你的驱动源文件同名
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的内容注释已在上图添加,保存退出之后,来到存放example.c和Makefile文件目录下,如下图所示:

img

图 19-4

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

img

图 19-5

编译完生成example.ko目标文件,如下图(图19-6)所示:

img

图 19-6

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

19.3.2 编译应用程序

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

img

图 19-7

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

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

img

图 19-8

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

19.3.3 运行测试

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

insmod example.ko

img

图 19-9

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

 ls /dev/device_test

img

图 19-10

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

./app /dev/device_test topeet

img

图 19-11

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

./app /dev/device_test topeet &

./app /dev/device_test itop &

img

图 19-12

在不存在竞争的情况下,传递的两个字符串数据应该是topeet和itop,而在上图中的打印信息为两个itop,原因是第二个app应用程序运行之后对共享资源进行了修改,两个app应用程序就产生了竞争关系,会在之后的章节中使用不同的方法对上述驱动程序进行改进,从而避免竞争的产生。

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

rmmod  example.ko

img

图 19-13

&


[外链图片转存中...(img-fE9a4Hvr-1694222498826)] 

图 19-12

在不存在竞争的情况下,传递的两个字符串数据应该是topeet和itop,而在上图中的打印信息为两个itop,原因是第二个app应用程序运行之后对共享资源进行了修改,两个app应用程序就产生了竞争关系,会在之后的章节中使用不同的方法对上述驱动程序进行改进,从而避免竞争的产生。

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

rmmod example.ko


[外链图片转存中...(img-HbEDhf8K-1694222498826)] 

图 19-13

至此,并发与竞争的实验就完成了。

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

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

相关文章

取个好名,是一生中极为重要之事

我们每十年一大运&#xff0c;五年一小运。每交次大运时&#xff0c;就会改变一次你身边的一切环境。每个大运&#xff0c;都会把你带入不同的风景&#xff0c;人生想要须尽欢&#xff0c;就不要太在意外在的一切&#xff0c;永远向内求。有志同道合的人就一起上路&#xff0c;…

《Tree of Thoughts: Deliberate Problem Solving with Large Language Models》中文翻译

《Tree of Thoughts: Deliberate Problem Solving with Large Language Models》- 思维树&#xff1a;用大型语言模型有意识地解决问题 论文信息摘要1. 介绍2. 背景3. 思想树&#xff1a;用 LM 有意识地解决问题4. 实验4.1 24 人游戏4.2 创意写作4.3 迷你填字游戏 5. 相关工作6…

语音芯片NRK3302 在按摩仪上的应用

在当今生活节奏快&#xff0c;生活压力大的现代社会。按摩仪已逐渐成为人们生活中不可或缺的日常用品&#xff0c;人们需要一个能够随时放松身体的工具。智能按摩仪应运而生&#xff0c;在按摩仪中加入语音芯片&#xff0c;让按摩仪变得更加智能&#xff0c;使用起来更加便利化…

私有云不是真正的云计算!

大数据产业创新服务媒体 ——聚焦数据 改变商业 中国云计算遇到困境&#xff0c;IaaS层面&#xff0c;阿里云、腾讯云等增长乏力&#xff1b;SaaS没有发展起来。反观美国&#xff0c;整个云计算蓬勃发展&#xff0c;AWS、微软云、谷歌云体量更大&#xff0c;增速却不低&#x…

YOLO目标检测——工地安全帽识别检测数据集+已标注yolo格式标签下载分享

实际项目应用&#xff1a;目标检测工地安全帽识别检测数据集在工地安全监测、工地管理、安全培训和教育、违规检测和预警以及安全统计和分析等领域都有着广泛的应用。通过准确识别和检测工人是否佩戴安全帽&#xff0c;可以帮助提高工地的安全性和管理效率&#xff0c;减少事故…

通过finalshell快速在ubuntu上安装jdk1.8

这篇文章主要介绍一下怎么通过finalshell连接ubuntu&#xff0c;然后在ubuntu上安装jdk1.8&#xff0c;让不熟悉linux操作系统的童鞋也能快速地完成安装。 目录 一、准备一台虚拟机 二、安装finalshell远程连接工具 三、获取ubuntu虚拟机的ip地址 四、通过finalshell连接u…

Spring以及SpringBoot/SpringCloud注解

一、SpringBoot/Spring 1、SpringBootApplication 包含Configuration、EnableAutoConfiguration、ComponentScan通常在主类上 其中ComponentScan让Spring Boot扫描到Configuration类并把它加入到程序上下文&#xff0c;如果扫描到有Component Controller Service等这些注解的…

vscode配置conda环境

vscode配置conda环境 写在最前面安装vscodeanaconda3 配置vscode中文vscode配置anaconda环境步骤 新建.ipynb项目 写在最前面 之前一直是jupyter notebookpycharm 帮朋友配置环境的时候发现&#xff1a;vscode结合了cell自动补齐&#xff0c;狠狠心动了 于是安装配置vscode 参…

数据结构和算法(3):列表

列表是一种线性数据结构&#xff0c;它允许在其中存储多个元素&#xff0c;并且可以动态地添加或删除元素。 循秩访问 可通过重载下标操作符&#xff0c;实现寻秩访问 template <typename T> // assert: 0 < r < size T List<T>::operator[](Rank r) cons…

easyrecovery 2023年最好用的数据恢复软件

EasyRecovery是一款操作简单、功能强大数据恢复软件,通过easyrecovery可以从硬盘、光盘、U盘、数码相机、手机等各种设备中恢复被删除或丢失的文件、图片、音频、视频等数据文件。 easyrecovery数据恢复软件&#xff0c;是国内顶尖工作室的技术杰作。它是一个硬盘数据恢复工具&…

[NLP]LLM---大模型指令微调中的“Prompt”

一 指令微调数据集形式太多 大家有没有分析过 prompt对模型训练或者推理的影响&#xff1f;之前推理的时候&#xff0c;发现不加训练的时候prompt&#xff0c;直接输入模型性能会变差的&#xff0c;这个倒是可以理解。假如不加prompt直接训练&#xff0c;是不是测试的时候不加…

1. Flink简述

Flink与Spark Streaming对比 数据模型和处理模型 ​ Spark 的数据模型是 RDD&#xff0c;很多时候 RDD 可以实现为分布式共享内存或者完全虚拟化&#xff08;即有的中间结果 RDD 当下游处理完全在本地时可以直接优化省略掉&#xff09;。这样可以省掉很多不必要的 I/O。 ​ …

LinkWeChat 私域管理平台基于企业微信的开源 SCRM

LinkWeChat 是国内首个基于企业微信的开源 SCRM&#xff0c;在集成了企微强大的开放能力的基础上&#xff0c;进一步升级拓展灵活高效的客户运营能力及多元化精准营销能力&#xff0c;让客户与企业之间建立强链接&#xff0c;帮助企业提高客户运营效率&#xff0c;强化营销能力…

ERROR: Failed building wheel for mpi4py

在深度学习虚拟环境中使用pip方式安装mpi4py时&#xff0c;出现错误&#xff1a; 无法安装成功时&#xff0c;可以尝试使用conda的方式&#xff1a;conda install mpi4py。

4. 广播变量

一、分区规则&#xff08;DataStream Broadcast&#xff09;和广播变量&#xff08;Flink Broadcast&#xff09; 1.1 DataStream Broadcast&#xff08;分区规则&#xff09; ​ 分区规则是把元素广播给所有的分区&#xff0c;数据会被重复处理。 DataStream.broadcast()1.…

揭秘#AI Grant 第二期项目,我是如何用AI获取灵感的?

hi&#xff0c;大家好&#xff0c;最近看到一篇文章&#xff0c;介绍了 AI版YC的二期项目&#xff0c;里面的项目非常值得我们去研究&#xff0c;推荐给大家&#xff1a; aigrant.com AI版YC 指的是 AI Grant&#xff0c;这是一家&#xff1a; 提供资金和支持的加速器项目由Nat…

Redis高并发分布式锁实战

高并发场景秒杀抢购超卖bug实战重现 秒杀抢购场景下实战JVM级别锁与分布式锁 大厂分布式锁Resisson框架实战 Lua脚本语言快速入门与使用注意事项 Redisson分布式锁源码剖析 Redis主从架构锁失效问题解析 从CAP角度剖析Redis与Zookeeper分布式锁区别 Redlock分布式锁原理与…

PostGreSQL:时间戳时区问题

时间|日期类型 PostGreSQL数据库内置的时间类型如下&#xff0c;注意到&#xff1a;内置的时间类型被分为了with time zone-带时区、without time zone-不带时区两种类型&#xff0c; time、timestamp和interval都可以接受一个可选的精度值 p&#xff08;取值&#xff1a;0-6&a…

探索云计算和大数据分析的崛起:API行业的机遇与挑战【电商大数据与电商API接入】

I. 引言 随着云计算和大数据分析技术的快速发展&#xff0c;企业和个人对数据分析和处理的需求不断增加。在这个信息爆炸的时代&#xff0c;数据已成为企业决策和战略规划的重要基础。云计算提供了强大的计算和存储能力&#xff0c;使得大规模数据的处理和分析变得更加容易和高…

Java 基于 SpringBoot 的高校点餐系统,附源码,数据库

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W,Csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 文章目录 1、效果演示2、 前言介绍3、主要技术4 系统设计4.1 系统概述**4.2 系统结构设计****4.3数据库设计…