10.按鍵之互斥、阻塞機制(詳解)

来源:http://www.cnblogs.com/lifexy/archive/2017/09/13/7515488.html
-Advertisement-
Play Games

本節目標: 當設備被一個程式打開時,存在被另一個程式打開的可能,如果兩個或多個程式同時對設備文件進行寫操作,這就是說我們的設備資源同時被多個進程使用,對共用資源(硬體資源、和軟體上的全局變數、靜態變數等)的訪問則很容易導致競態。 顯然這不是我們想要的,所以本節引入互斥的概念:實現同一時刻,只能一個應 ...


  • 本節目標:
  1. 學習原子操作和互斥信號量,實現互斥機制,同一時刻只能一個應用程式使用驅動程式
  2. 學習阻塞和非阻塞操作

當設備被一個程式打開時,存在被另一個程式打開的可能,如果兩個或多個程式同時對設備文件進行寫操作,這就是說我們的設備資源同時被多個進程使用,對共用資源(硬體資源、和軟體上的全局變數、靜態變數等)的訪問則很容易導致競態。

顯然這不是我們想要的,所以本節引入互斥的概念:實現同一時刻,只能一個應用程式使用驅動程式

互斥其實現很簡單,就是採用一些標誌,當文件被一個進程打開後,就會設置該標誌,使其他進程無法打開設備文件。


1.其中的標誌需要使用函數來操作,不能直接通過判斷變數來操作標誌

比如:

if (-- canopen != 0)  //當canopen==0,表示沒有進程訪問驅動,當canopen<0:表示有進程訪問

 

    編譯彙編來看,分了3段: 讀值、減1、判斷

如果剛好在讀值的時候發生了中斷,有另一個進程訪問時,那麼也會訪問成功,也會容易導致訪問競態。

1.1所以採用某種函數來實現,保證執行過程不被其他行為打斷,有兩種類型函數可以實現:

原子操作(像原子一樣不可再細分不可被中途打斷)

當多個進程同時訪問同一個驅動時,只能有一個進程訪問成功,其它進程會退出

互斥信號量操作

比如:A、B進程同時訪問同一個驅動時,只有A進程訪問成功了,B進程進入休眠等待狀態,當A進程執行完畢釋放後,等待狀態的B進程又來訪問,保證一個一個進程都能訪問

2. 原子操作詳解

原子操作指的是在執行過程中不會被別的代碼路徑所中斷的操作。

原子操作函數如下:

1)atomic_t v = ATOMIC_INIT(0);         //定義原子變數v並初始化為0

2)atomic_read(atomic_t *v);            //返回原子變數的值

3)void atomic_inc(atomic_t *v);        //原子變數增加1

4)void atomic_dec(atomic_t *v);        //原子變數減少1

5)int atomic_dec_and_test(atomic_t *v); //自減操作後測試其是否為0,為0則返回true,否則返回false。

 

2.1修改驅動程式

定義原子變數:

/*定義原子變數canopen並初始化為1 */
atomic_t canopen = ATOMIC_INIT(1);  

 

在.open成員函數里添加:

/*自減操作後測試其是否為0,為0則返回true,否則返回false   */
if(!atomic_dec_and_test(&canopen))     
 {
  atomic_inc(&canopen);       //++,複位
  return -1;
 }

 

在. release成員函數里添加:

  atomic_inc(&canopen);       //++,複位

 

2.2修改測試程式:

int main(int argc,char **argv)
{
  int oflag;
  unsigned int val=0;     
  fd=open("/dev/buttons",O_RDWR);          
  if(fd<0)
       {printf("can't open, fd=%d\n",fd);
       return -1;}
   while(1)
   { 
     read( fd, &ret, 1);              //讀取驅動層數據
     printf("key_vale=0X%x\r\n",ret);   
   }  

   return 0;

}

 

2.3 測試效果

如下圖,可以看到第一個進程訪問驅動成功,後面的就再也不能訪問成功了

3.互斥信號量詳解

互斥信號量(semaphore)是用於保護臨界區的一種常用方法,只有得到信號量的進程才能執行臨界區代碼。

當獲取不到信號量時,進程進入休眠等待狀態。

信號量函數如下:

/*註意: 在2.6.36版本後這個函數DECLARE_MUTEX修改成DEFINE_SEMAPHORE了*/
1)static DECLARE_MUTEX(button_lock);     //定義互斥鎖button_lock,被用來後面的down和up用
2)void down(struct semaphore * sem); // 獲取不到就進入不被中斷的休眠狀態(down函數中睡眠)
3)int down_interruptible(struct semaphore * sem); //獲取不到就進入可被中斷的休眠狀態(down函數中睡眠)
4)int down_trylock(struct semaphore * sem); //試圖獲取信號量,獲取不到則立刻返回正數
5)void up(struct semaphore * sem); //釋放信號量

 

3.1修改驅動程式(以down函數獲取為例)

(1)定義互斥鎖變數:

/*定義互斥鎖button_lock,被用來後面的down()和up()使用 */
static DECLARE_MUTEX(button_lock);  

 

(2)在.open成員函數里添加:

/* 獲取不到就進入不被中斷的休眠狀態(down函數中睡眠) */
          down(&button_lock);

 

(3)在. release成員函數里添加:

 /*         釋放信號量          */
          up(&button_lock);               

3.2修改測試程式:

 int main(int argc,char **argv)
{
  int oflag;
  unsigned int val=0;      
  fd=open("/dev/buttons",O_RDWR);          
  if(fd<0)
       {printf("can't open, fd=%d\n",fd);
       return -1;}
  else
       {
       printf("can open,PID=%d\n",getpid());    //打開成功,列印pid進程號
       }
   while(1)
   { 
     read( fd, &ret, 1);              //讀取驅動層數據
     printf("key_vale=0X%x\r\n",ret);  
   }  
   return 0;
}

 

3.3 測試效果

如下圖所示,3個進程同時訪問時,只有一個進程訪問成功,其它2個進程進入休眠等待狀態

 

如下圖所示,多個信號量訪問時, 會一個一個進程來排序訪問

 

4.阻塞與非阻塞

4.1阻塞操作  

進程進行設備操作時,使用down()函數,若獲取不到資源則掛起進程,將被掛起的進程進入休眠狀態,被從調度器的運行隊列移走,直到等待的條件被滿足。

在read讀取按鍵時, 一直等待按鍵按下才返回數據

4.2非阻塞操作 

進程進行設備操作時,使用down_trylock()函數,若獲取不到資源並不掛起,直接放棄。

在read讀取按鍵時, 不管有沒有數據都要返回

4.3 怎麼來判斷阻塞與非阻塞操作?

在用戶層open時,預設為阻塞操作,如果添加了” O_NONBLOCK”,表示使open()、read()、write()不被阻塞

實例:

fd=open("/dev/buttons",O_RDWR);                                          //使用阻塞操作
fd = open("/dev/buttons ", O_RDWR | O_NONBLOCK);                         //使用非阻塞操作

 

然後在驅動設備中,通過file_operations成員函數.open、.read、.write帶的參數file->f_flags 來查看用戶層訪問時帶的參數

實例:

  if(  file->f_flags & O_NONBLOCK )   //非阻塞操作,獲取不到則退出
  {
     ... ...
  }
  else   //阻塞操作,獲取不到則進入休眠
  {
     ... ...
  }

4.4修改應用程式,通過判斷file->f_flags來使用阻塞操作還是非阻塞操作

(1)定義互斥鎖變數:

/*定義互斥鎖button_lock,被用來後面的down()和up()使用 */
static DECLARE_MUTEX(button_lock); 

(2)在.open成員函數里添加:

if( file->f_flags & O_NONBLOCK )   //非阻塞操作
  {
   if(down_trylock(&button_lock) )       //嘗試獲取信號量,獲取不到則退出
            return -1;
  }
else //阻塞操作 { down(&button_lock); //獲取信號量,獲取不到則進入休眠 }

 

 (3)在. release成員函數里添加:

        /*釋放信號量*/
          up(&button_lock);     

 

4.5 寫阻塞測試程式 fifth_blocktext.c

代碼如下:

int main(int argc,char **argv)
{
  int oflag;
  unsigned int val=0;       
  fd=open("/dev/buttons",O_RDWR);           //使用阻塞操作
  if(fd<0)
       {printf("can't open, fd=%d\n",fd);      
       return -1;}
  else
       {
       printf("can open,PID=%d\n",getpid());    //打開成功,列印pid進程號
       } 

   while(1)
   { 
    val=read( fd, &ret, 1);              //讀取驅動層數據
     printf("key_vale=0X%x,retrun=%d\r\n",ret,val);  
   }
   return 0;

}

 

4.6 非阻塞測試效果

如下圖所示:      

 

4.7寫阻塞測試程式 fifth_nonblock.c

代碼如下:

int main(int argc,char **argv)
{
  int oflag;
  unsigned int val=0;      
  fd=open("/dev/buttons",O_RDWR | O_NONBLOCK);   //使用非阻塞操作 
  if(fd<0)
       {printf("can't open, fd=%d\n",fd);
       return -1;}
  else
       {
       printf("can open,PID=%d\n",getpid());    //打開成功,列印pid進程號
       }
while(1) { val=read( fd, &ret, 1); //讀取驅動層數據 printf("key_vale=0X%x,retrun=%d\r\n",ret,val); sleep(3); //延時3S } return 0;
}

 

4.8 阻塞測試效果

如下圖所示:      

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • nginx編譯安裝步驟 ①. 檢查軟體安裝的系統環境 cat /etc/redhat-release uname -r ②. 安裝nginx的依賴包(pcre-devel openssl-devel) 假設不進行安裝 yum install -y pcre-devel openssl-devel ③ ...
  • 我們可以把路由器比作網路世界的骨架,我們之所以能夠在網路世界里暢游,很大程度上是得益於這個鐵盒子。 路由器硬體架構 隨著專用多核網路處理器、專用轉發晶元的出現,使得現代路由器擺脫了以往純軟體轉發的局限,向著高吞吐率、硬體快速轉發等方向發展。高端的路由器設計成多板分散式+冗餘備份的架構,使轉發能力成倍 ...
  • 本文目錄: 1.幾個顯示函數2.action函數3.is_true和is_false函數4.confirm函數5.pid檢測相關函數 5.1 checkpid、__pids_var_run和__pids_pidof函數 5.2 pidfileofproc和pidofproc函數6.重頭戲(一):da ...
  • 原文發表於cu:2016-07-04 參考文檔: http://seanlook.com/2015/01/21/openldap-install-guide-ssl/ 一.環境 Server:基於CentOS-7-x86_64-1511 Server IP: 172.18.12.203 OpenLD ...
  • 依賴包和常用包yum install gcc gcc-c++ make zlib-devel readline readline-devel tkutil tk tkutil-devel tk-devel openssl openssl-devel wget vim ntp -y下載rediswge ...
  • CentOS7自帶的SSH服務是OpenSSH中的一個獨立守護進程SSHD。由於使用telnet在網路中是明文傳輸所以用其管理伺服器是非常不安全的不安全,SSH協議族可以用來對伺服器的管理以及在電腦之間傳送文件。 一、配置文件 伺服器配置文件 /etc/ssh/sshd_config 日誌文件 / ...
  • 作為文本三劍客之一的sed,功能的確的強大,本文介紹一些基本的用法和高級選項,需要在今後的日常使用和工作中不斷的熟悉和鞏固。 ...
  • 一、開始 1、打開或新建asp.net mvc 4項目 2、修改 global.asax文件 原: 新: 3、刪除以下ASP.NET NuGet packages ,使用 Package Manager Console (PMC)進行移除 4、更新ASP.NET NuGet packages 二、更 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...