驱动-自旋锁
前面原子操作进行了讲解, 并使用原子整形操作对并发与竞争实验进行了改进,但是原子操作只能对整形变量或者位进行保护, 而对于结构体或者其他类型的共享资源, 原子操作就力不从心了, 这时候就轮到自旋锁的出场了。
两个 app 应用程序之间对共享资源的竞争访问引起了数据传输错误, 而在 Linux 内核中, 提供了四种处理并发与竞争的常见方法:
分别是原子操作、 自旋锁、 信号量、 互斥体, 这里了解下原子操作
文章目录
- 自旋锁概念
- 工作原理
- 特点
- 常用API
- 参考资料
- 驱动-原子操作实验
- 实验源码 spinlock.c
- 部分源码解读
- Makefile 编译文件
- 测试程序 app.c
- 加载驱动 insmod
- 查看 dev 下生成的字符设备
- ./app 执行程序 验证
- 总结
自旋锁概念
自旋锁是一种低级的同步原语,用于在多处理器系统中保护共享资源。与互斥锁不同,当一个线程尝试获取已被占用的自旋锁时,它不会阻塞或睡眠,而是会在一个循环中不断检查锁的状态(即"自旋"),直到锁变为可用状态。
工作原理
-
尝试获取锁:线程尝试通过原子操作获取锁
-
锁可用:获取成功,线程继续执行临界区代码
-
锁被占用:线程进入忙等待循环,不断检查锁状态
-
锁释放:当锁持有者释放锁后,等待线程可以获取锁
特点
-
忙等待:不放弃CPU,持续检查锁状态
-
短临界区:适合保护执行时间很短的代码段
-
无上下文切换:避免了线程切换的开销
-
多处理器有效:在单处理器系统上可能浪费CPU周期
常用API
#include <linux/spinlock.h>// 定义和初始化
spinlock_t my_lock;
spin_lock_init(&my_lock);// 获取锁
spin_lock(&my_lock); // 获取锁,禁用本地 CPU 中断
spin_lock_irq(&my_lock); // 获取锁并禁用硬件中断
spin_lock_irqsave(&my_lock, flags); // 保存中断状态并获取锁// 释放锁
spin_unlock(&my_lock); // 释放锁
spin_unlock_irq(&my_lock); // 释放锁并恢复中断
spin_unlock_irqrestore(&my_lock, flags); // 释放锁并恢复之前的中断状态// 尝试获取锁
int spin_trylock(&my_lock); // 非阻塞尝试获取锁,成功返回非零
参考资料
接下来还是以前面字符设备 动态参数传递实验为基础,打开访问字符设备实验。 所以以前知识点 建议了解
在字符设备这块内容,所有知识点都是串联起来的,需要整体来理解,缺一不可,建议多了解一下基础知识
驱动-申请字符设备号
驱动-注册字符设备
驱动-创建设备节点
驱动-字符设备驱动框架
驱动-杂项设备
驱动-内核空间和用户空间数据交换
驱动-文件私有数据
Linux驱动之 原子操作
Linux驱动—原子操作
驱动-原子操作实验
实验源码 spinlock.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/atomic.h>
#include <linux/errno.h>static spinlock_t spinlock_test;//定义spinlock_t类型自旋变量
static int flag=1;static int open_test(struct inode *inode,struct file *file){spin_lock(&spinlock_test); //自旋枷锁if(flag!=1){ //判断标志位flag 的值是否等于1 spin_unlock(&spinlock_test); return -EBUSY;}flag=0; //将标志位的值设置为0spin_unlock(&spinlock_test); //自旋锁解锁printk("\n this 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类型字符串变量kbufprintk("\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");spin_lock(&spinlock_test);//自旋锁加锁flag = 1;spin_unlock(&spinlock_test);//自旋锁解锁 return 0;
}struct chrdev_test
{dev_t dev_num; //定义dev_t类型变量来表示设备号int major,minor; //定义int 类型的主设备号和次设备号struct cdev cdev_test; //定义字符设备struct class *class_test; //定义结构体变量class 类
};struct chrdev_test dev1; //创建chardev_test类型结构体变量static struct file_operations fops_test = {.owner=THIS_MODULE,//将owner字段指向本模块,可以避免在模块的操作正在被使用时卸载该模块.open = open_test,//将open字段指向chrdev_open(...)函数.read = read_test,//将open字段指向chrdev_read(...)函数.write = write_test,//将open字段指向chrdev_write(...)函数.release = release_test,//将open字段指向chrdev_release(...)函数
};//定义file_operations结构体类型的变量cdev_test_opsstatic int __init chrdev_fops_init(void)//驱动入口函数
{if(alloc_chrdev_region(&dev1.dev_num,0,1,"chrdev_name") < 0){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\n",dev1.major);printk("minor is %d\n",dev1.minor);使用cdev_init()函数初始化cdev_test结构体,并链接到cdev_test_ops结构体cdev_init(&dev1.cdev_test,&fops_test);dev1.cdev_test.owner = THIS_MODULE;//将owner字段指向本模块,可以避免在模块的操作正在被使用时卸载该模块 cdev_add(&dev1.cdev_test,dev1.dev_num,1);printk("cdev_add is ok\n");dev1.class_test = class_create(THIS_MODULE,"class_test");//使用class_create进行类的创建,类名称为class_testdevice_create(dev1.class_test,NULL,dev1.dev_num,NULL,"device_test");//使用device_create进行设备的创建,设备名称为device_testreturn 0;
}
static void __exit chrdev_fops_exit(void)//驱动出口函数
{cdev_del(&dev1.cdev_test);//使用cdev_del()函数进行字符设备的删除unregister_chrdev_region(dev1.dev_num,1);//释放字符驱动设备号 device_destroy(dev1.class_test,dev1.dev_num);//删除创建的设备class_destroy(dev1.class_test);//删除创建的类printk("module exit \n");}
module_init(chrdev_fops_init);//注册入口函数
module_exit(chrdev_fops_exit);//注册出口函数
MODULE_LICENSE("GPL v2");//同意GPL开源协议
MODULE_AUTHOR("wang fang chen "); //作者信息
部分源码解读
- 上面测试源码demo,完全还是基于之前字符设备的一套,和 原子操作实验代码完全一样,唯一区别是没有用原子操作来防止竞争,用了自旋锁来防止资源竞争
- 可以简要看一下 自旋锁这里是一堆一堆出现的。 spin_lock(&spinlock_test); //自旋枷锁 spin_unlock(&spinlock_test); //自旋锁解锁
就是枷锁 设置flag,然后解锁。 恢复flag 状态时候 就枷锁->设置flag 的值->解锁。
Makefile 编译文件
#!/bin/bash
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
obj-m += spinlock.o
KDIR :=/home/wfc123/Linux/rk356x_linux/kernel
PWD ?= $(shell pwd)
all:make -C $(KDIR) M=$(PWD) modulesclean:make -C $(KDIR) M=$(PWD) clean
测试程序 app.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main(int argc,char *argv[])
{int fd;//定义int类型的文件描述符char str1[10]={0};//定义读取缓冲区str1fd=open(argv[1],O_RDWR,0666);//调用open函数,打开输入的第一个参数文件,权限为可读可写//fd=open("/dev/device_test",O_RDWR,0666);//调用open函数,打开输入的第一个参数文件,权限为可读可写if(fd<0){printf("open is error\n");return -1;}printf("open is ok\n");if(strcmp(argv[2],"topeet")==0){write(fd,"topeet",sizeof(str1)); }else if(strcmp(argv[2],"itop")==0){write(fd,"itop",sizeof(str1)); }close(fd);//调用close函数,对取消文件描述符到文件的映射return 0;
}
编译 测试程序 app
aarch64-linux-gnu-gcc -o app app.c -static
加载驱动 insmod
执行命令,测试结果如下:

查看 dev 下生成的字符设备
ls /dev/device_test
[root@topeet:/mnt/sdcard]# ls /dev/device_test
/dev/device_test
[root@topeet:/mnt/sdcard]#
./app 执行程序 验证
root@topeet:/mnt/sdcard]# ./app /dev/device_test topeet &
de[root@topeet:/mnt/sdcard]# ./app /dev/device_test itopopen is okopen is error
这个就验证了 自旋锁 起作用了的,保护了 变量 flag
总结
这里简单介绍了自旋锁的应用场景,需要了解原理和使用api 即可。
相关文章:
驱动-自旋锁
前面原子操作进行了讲解, 并使用原子整形操作对并发与竞争实验进行了改进,但是原子操作只能对整形变量或者位进行保护, 而对于结构体或者其他类型的共享资源, 原子操作就力不从心了, 这时候就轮到自旋锁的出场了。 两个…...
10.(vue3.x+vite)div实现tooltip功能(css实现)
1:效果截图 2:代码实现 <template><div><div class="tooltip" style="margin-top: 20%; margin-left: 20%; background-color: blueviolet; color: white;...
使WebSocket 稳定可靠,需要考虑的方向
文章目录 1. 连接管理2. 心跳检测3. 重连机制4. 消息队列5. 错误处理6. 资源管理7. 安全性8. 状态同步 示例代码1. 添加依赖2. WebSocket 客户端实现代码注释功能标注3. 安卓端使用MainActivity.java布局文件(activity_main.xml) 4. 后端(Fla…...
Linux:进程:进程调度
进程在CPU上运行具有以下特性: 竞争、独⽴、并⾏、并发 竞争性:系统进程数⽬众多,⽽CPU资源很少甚至只有一个,所以进程之间是具有竞争属性的。为 了⾼效完成任务,更合理竞争相关资源,便具有了优先级 独⽴性: 为了避…...
Stable Diffusion 图像生成 GUI 应用:图像缩放等五个优化——SD界面学习记录
本篇续前面Stable DiffusionPyqt5实现图像生成和管理界面,链接如下: Stable DiffusionPyqt5: 实现图像生成与管理界面(带保存 历史记录 删除功能)——我的实验记录(结尾附系统效果图)-CSDN博客…...
职坐标解码互联网行业转型发展新动能
当前,互联网行业正以前所未有的速度重塑全球产业格局。工信部最新数据显示,我国互联网企业营收连续三年保持双位数增长,其中百强企业在人工智能、物联网等领域的投入强度同比提升40%,展现出强劲的技术引领力。与此同时,…...
【含文档+PPT+源码】基于微信小程序的非遗文化黄梅戏宣传平台的设计与实现
课程目标: 教你从零开始部署运行项目,学习环境搭建、项目导入及部署,含项目源码、文档、数据库、软件等资料 课程简介: 本课程演示的是一款基于微信小程序的非遗文化黄梅戏宣传平台的设计与实现,主要针对计算机相关…...
Causal Attention的底层原理
Causal Attention Transformer的Decoder中最显著的结构是Casual Attention。 通过本篇文章,你将学会 Casual Attention的机制原理 Casual Attention在TensorFlow中的实现原理 如何快速地保存并打印TensorFlow中模型已经训练好的参数 如何实现Transformer的Dec…...
深入理解类:ArkTS面向对象编程的核心概念
# 深入理解类:ArkTS面向对象编程的核心概念 在编程世界里,面向对象编程(OOP)是一种强大的编程范式,而类则是OOP的核心构建块。在ArkTS语言中,类的设计和使用对于构建复杂、可维护的应用程序至关重要。今天…...
AI 驱动下的后端开发架构革命:从智能协同体系
AI 驱动下的后端开发架构革命:从智能协同体系 一、引言:AI 重构后端开发范式 在 2025 年的企业级技术演进中,人工智能正从辅助工具升级为核心架构要素。根据 Gartner《2025 智能技术栈成熟度报告》,传统 "人力编码 硬规则…...
vue3 Ts axios 封装
vue3 Ts axios 封装 axios的封装 import axios, { AxiosError, AxiosInstance, InternalAxiosRequestConfig, AxiosResponse, AxiosRequestConfig, AxiosHeaders } from axios import qs from qs import { config } from ./config import { ElMessage } from element-plus// …...
CyberAgentAILab 开源数字人项目TANGO,heygen的开源版来了~
简介 TANGO 是 CyberAgentAILab 开源的一项前沿研究成果,其初衷在于探索高效生成模型在实际应用场景中的表现。项目诞生于 CyberAgent 在整合创意与人工智能的实践中,旨在为数字内容生成、交互和实时渲染等领域提供一个高性能、模块化、可扩展的解决方案…...
ROS ROS2 机器人深度相机激光雷达多传感器标定工具箱入门教程(一)
系列文章目录 目录 系列文章目录 前言 一、安装 1.1 ROS 2 官方软件包 二、教程 2.1 标定配置器 2.1.1 机器人选项 2.1.2.1 外参相机-激光雷达标定 2.1.2.2 外参激光雷达-激光雷达标定 2.1.2.3 外参相机参照标定 2.1.2.4 外参激光雷达-参考标定 2.2 外参照相机-激…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(6):ながら 一边。。一边
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(6):ながら 一边。。一边 1、前言(1)情况说明(2)工程师的信仰 2、知识点(1)ながら1)一边。。一边2࿰…...
从EOF到REOF:如何用旋转经验正交函数提升时空数据分析精度?
目录 1. 基本概念与原理2. 应用场景3. 与传统EOF的区别4. 技术实现5. 其他领域中的“REOF”参考资料 REOF 的输入是多个地区在不同时间的气候数据(如温度或降雨量),它的作用是通过旋转计算找出这些数据中最主要的变化规律,输出则是…...
【HDFS入门】HDFS与Hadoop生态的深度集成:与YARN、MapReduce和Hive的协同工作原理
目录 引言 1 HDFS核心架构回顾 2 HDFS与YARN的集成 3 HDFS与MapReduce的协同 4 HDFS与Hive的集成 4.1 Hive架构与HDFS交互 4.2 Hive数据组织 4.3 Hive查询执行流程 5 HDFS在生态系统中的核心作用 6 性能优化实践 7 总结 引言 在大数据领域,Hadoop生态系统…...
用 AI 十天开发小程序:探秘 “幸运塔塔屋” 之 “解惑指南书” 功能
在当今软件开发领域,AI 技术正以前所未有的速度改变着我们的开发方式。我仅用十天时间,借助 AI 成功开发出 “幸运塔塔屋” 小程序,其中 “解惑指南书” 功能别具一格。今天,就为大家详细剖析这个功能从构思到落地的全过程。 十天…...
直流电源基本原理
整流电路 在构建整流电路时,要选择合适参数的二极管 If是二极管能够通过电流的能力,也是最大整流的平均电流。 还要考虑二极管的反向截至电压。 脉动系数电压交流幅值/直流平均电压(越小越好) 三相整流电路优点: …...
osu ai 论文笔记 DQN
e https://theses.liacs.nl/pdf/2019-2020-SteeJvander.pdf Creating an AI for the Rhytm Game osu! 20年的论文 用监督学习训练移动模型100首歌能达到95准确率 点击模型用DQN两千首歌65准确率 V抖用的居然不是强化学习? 5,6星打96准确度还是有的东西的 这是5.…...
MapReduce实验:分析和编写WordCount程序(对文本进行查重)
实验环境:已经部署好的Hadoop环境 Hadoop安装、配置与管理_centos hadoop安装-CSDN博客 实验目的:对输入文件统计单词频率 实验过程: 1、准备文件 test.txt文件,它是你需要准备的原始数据文件,存放在你的 Linux 系…...
Windows Acrobat Pro DC-v2025.001.20435-x64-CN-Portable便携版
Windows Acrobat Pro 链接:https://pan.xunlei.com/s/VOO1nMjQ1Qf53dyISGne0c_9A1?pwdsfgn# Acrobat Pro 2024 专业增强版特色 ● 创建和编辑 PDF 文件:可以将各种类型的文档转换为 PDF 格式,并进行编辑和修改。 ● 合并和拆分 PDF&#…...
二十、FTP云盘
1、服务端 #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <netinet/in.h>…...
【4】k8s集群管理系列--harbor镜像仓库本地化搭建
一、harbor基本概念 Harbor是一个由VMware开源的企业级Docker镜像仓库解决方案,旨在解决企业在容器化应用部署中的痛点,提供镜像存储、管理、安全和分发的全生命周期管理。Harbor扩展了Docker Registry,增加了企业级功能,如…...
Oracle 12.1.0.2补丁安装全流程
第一步,先进行备份 tar -cvf u01.tar /u01 第二步,更新OPatch工具包 根据补丁包中readme信息汇总提示的信息,下载对应版本的OPatch工具包,本次下载的版本为: p6880880_122010_Linux-x86-64.zip opatch版本为最新的…...
【AAOS】【源码分析】Car UX Restrictions
AAOS UX的核心理念:安全驾驶是驾驶员的首要责任。汽车制造商和应用程序开发人员的所有设计都必须反映这一优先事项。 AAOS平台允许设备制造商(OEM)对不同驾驶状态下的限制进行定制。 驾驶员分心指南 只有符合Driver Distraction Guidelines的应用才可以在驾驶过程中运行。…...
解读《人工智能指数报告 2025》:洞察 AI 发展新态势
美国斯坦福大学 “以人为本人工智能研究院”(HAI)近日发布的第八版《人工智能指数报告》(AI Index Report 2025)备受全球瞩目。自 2017 年首次发布以来,该报告一直为政策制定者、研究人员、企业高管和公众提供准确、严…...
【SpringBoot+Vue自学笔记】003 SpringBoot Controll
跟着这位老师学习的:https://www.bilibili.com/video/BV1nV4y1s7ZN?vd_sourceaf46ae3e8740f44ad87ced5536fc1a45 这段话的意思其实是:Spring Boot 简化了传统 Web 项目的搭建流程,让你少折腾配置,直接开搞业务逻辑。 ὒ…...
探索Web3平台的数据安全和保护机制
在数字化时代,Web3 平台以其去中心化、透明性和用户主权等特点,正逐渐成为互联网技术的新宠。然而,随着数据价值的日益凸显,Web3 平台的数据安全和保护机制变得尤为重要。本文将深入探讨 Web3 平台的数据安全和保护机制࿰…...
基于ssh密钥访问远程Linux
1、在本地机器上生成密钥对(默认保存在 ~/.ssh/) ssh-keygen -t ed25519 或使用 RSA(兼容性更好): ssh-keygen -t rsa -b 4096 2、 将公钥上传到远程主机 方法一:使用 ssh-copy-id ssh-copy-id -i ~/.ssh/id_ed25519.pub us…...
《基于神经网络实现手写数字分类》
《基于神经网络实现手写数字分类》 一、主要内容: 1、通过B站陈云霁老师的网课,配合书本资料,了解神经网络的基本组成和数学原理。 2、申请云平台搭建实验环境 3、基于5个不同的实验模块逐步理解实验操作步骤,并实现不同模块代码…...
