1. 程式人生 > >嵌入式 自旋鎖(spinlock)詳解以及使用示例

嵌入式 自旋鎖(spinlock)詳解以及使用示例

1、使用示例:

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/timer.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/fs.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include <asm/io.h>
//#include <asm/arch/hardware.h>
#include <linux/kthread.h>
//#include <asm/arch/clock.h>
#include <linux/string.h>
#include <linux/sched.h>

#define OSDRV_MODULE_VERSION_STRING "HISI_WDT-MDC030001 @Hi3518v100"

#define HISILICON_SCTL_BASE 0x20050000
/* define watchdog IO */
#define HIWDT_BASE      0x20040000
#define HIWDT_REG(x)    (HIWDT_BASE + (x))

#define HIWDT_LOAD      0x000
#define HIWDT_VALUE     0x004
#define HIWDT_CTRL      0x008
#define HIWDT_INTCLR    0x00C
#define HIWDT_RIS       0x010
#define HIWDT_MIS       0x014
#define HIWDT_LOCK      0xC00

#define HIWDT_UNLOCK_VAL    0x1ACCE551

void __iomem *reg_ctl_base_va;
void __iomem *reg_wdt_base_va;
#define IO_WDT_ADDRESS(x) (reg_wdt_base_va + ((x)-(HIWDT_BASE)))

#define hiwdt_readl(x)      readl(IO_WDT_ADDRESS(HIWDT_REG(x)))
#define hiwdt_writel(v,x)   writel(v, IO_WDT_ADDRESS(HIWDT_REG(x)))

/* debug */
#define HIDOG_PFX "HiDog: "
#define hidog_dbg(params...) printk(KERN_INFO HIDOG_PFX params)

/* module param */
#define HIDOG_TIMER_MARGIN    60
static int default_margin = HIDOG_TIMER_MARGIN;    /* in seconds */
#define HIDOG_TIMER_DEMULTIPLY  9
module_param(default_margin, int, 0);
MODULE_PARM_DESC(default_margin, "Watchdog default_margin in seconds. (0<default_margin<80, default=" __MODULE_STRING(HIDOG_TIMER_MARGIN) ")");

static int nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, int, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");

static int nodeamon = 0;
module_param(nodeamon, int, 0);
MODULE_PARM_DESC(nodeamon, "By default, a kernel deamon feed watchdog when idle, set 'nodeamon=1' to disable this. (default=0)");

/* watchdog info */
static struct watchdog_info ident = {
    .options =  WDIOF_SETTIMEOUT |
                WDIOF_KEEPALIVEPING |
                WDIOF_MAGICCLOSE,
    .firmware_version = 0,
    .identity = "Hisilicon Watchdog",
};

/* local var */
static DEFINE_SPINLOCK(hidog_lock);
static int cur_margin = HIDOG_TIMER_MARGIN;
//static pid_t pid_hidog_deamon = -1;
struct task_struct *task_hidog_deamon = NULL;


#define HIDOG_EXIT    0
#define HIDOG_SELFCLR    1
#define HIDOG_EXTCLR    2
volatile static unsigned int hidog_state = 0;

static unsigned long driver_open = 0, orphan_timer = 0;
static int options = WDIOS_ENABLECARD;

#ifndef MHZ
#define MHZ (1000*1000)
#endif

static unsigned long rate = 3*MHZ;

static inline void hidog_set_timeout(unsigned int nr)
{
    unsigned long cnt = (~0x0)/rate;        /* max cnt */
    unsigned long flags; 

    spin_lock_irqsave(&hidog_lock, flags); 

    if( nr==0 || nr>cnt)
        cnt=~0x0; 
    else
        cnt = nr*rate;
    /* unlock watchdog registers */ 
    hiwdt_writel(HIWDT_UNLOCK_VAL, HIWDT_LOCK); 
    hiwdt_writel(cnt, HIWDT_LOAD); 
    hiwdt_writel(cnt, HIWDT_VALUE); 
    /* lock watchdog registers */ 
    hiwdt_writel(0, HIWDT_LOCK); 
    spin_unlock_irqrestore(&hidog_lock, flags); 
};

static inline void hidog_feed(void)
{
    unsigned long flags; 

    spin_lock_irqsave(&hidog_lock, flags); 
    /* unlock watchdog registers */ 
    hiwdt_writel(HIWDT_UNLOCK_VAL, HIWDT_LOCK); 
    /* clear watchdog */ 
    hiwdt_writel(0x00, HIWDT_INTCLR); 
    /* lock watchdog registers */ 
    hiwdt_writel(0, HIWDT_LOCK); 
    spin_unlock_irqrestore(&hidog_lock, flags); 
};

static int hidog_set_heartbeat(int t)
{
    int ret = 0;
    unsigned int cnt = (~0x0)/rate; 

    if( t==0 ) {
        printk(KERN_ERR HIDOG_PFX "set heartbeat to 0, heartbeat will not be changed.\n");
        t = cur_margin;
        ret = 1;
    } else if( t>cnt ) {
        printk(KERN_ERR HIDOG_PFX "set heartbeat range error, t = %d\n", t);
        printk(KERN_ERR HIDOG_PFX "force heartbeat to %d\n", cnt);
        t = cnt;
        ret = -1;
    }

    cur_margin = t;

    hidog_set_timeout(t);
    hidog_feed();

    if(NULL != task_hidog_deamon)
        wake_up_process(task_hidog_deamon);    

    return ret;
}

static int hidog_keepalive(void)
{
    hidog_feed();
    return 0;
} 

自旋鎖詳解:

自旋鎖與互斥鎖有點類似,只是自旋鎖不會引起呼叫者睡眠,如果自旋鎖已經被別的執行單元保持,呼叫者就一直迴圈在那裡看是否該自旋鎖的保持者已經釋放了鎖,"自旋"一詞就是因此而得名。

  由於自旋鎖使用者一般保持鎖時間非常短,因此選擇自旋而不是睡眠是非常必要的,自旋鎖的效率遠高於互斥鎖。   訊號量和讀寫訊號量適合於保持時間較長的情況,它們會導致呼叫者睡眠,因此只能在程序上下文使用(_trylock的變種能夠在中斷上下文使用),而自旋鎖適合於保持時間非常短的情況,它可以在任何上下文使用。   如果被保護的共享資源只在程序上下文訪問,使用訊號量保護該共享資源非常合適,如果對共巷資源的訪問時間非常短,自旋鎖也可以。但是如果被保護的共享資源需要在中斷上下文訪問(包括底半部即中斷處理控制代碼和頂半部即軟中斷),就必須使用自旋鎖。
  自旋鎖保持期間是搶佔失效的,而訊號量和讀寫訊號量保持期間是可以被搶佔的。自旋鎖只有在核心可搶佔或SMP的情況下才真正需要,在單CPU且不可搶佔的核心下,自旋鎖的所有操作都是空操作。   跟互斥鎖一樣,一個執行單元要想訪問被自旋鎖保護的共享資源,必須先得到鎖,在訪問完共享資源後,必須釋放鎖。如果在獲取自旋鎖時,沒有任何執行單元保持該鎖,那麼將立即得到鎖;如果在獲取自旋鎖時鎖已經有保持者,那麼獲取鎖操作將自旋在那裡,直到該自旋鎖的保持者釋放了鎖。   無論是互斥鎖,還是自旋鎖,在任何時刻,最多隻能有一個保持者,也就說,在任何時刻最多隻能有一個執行單元獲得鎖。   自旋鎖的API有: spin_lock_init(x)
  該巨集用於初始化自旋鎖x。自旋鎖在真正使用前必須先初始化。該巨集用於動態初始化。 DEFINE_SPINLOCK(x)
  該巨集宣告一個自旋鎖x並初始化它。該巨集在2.6.11中第一次被定義,在先前的核心中並沒有該巨集。
SPIN_LOCK_UNLOCKED
  該巨集用於靜態初始化一個自旋鎖。
DEFINE_SPINLOCK(x)等同於spinlock_t x = SPIN_LOCK_UNLOCKED spin_is_locked(x)
  該巨集用於判斷自旋鎖x是否已經被某執行單元保持(即被鎖),如果是,返回真,否則返回假。
spin_unlock_wait(x)
  該巨集用於等待自旋鎖x變得沒有被任何執行單元保持,如果沒有任何執行單元保持該自旋鎖,該巨集立即返回,否則將迴圈在那裡,直到該自旋鎖被保持者釋放。
spin_trylock(lock)
  該巨集盡力獲得自旋鎖lock,如果能立即獲得鎖,它獲得鎖並返回真,否則不能立即獲得鎖,立即返回假。它不會自旋等待lock被釋放。
spin_lock(lock)
  該巨集用於獲得自旋鎖lock,如果能夠立即獲得鎖,它就馬上返回,否則,它將自旋在那裡,直到該自旋鎖的保持者釋放,這時,它獲得鎖並返回。總之,只有它獲得鎖才返回。
spin_lock_irqsave(lock, flags)
  該巨集獲得自旋鎖的同時把標誌暫存器的值儲存到變數flags中並失效本地中斷。
spin_lock_irq(lock)
  該巨集類似於spin_lock_irqsave,只是該巨集不儲存標誌暫存器的值。
spin_lock_bh(lock)
  該巨集在得到自旋鎖的同時失效本地軟中斷。
spin_unlock(lock)
  該巨集釋放自旋鎖lock,它與spin_trylock或spin_lock配對使用。如果spin_trylock返回假,表明沒有獲得自旋鎖,因此不必使用spin_unlock釋放。
spin_unlock_irqrestore(lock, flags)
  該巨集釋放自旋鎖lock的同時,也恢復標誌暫存器的值為變數flags儲存的值。它與spin_lock_irqsave配對使用。
spin_unlock_irq(lock)
  該巨集釋放自旋鎖lock的同時,也使能本地中斷。它與spin_lock_irq配對應用。
spin_unlock_bh(lock)
  該巨集釋放自旋鎖lock的同時,也使能本地的軟中斷。它與spin_lock_bh配對使用。
spin_trylock_irqsave(lock, flags)   該巨集如果獲得自旋鎖lock,它也將儲存標誌暫存器的值到變數flags中,並且失效本地中斷,如果沒有獲得鎖,它什麼也不做。
  因此如果能夠立即獲得鎖,它等同於spin_lock_irqsave,如果不能獲得鎖,它等同於spin_trylock。如果該巨集獲得自旋鎖lock,那需要使用spin_unlock_irqrestore來釋放。
spin_trylock_irq(lock)   該巨集類似於spin_trylock_irqsave,只是該巨集不儲存標誌暫存器。如果該巨集獲得自旋鎖lock,需要使用spin_unlock_irq來釋放。 spin_trylock_bh(lock)
  該巨集如果獲得了自旋鎖,它也將失效本地軟中斷。如果得不到鎖,它什麼也不做。因此,如果得到了鎖,它等同於spin_lock_bh,如果得不到鎖,它等同於spin_trylock。如果該巨集得到了自旋鎖,需要使用spin_unlock_bh來釋放。
spin_can_lock(lock)
  該巨集用於判斷自旋鎖lock是否能夠被鎖,它實際是spin_is_locked取反。如果lock沒有被鎖,它返回真,否則,返回假。該巨集在2.6.11中第一次被定義,在先前的核心中並沒有該巨集。
  獲得自旋鎖和釋放自旋鎖有好幾個版本,因此讓讀者知道在什麼樣的情況下使用什麼版本的獲得和釋放鎖的巨集是非常必要的。   如果被保護的共享資源只在程序上下文訪問和軟中斷上下文訪問,那麼當在程序上下文訪問共享資源時,可能被軟中斷打斷,從而可能進入軟中斷上下文來對被保護的共享資源訪問,因此對於這種情況,對共享資源的訪問必須使用spin_lock_bh和spin_unlock_bh來保護。   當然使用spin_lock_irq和spin_unlock_irq以及spin_lock_irqsave和spin_unlock_irqrestore也可以,它們失效了本地硬中斷,失效硬中斷隱式地也失效了軟中斷。但是使用spin_lock_bh和spin_unlock_bh是最恰當的,它比其他兩個快。   如果被保護的共享資源只在程序上下文和tasklet或timer上下文訪問,那麼應該使用與上面情況相同的獲得和釋放鎖的巨集,因為tasklet和timer是用軟中斷實現的。   如果被保護的共享資源只在一個tasklet或timer上下文訪問,那麼不需要任何自旋鎖保護,因為同一個tasklet或timer只能在一個CPU上執行,即使是在SMP環境下也是如此。實際上tasklet在呼叫tasklet_schedule標記其需要被排程時已經把該tasklet繫結到當前CPU,因此同一個tasklet決不可能同時在其他CPU上執行。   timer也是在其被使用add_timer新增到timer佇列中時已經被幫定到當前CPU,所以同一個timer絕不可能執行在其他CPU上。當然同一個tasklet有兩個例項同時執行在同一個CPU就更不可能了。   如果被保護的共享資源只在兩個或多個tasklet或timer上下文訪問,那麼對共享資源的訪問僅需要用spin_lock和spin_unlock來保護,不必使用_bh版本,因為當tasklet或timer執行時,不可能有其他tasklet或timer在當前CPU上執行。  如果被保護的共享資源只在一個軟中斷(tasklet和timer除外)上下文訪問,那麼這個共享資源需要用spin_lock和spin_unlock來保護,因為同樣的軟中斷可以同時在不同的CPU上執行。   如果被保護的共享資源在兩個或多個軟中斷上下文訪問,那麼這個共享資源當然更需要用spin_lock和spin_unlock來保護,不同的軟中斷能夠同時在不同的CPU上執行。   如果被保護的共享資源在軟中斷(包括tasklet和timer)或程序上下文和硬中斷上下文訪問,那麼在軟中斷或程序上下文訪問期間,可能被硬中斷打斷,從而進入硬中斷上下文對共享資源進行訪問,因此,在程序或軟中斷上下文需要使用spin_lock_irq和spin_unlock_irq來保護對共享資源的訪問。   而在中斷處理控制代碼中使用什麼版本,需依情況而定,如果只有一箇中斷處理控制代碼訪問該共享資源,那麼在中斷處理控制代碼中僅需要spin_lock和spin_unlock來保護對共享資源的訪問就可以了。   因為在執行中斷處理控制代碼期間,不可能被同一CPU上的軟中斷或程序打斷。但是如果有不同的中斷處理控制代碼訪問該共享資源,那麼需要在中斷處理控制代碼中使用spin_lock_irq和spin_unlock_irq來保護對共享資源的訪問。   在使用spin_lock_irq和spin_unlock_irq的情況下,完全可以用spin_lock_irqsave和spin_unlock_irqrestore取代,那具體應該使用哪一個也需要依情況而定,如果可以確信在對共享資源訪問前中斷是使能的,那麼使用spin_lock_irq更好一些。   因為它比spin_lock_irqsave要快一些,但是如果你不能確定是否中斷使能,那麼使用spin_lock_irqsave和spin_unlock_irqrestore更好,因為它將恢復訪問共享資源前的中斷標誌而不是直接使能中斷。   當然,有些情況下需要在訪問共享資源時必須中斷失效,而訪問完後必須中斷使能,這樣的情形使用spin_lock_irq和spin_unlock_irq最好。
  spin_lock用於阻止在不同CPU上的執行單元對共享資源的同時訪問以及不同程序上下文互相搶佔導致的對共享資源的非同步訪問,而中斷失效和軟中斷失效卻是為了阻止在同一CPU上軟中斷或中斷對共享資源的非同步訪問。

相關推薦

嵌入式 spinlock以及使用示例

1、使用示例: #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> #include <linux/timer.h>

搶佔式核心與非搶佔式核心中的spinlock的區別

一、概括 (1)自旋鎖適用於SMP系統,UP系統用spinlock是作死。 (2)保護模式下禁止核心搶佔的方法:1、執行終端服務例程時2、執行軟中斷和tasklet時3、設定本地CPU計數器preempt_count (3)自旋鎖的忙等待的實際意義是:嘗試獲取自旋鎖的另一個

Nginx學習之四-Nginx程序同步方式-spinlock

自旋鎖簡介 Nginx框架使用了三種訊息傳遞方式:共享記憶體、套接字、訊號。 Nginx主要使用了三種同步方式:原子操作、訊號量、檔案鎖。 基於原子操作,nginx實現了一個自旋鎖。自旋鎖是一種非睡眠鎖。如果某程序檢視獲得自旋鎖,當發現鎖已經被其他程序獲得時,那麼不會使得

聊聊高併發實現幾種

在聊聊高併發(七)實現幾種自旋鎖(二) 這篇中介紹了兩種佇列鎖,一種是有界佇列鎖,一種是無界佇列鎖。其中無界佇列鎖CLHLock採用了連結串列的方式來組織多執行緒,使用了兩個ThreadLocal做指標指向自身的node和前一個node。它的特點是在前一個node的lock

聊聊高併發實現幾種

在聊聊高併發(五)理解快取一致性協議以及對併發程式設計的影響 我們瞭解了處理器快取一致性協議的原理,並且提到了它對併發程式設計的影響,“多個執行緒對同一個變數一直使用CAS操作,那麼會有大量修改操作,從而產生大量的快取一致性流量,因為每一次CAS操作都會發出廣播通知其他處理

適應不看後悔,看了必懂

自旋鎖 所謂自旋,就是指當有另外一個執行緒來競爭鎖時,這個執行緒會在原地迴圈等待,而不是把該執行緒給阻塞,直到那個獲得鎖的執行緒釋放鎖之後,這個執行緒就可以馬上獲得鎖的。鎖在原地迴圈的時候,是會消耗cpu的,就相當於在執行一個啥也沒有的for迴圈。 本來一個執行緒把鎖釋放之後,當前執行緒

spin lock與互斥量的區別

自旋鎖(spin lock)與互斥量(mutex)的比較 自旋鎖是一種非阻塞鎖,也就是說,如果某執行緒需要獲取自旋鎖,但該鎖已經被其他執行緒佔用時,該執行緒不會被掛起,而是在不斷的消耗CPU的時間,不停的試圖獲取自旋鎖。 互斥量是阻塞鎖,當某執行緒無法獲取互斥量時,該執行緒

排隊Ticket spinlocks

(譯自http://lwn.net/Articles/267968/,作者Jonathan Corbet) 自旋鎖是Linux核心中最底層的互斥機制。因此,它們對核心的安全和效能有很大的影響,人們花很大力氣去優化各種自旋鎖的實現(不同的硬體體系結構會有不同的實

java 可重入且無死

java自旋鎖 的實現原理:如果自旋鎖被另外一個執行緒物件持有,那麼當前獲取鎖的執行緒將陷入while迴圈等待,直到那個持有自旋鎖的執行緒物件釋放它所持有的自旋鎖,那麼那些想要獲取該自旋鎖的執行緒物件 將會有一個獲得該自旋鎖。   基於他這種原理,等待的時候,並不釋放cpu

GoLang基礎數據類型--->字典map

golang ont nbsp spa 數據 否則 創作 聲明 作者                          GoLang基礎數據類型--->字典(map)詳解                                                 

SQL語句之數據定義語言DDL

三種 absolute row redundant 字符 對象 not null 工作 part 操作對象:數據庫 1)創建數據庫 MariaDB [(none)]> help create databaseName: ‘CREATE DATABASE‘Descrip

指標1-- 軌道線指標ENE

本質 平均值 width 簡單 公式 方向 重新 alt 改變 軌道線指標(ENE): 1、定義:軌道線(ENE)由上軌線(UPPER)和下軌線(LOWER)及中軌線(ENE)組成,軌道線的優勢在於其不僅具有趨勢軌道的研判分析作用,也可以敏銳的覺察股價運行過程中方向的改

指標5-- 布林線指標BOLL

tar 線下 pan evel 隨著 向上 log link bsp 一、定義:布林線指標,即BOLL指標,其英文全稱是“Bollinger Bands”,布林線(BOLL)由約翰·布林先生創造,其利用統計原理,求出股價的標準差及其信賴區間,從而確定股價的波動範圍及未來走勢

GoLang基礎資料型別--->字典map

                     GoLang基礎資料型別--->字典(map)詳解                                             作者:尹正傑 版權宣告:原創作品,謝絕轉載!否則將追究法律責任。  

linux 之mysql——約束constraint

一、什麼是約束 約束英文:constraint 約束實際上就是表中資料的限制條件 二、約束作用 表在設計的時候加入約束的目的就是為了保證表中的記錄完整和有效性 比如name欄位中要讓其使用者名稱不重複,這就需要新增約束。或者必須註冊的時候需要新增郵箱等  三、約束種類

c++迭代器iterator【轉】

(轉自:https://www.cnblogs.com/hdk1993/p/4419779.html) 1. 迭代器(iterator)是一中檢查容器內元素並遍歷元素的資料型別。 (1) 每種容器型別都定義了自己的迭代器型別,如vector: vector<int>::it

HttpURLConnection

(轉)詳解HttpURLConnection 請求響應流程 設定連線引數的方法     setAllowUserInteraction setDoInput setDoOutput setIfModifiedSince se

SpringMVC學習 Dispatcher

前端控制器 Dispatcherservlet 截獲請求後做了什麼工作呢?DispatcherServlet 又是如何分派請求的呢? 分析DispatcherServlet 原始碼如下: protected void initStrategies(ApplicationContext

自動化監控--zabbix中的Macros巨集

巨集 Zabbix支援許多在多種情況下使用的巨集。巨集是一個變數,由如下特殊語法標識: {MACRO} 根據在上下文中, 巨集解析為一個特殊的值。有效地使用巨集可以節省時間,並使Zabbix變地更加高效。 在一個的典型用途中,巨集可以用於模板中。因此,模板的觸發器可能

自動化監控--zabbix中的template模板

模板概述 模板是可以方便地應用於多個主機的一組實體。而這些實體包括:items(監控項)、triggers(觸發器),graphs(圖形)、applications(應用)、screens (聚合圖形(自Zabbix 2.0起))、low-level discovery rules