驱动-互斥锁

互斥锁可以说是“量值” 为 1 的
信号量, 最终实现的效果相同, 既然有了信号量, 那为什么还要有互斥锁呢? 这就是我们这里需要了解并掌握的

文章目录

  • 参考资料
  • 互斥锁的介绍
  • 互斥锁结构体 - mutex
  • 互斥锁 API
  • 互斥锁实验
    • 源码程序-mutex.c
      • 部分源码解读
    • 编译脚本 Makefile
    • 测试程序 app.c
    • 准备测试命令和测试脚本-app.sh
      • 测试命令
      • 测试脚本
    • 加载驱动 insmod - 查看 dev 下生成的设备
    • 测试验证互斥锁程序
      • 直接命令后台验证
      • 脚本批量执行后台任务 测试验证
  • 总结
    • 互斥锁与信号量的区别与联系
      • 基本概念对比
      • 关键区别详解
        • 所有权机制
        • 计数方式
        • 性能特点
      • 联系与共同点
      • 使用场景建议
        • 使用互斥锁的情况
        • 使用信号量的情况
      • 选择指南
        • 优先使用互斥锁
        • 考虑使用信号量


参考资料

前面了解了原子操作和自旋锁,当然还有之前的字符设备相关操作,前面基础知识还是需要重点掌握的,才能将知识点串联起来:

接下来还是以前面字符设备 动态参数传递实验为基础,打开访问字符设备实验。 所以以前知识点 建议了解
在字符设备这块内容,所有知识点都是串联起来的,需要整体来理解,缺一不可,建议多了解一下基础知识
驱动-申请字符设备号
驱动-注册字符设备
驱动-创建设备节点
驱动-字符设备驱动框架
驱动-杂项设备
驱动-内核空间和用户空间数据交换
驱动-文件私有数据
Linux驱动之 原子操作
Linux驱动—原子操作
驱动-自旋锁
驱动-自旋锁死锁
驱动-信号量

互斥锁的介绍

  • 将信号量量值设置为 1, 最终实现的就是互斥效果, 这里要了解的互斥锁功能相同, 虽然两者功能相同但是具体的实现方式是不同的, 但是使用互斥锁效率更高、更简洁, 所以如果使用到的信号量“量值”为 1,一般将其修改为使用互斥锁实现。当有多个线程几乎同时修改某一个共享数据的时候, 需要进行同步控制。线程同步能够保证多个线程安全访问竞争资源, 最简单的同步机制是引入互斥锁。

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

互斥锁结构体 - mutex

struct mutex {atomic_t count;          // 锁计数器:1-未锁,0-已锁,负值-有等待者spinlock_t wait_lock;    // 保护等待队列的自旋锁struct list_head wait_list; // 等待该锁的进程队列
};

互斥锁 API

在这里插入图片描述

互斥锁实验

源码程序-mutex.c

这个源码程序,用到的还是访问字符设备的最基本内容来讲解,另外添加了 互斥锁api 来规避并发和竞争

#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>
#include <linux/semaphore.h>
#include <linux/mutex.h>struct mutex mutex_test;//定义mutex类型的互斥锁结构体变量mutex_teststatic int open_test(struct  inode  *inode,struct file *file){printk("\n this 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类型字符串变量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");mutex_unlock(&mutex_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)//驱动入口函数
{mutex_init(&mutex_test);//对互斥体进行初始化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 "); //作者信息

部分源码解读

字符设备操作这里不再赘述,重点看看互斥锁怎么用的。
在之前学习过原子操作设置标志位, 在同一时间内只允许一个任务对共享资源进行访问的方式所不
同, 这里将采用互斥锁的方式避免竞争的产生。 由于互斥体在同一时间内只允许一个任务对共享资源进行, 所以除了在 atomic_init()函数内加入初始化互斥锁函数之外,只需要在 open()函数中加入互斥锁加锁函数, 在 release()函数中加入互斥锁解锁函数即可

  • 定义结构体 - mutex
struct mutex mutex_test;//定义mutex类型的互斥锁结构体变量mutex_test
  • 驱动入口函数 init 中初始化 互斥锁结构体,设置值 - mutex_init
static int __init chrdev_fops_init(void)//驱动入口函数
{mutex_init(&mutex_test);//对互斥体进行初始化
...
}
  • 在open 中加锁 - mutex_lock
static int open_test(struct  inode  *inode,struct file *file){printk("\n this is open_test \n");mutex_lock(&mutex_test);//互斥锁加锁return 0;};
  • 程序释放资源时候,解锁- mutex_unlock
static int release_test(struct inode *inode,struct file *file)
{printk("\nthis is release_test \n");mutex_unlock(&mutex_test);//互斥锁解锁return 0;
}

编译脚本 Makefile

#!/bin/bash
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
obj-m += mutex.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

准备测试命令和测试脚本-app.sh

测试命令

同时后台执行两个命令

./app /dev/device_test topeet &
./app /dev/device_test itop

测试脚本

这里准备签名驱动自选死锁的脚本,方便看看 互斥锁的作用和效果。 app.sh

[root@topeet:/mnt/sdcard]# cat app.sh#!/bin/bash
taskset -c 0 ./app /dev/device_test topeet &
taskset -c 1 ./app /dev/device_test topeet &
taskset -c 2 ./app /dev/device_test topeet &
taskset -c 3 ./app /dev/device_test topeet &
taskset -c 0 ./app /dev/device_test topeet &
taskset -c 1 ./app /dev/device_test topeet &
taskset -c 2 ./app /dev/device_test topeet &
taskset -c 3 ./app /dev/device_test topeet &

加载驱动 insmod - 查看 dev 下生成的设备

加载驱动后,看一下字符相关操作是否有相关打印,从结果上看打印OK,逻辑正常在走。
在这里插入图片描述

字符设备都已经生成了,说明测试程序没有问题的。

测试验证互斥锁程序

直接命令后台验证

./app /dev/device_test topeet &
./app /dev/device_test itop

看实验结果如下:文件操作是一个等着一个执行的呢
在这里插入图片描述

脚本批量执行后台任务 测试验证

实际结果是,打印一个接着一个打印,会按照程序里面的逻辑 等待几秒,执行完后才会执行下一个任务命令。 而且最重要的是 这里用的是自旋锁死锁的 脚本来验证,在互斥锁这里不会死机。 这样更方便理解 互斥锁的原理了。
在这里插入图片描述

总结

  • 互斥锁也是解决并发、竞争问题的一种方案
  • 浅显的看: 互斥锁原理就是一个全局的变量,类似于原子操作。会让线程、进程去处理其它事情,不用想自旋锁原地等待。大量频繁使用会增加切换资源消耗。

互斥锁与信号量的区别与联系

基本概念对比

特性互斥锁(Mutex)内核态信号量(Semaphore)
本质特殊的二进制信号量更通用的同步机制
持有者有明确的持有者(必须由获取者释放)无持有者概念
计数只能是0或1(二进制)可以是任意正整数(计数信号量)
性能更高(优化过的实现)相对较低
优先级继承支持(防止优先级反转)不支持
使用场景短期临界区保护资源计数管理

关键区别详解

所有权机制

互斥锁具有严格的所有权概念:

只有锁定mutex的线程才能解锁它内核会跟踪当前持有者这种设计有助于调试和死锁检测
  • 信号量没有所有权概念:
任何线程都可以对信号量执行up操作更灵活但也更容易出错
计数方式
  • 互斥锁是二进制锁:
只有锁定/未锁定两种状态一次只允许一个线程进入临界区
  • 信号量是计数信号量:
初始化时可设置任意正整数值允许多个线程同时访问资源(当计数>1)
性能特点
  • 互斥锁经过高度优化:
快速路径(fast path)通常只需几条原子指令在无竞争情况下性能接近无锁
  • 信号量开销较大:
总是涉及上下文切换即使在无竞争情况下也需要更多操作

联系与共同点

  • 同步基础:两者都基于内核的等待队列机制实现
  • 睡眠特性:当资源不可用时,都会使调用者睡眠
  • 不可中断上下文使用:都不能在原子上下文(如中断处理程序)中使用
  • 解决竞态条件:都可用于保护共享资源,防止竞态条件

使用场景建议

使用互斥锁的情况
  • 需要严格互斥访问的共享资源
  • 临界区执行时间较短
  • 需要防止优先级反转的实时应用
使用信号量的情况
  • 需要限制并发访问数量的资源
  • 允许多个读者同时访问的情况
  • 需要跨多个模块释放锁的复杂场景

选择指南

优先使用互斥锁
  • 只需要二进制锁定
  • 性能是关键考量
  • 需要调试支持(如死锁检测)
  • 在实时系统中需要优先级继承
考虑使用信号量
  • 需要计数功能
  • 锁定可能被不同模块释放
  • 需要允许多个并发访问(如读者)

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

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

相关文章

人工智能100问☞第17问:智能体的定义及其基本特征?

目录 一、通俗解释 二、专业解析 三、权威参考 智能体是能够通过传感器感知环境、自主决策并借助执行器采取行动以实现特定目标的智能实体或系统。 一、通俗解释 智能体就像一台能自己“看、想、动”的智能机器。比如你手机里的语音助手&#xff0c;它能听懂你说的话&…

Linux系统入门第十一章 --Shell编程之函数与数组

一、Shell函数 1、函数的用法 Shell函数可用于存放一系列的指令。在Shell脚本执行的过程中&#xff0c;函数被置于内存中&#xff0c;每次调用函数时不需要从硬盘读取&#xff0c;因此运行的速度比较快。在Shell编程中函数并非是必须的元素&#xff0c;但使用函数可以对程序进…

Baumer工业相机堡盟工业相机的工业视觉中为什么偏爱“黑白相机”

Baumer工业相机堡盟工业相机的工业视觉中为什么偏爱“黑白相机” Baumer工业相机​为什么偏爱“黑白相机”&#xff1f;​工业视觉中为什么倾向于多使用黑白相机黑白相机在工业视觉中的应用场景有哪些&#xff1f; Baumer工业相机 工业相机是常用与工业视觉领域的常用专业视觉…

MiM: Mask in Mask Self-SupervisedPre-Training for 3D Medical Image Analysis

Abstract Vision Transformer在3D医学图像分析的自监督学习&#xff08;Self-Supervised Learning&#xff0c;SSL&#xff09;中展现了卓越的性能。掩码自编码器&#xff08;Masked Auto-Encoder&#xff0c;MAE&#xff09;用于特征预训练&#xff0c;可以进一步释放ViT在各…

SQL注入的绕过方式

1.注释与空白符绕过 利用#,--,/**/替代被过滤的注释符 利用%09&#xff08;Tab&#xff09;,%0A(换行) &#xff0c;/**/代替空格&#xff1a;如union%0Aselect%0A1,2,3 2.编码绕过&#xff1a; URL编码&#xff0c;双重编码&#xff0c;十六进制编码&#xff0c;Unicode编…

数据加密方式(对称加密/非对称加密 /数字签名/证书)

文章目录 数据加密方式常用加密方式对比哈希算法&#xff08;Hashing&#xff09;哈希算法的特点常见的哈希算法哈希算法的应用哈希与加密的区别哈希算法的安全性问题 对称加密&#xff08;Symmetric Encryption&#xff09;工作原理主要特点常见的对称加密算法优缺点 非对称加…

UnityDots学习(五)

此篇开始研究实际应用到项目或个人Demo中。参考国外CodeMonkey的RTS包含一些基础API应用。 前言 游戏不必100%使用Dots完全实现。因为面向组件开发一个功能复杂度和调试都比面向对象要更难。对于某些模块&#xff0c;比如UI&#xff0c;事件管理系统&#xff0c;网络等&#…

移动端前端开发中常用的css

在开发移动端项目的时候&#xff0c;很多样式都是相同的&#xff0c;比如说图标大小&#xff0c;头像大小&#xff0c;页面底部保存(添加按钮&#xff09;&#xff0c;项目主体颜色等等&#xff0c;对于这些在项目中常用到的&#xff0c;通常都会写在公共样式中&#xff08;pub…

Vue3 中 ref 与 reactive 的区别及底层原理详解

一、核心区别 1. 数据类型与使用场景 • ref 可定义基本类型&#xff08;字符串、数字、布尔值&#xff09;和对象类型的响应式数据。对于对象类型&#xff0c;ref 内部会自动调用 reactive 将其转换为响应式对象。 语法特点&#xff1a;需通过 .value 访问或修改数据&#…

AGV导航控制器技术方案——基于EFISH-SBC-RK3576/SAIL-RK3576的国产化革新‌(新一代工业级自主可控解决方案)‌

一、方案核心架构 ‌1. 硬件拓扑设计‌ ‌主控单元‌&#xff1a;SAIL-RK3576核心板&#xff08;八核A72A53M0异构架构&#xff09;‌传感器层‌&#xff1a; 双激光雷达&#xff08;RS-LiDAR-16线 SICK TIM240&#xff09;9轴IMU&#xff08;BMI088&#xff09; 轮式编码器&…

AI 辅助生成原型图

AI 辅助生成原型图 一、HTML 转设计稿工具介绍 网页转设计稿工具 使用 MasterGo 的 html-to-mastergo 插件可将网页转为设计稿&#xff0c;支持&#xff1a; 任意在线 HTML 文件&#xff08;通过将 AI 生成的 UI 发布为在线页&#xff0c;可通过 Vercel 实现&#xff09;离…

从零打造个人博客静态页面与TodoList应用:前端开发实战指南

前言 在当今数字时代&#xff0c;拥有个人博客和高效的任务管理工具已成为开发者展示自我和提升生产力的标配。本文将带你从零开始&#xff0c;通过纯前端技术实现一个兼具个人博客静态页面和TodoList任务管理功能的综合应用。无论你是前端新手还是希望巩固基础的中级开发者&a…

工作流与n8n:自动化技术的演进与开源工具的核心地位

第一章 工作流的基础理论介绍 1.1 工作流的定义与核心要素 工作流&#xff08;Workflow&#xff09;是指一系列相互衔接、自动化的业务活动或任务&#xff0c;其核心在于通过规则驱动的流程设计&#xff0c;实现跨系统、跨角色的协同作业。根据国际工作流管理联盟&#xff08…

WordPress插件:WPJAM Basic优化设置

WPJAM Basic 插件的「优化设置」是我爱水煮鱼博客多年使用 WordPress 的经验而整理的各类优化设置。 一、功能屏蔽 功能屏蔽就是屏蔽一些WordPress中用不上、难用的功能&#xff0c;目前的支持屏蔽以下功能&#xff1a; &#xff08;1&#xff09;屏蔽文章修订功能 文章修…

Spring AI 入门(持续更新)

介绍 Spring AI 是 Spring 项目中一个面向 AI 应用的模块&#xff0c;旨在通过集成开源框架、提供标准化的工具和便捷的开发体验&#xff0c;加速 AI 应用程序的构建和部署。 依赖 <!-- 基于 WebFlux 的响应式 SSE 传输 --> <dependency><groupId>org.spr…

c/c++日志库初识

C/C日志库&#xff1a;从入门到实践的深度指南 在软件开发的世界里&#xff0c;日志&#xff08;Logging&#xff09;扮演着一个沉默却至关重要的角色。它像是飞行记录仪的“黑匣子”&#xff0c;记录着应用程序运行时的关键信息&#xff0c;帮助开发者在问题发生时追溯根源&a…

C 语言图形编程 | 界面 / 动画 / 字符特效

注&#xff1a;本文为 “C 语言图形编程” 相关文章合辑。 略作重排&#xff0c;如有内容异常&#xff0c;请看原文。 C 语言图形化界面——含图形、按钮、鼠标、进度条等部件制作&#xff08;带详细代码、讲解及注释&#xff09; 非线性光学元件于 2020-02-15 09:42:37 发布…

开发狂飙VS稳定刹车:Utility Tree如何让架构决策“快而不失控”

大家好&#xff0c;我是沛哥儿。 在软件技术架构的世界里&#xff0c;架构师们常常面临灵魂拷问&#xff1a;高并发和低成本哪个优先级更高&#xff1f; 功能迭代速度和系统稳定性该如何平衡&#xff1f; 当多个质量属性相互冲突时&#xff0c;该如何做出科学决策&#xff1f; …

SCI论文图数据提取软件——GetData Graph Digitizer

在写综述或者毕业论文的时候一般会引用前人的文献数据图&#xff0c;但是直接截图获取来的数据图通常质量都不太高。因此我们需要从新画一张图&#xff0c;可以通过origin绘图来实现&#xff0c;今天介绍一个新的软件GetData Graph Digitizer 感谢下面博主分享的破解安装教程 …

深入探索 Apache Spark:从初识到集群运行原理

深入探索 Apache Spark&#xff1a;从初识到集群运行原理 在当今大数据时代&#xff0c;数据如同奔涌的河流&#xff0c;蕴藏着巨大的价值。如何高效地处理和分析这些海量数据&#xff0c;成为各行各业关注的焦点。Apache Spark 正是为此而生的强大引擎&#xff0c;它以其卓越…