驅動-互斥鎖

互斥鎖可以說是“量值” 為 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.pswp.cn/bicheng/80312.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/80312.shtml
英文地址,請注明出處:http://en.pswp.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;它以其卓越…