国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【嵌入式環(huán)境下linux內(nèi)核及驅(qū)動(dòng)學(xué)習(xí)筆記-(5-驅(qū)動(dòng)的并發(fā)控制機(jī)制)】

這篇具有很好參考價(jià)值的文章主要介紹了【嵌入式環(huán)境下linux內(nèi)核及驅(qū)動(dòng)學(xué)習(xí)筆記-(5-驅(qū)動(dòng)的并發(fā)控制機(jī)制)】。希望對大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

1、上下文和并發(fā)

在討論并發(fā)前,先要了解以下幾個(gè)概念:執(zhí)行流,上下文,共享與臨界等。

什么叫執(zhí)行流:

  • 【執(zhí)行流】:有開始有結(jié)束總體順序執(zhí)行的一段代碼 又稱上下文

上下文分類:

  • 【任務(wù)上下文】:普通的,具有五種狀態(tài)(就緒態(tài)、運(yùn)行態(tài)、睡眠態(tài)、暫停態(tài)、僵死態(tài)),可被阻塞的上下文。
  • 【異常上下文】:異常時(shí)的中斷處理上下文。不能進(jìn)入阻塞狀態(tài)。

\qquad 對于應(yīng)用層編程而言,只有任務(wù)上下文。
\qquad 對于內(nèi)核編程而言,即有任務(wù)上下文也有異常上下文。

什么是共享與臨界:

  • 【競態(tài)】:多任務(wù)并行執(zhí)行時(shí),如果在一個(gè)時(shí)刻同時(shí)操作同一個(gè)資源,會(huì)引起資源的錯(cuò)亂,這種錯(cuò)亂情形被稱為競態(tài)
  • 【共享資源】:可能會(huì)被多個(gè)任務(wù)同時(shí)使用的資源。
  • 【臨界區(qū)】:操作共享資源的代碼段。

因此為了控制競態(tài),就需要有相應(yīng)的“并發(fā)控制機(jī)制”。

  • 【并發(fā)控制機(jī)制】:為了解決競態(tài),需要提供一種控制機(jī)制,來避免在同一時(shí)刻使用共享資源,這種機(jī)制被稱為并發(fā)控制機(jī)制

2、并發(fā)控制機(jī)制的分類與使用場景

內(nèi)核中并發(fā)控制機(jī)制分為以下幾類:

  • 原子操作類 :不會(huì)被系統(tǒng)包括異常打斷的操作。
  • 忙等待類 :通過循環(huán)輪詢的方式,等待資源可用的操作。
  • 阻塞類:資源不可用時(shí),進(jìn)入睡眠態(tài)等待資源可用。

并發(fā)控制機(jī)制的使用場景:

  • 互斥場景:多個(gè)任務(wù)不能同時(shí)使用同一資源,一個(gè)在用時(shí),其它的處于等待狀態(tài),互斥體現(xiàn)的是一種排它性。一般操作過程如下:

對互斥鎖初始化為可用 --> 對互斥鎖進(jìn)行P操作鎖定 --> 臨界區(qū) --> 對互斥鎖進(jìn)行V操作放開

  • 同步場景:多個(gè)并行任務(wù)對資源的有序訪問,同步體現(xiàn)的是一種協(xié)作性。

對互斥鎖初始化為不可用 --> “先行任務(wù)”完成所有操作后 --> “先行方”進(jìn)行V操作釋放資源 --> “后行方”P操作直到鎖定資源 -->“后行方”執(zhí)行操作。

3、并發(fā)控制詳述

3.1 并發(fā)控制機(jī)制–中斷屏蔽

使用原則:
\qquad 當(dāng)一個(gè)中斷服務(wù)程序ISR與被打斷的任務(wù)可能使用相同的資源時(shí)。這時(shí),就需要在被打斷的任務(wù)在進(jìn)入臨界區(qū)之前先進(jìn)行中斷屏蔽,等執(zhí)行完成后再恢復(fù)中斷。

中斷屏蔽相關(guān)的函數(shù)如下:

中斷屏蔽相關(guān)函數(shù) 使能中斷相關(guān)函數(shù)
local_irq_disable() loacal_irq_enable()
local_irq_save(flags) loacal_irq_restore(flags) 涉及cpu的中斷屏蔽字相關(guān)
local_bh_disable() local_bh_enable() 與中斷低半部有關(guān),操作軟中斷

注意事項(xiàng)

  • 中斷屏蔽后的臨界區(qū)代碼不能占用太長時(shí)間,需要盡快完成。否則中被屏蔽會(huì)引起系統(tǒng)調(diào)度等一系列問題。
  • Local_irq_disable()和local_irq_enable()都只能禁止和使能本CPU內(nèi)的中斷,因此,并不能解決SMP多CPU引發(fā)的況態(tài)。因此單獨(dú)使用
  • 中斷屏蔽通常不是一種值得推薦的避免況態(tài)的方法,它適合與下面要介紹的自旋鎖聯(lián)合使用。

適用場合:中斷上下文與某任務(wù)共享資源時(shí),或多個(gè)不同優(yōu)先級的中斷上下文間共享資源時(shí)。

3.1.1 函數(shù)詳解

3.1.1.1 local_irq_enable 與 local_irq_disable

local_irq_enable()和local_irq_disable()函數(shù)用于在Linux內(nèi)核中臨時(shí)啟用和禁用本地中斷。
原型為:

#include <asm/irq.h>

void local_irq_enable(void);
void local_irq_disable(void); 

這兩個(gè)函數(shù)的作用是:

  • local_irq_enable(): 重新啟用本地中斷,允許硬件在CPU上發(fā)送中斷信號。
  • local_irq_disable(): 禁用本地中斷,防止硬件在CPU上發(fā)送任何中斷信號。
    本地中斷是CPU上除NMI之外的所有外部中斷,比如時(shí)鐘中斷、串口中斷、網(wǎng)卡中斷等。

使用示例:

void do_some_thing(void) 
{
    local_irq_disable();  // 禁用本地中斷
    /*做一些需要防止中斷的操作*/  
    
    local_irq_enable(); // 操作完成,重新啟用中斷
}

這兩個(gè)函數(shù)通常用于:

  1. 臨時(shí)保護(hù)一小段關(guān)鍵代碼或共享資源,防止中斷處理程序干擾。因?yàn)橹袛嗵幚沓绦驎?huì)打斷正常指令流,可能訪問共享資源。
  2. 在原子操作周圍,確保整個(gè)操作序列不被中斷打斷,從而避免競爭條件。
  3. 在本地時(shí)鐘節(jié)拍的開頭和結(jié)尾,用于測量某段代碼的執(zhí)行時(shí)間等。

它們會(huì)修改CPU的狀態(tài)寄存器中的IF標(biāo)志位來禁用或重新啟用中斷。在禁用中斷期間,所有的中斷請求都會(huì)被屏蔽,直到再次啟用中斷。


3.1.1.2 local_irq_save 與 local_irq_restore

local_irq_save()和local_irq_restore()函數(shù)也用于在Linux內(nèi)核中臨時(shí)保存和恢復(fù)本地中斷狀態(tài)。
原型為:

#include <asm/irq.h>

unsigned long local_irq_save(void);
void local_irq_restore(unsigned long flags);

這兩個(gè)函數(shù)的作用是:

  • local_irq_save(): 禁用本地中斷,并返回此前的中斷狀態(tài)。
  • local_irq_restore(): 根據(jù)傳入的flags恢復(fù)之前保存的本地中斷狀態(tài)。

與local_irq_enable()和local_irq_disable()相比,local_irq_save()可以保存當(dāng)前的中斷狀態(tài),并在稍后通過local_irq_restore()恢復(fù)。而local_irq_enable()和local_irq_disable()無法達(dá)到保存與恢復(fù)中斷狀態(tài)的效果。

使用示例:

unsigned long flags;

flags = local_irq_save();  // 禁用中斷并保存狀態(tài)
/*做一些需要防止中斷的操作*/  

local_irq_restore(flags); // 操作完成,恢復(fù)之前的中斷狀態(tài)

這兩個(gè)函數(shù)的用途與local_irq_enable()和local_irq_disable()類似,常用于:

  1. 臨時(shí)保護(hù)關(guān)鍵代碼或共享資源,但需要在稍后恢復(fù)原有的中斷狀態(tài)。
  2. 實(shí)現(xiàn)復(fù)雜的同步機(jī)制,根據(jù)需要禁用與恢復(fù)中斷。
  3. 精確測量某段代碼的執(zhí)行時(shí)間,通過保存并恢復(fù)中斷狀態(tài)實(shí)現(xiàn)準(zhǔn)確計(jì)時(shí)。
    與local_irq_enable()/disable()相比,這兩個(gè)函數(shù)可以更精細(xì)地控制中斷狀態(tài),實(shí)現(xiàn)根據(jù)條件條件性禁用與恢復(fù)中斷。這在編寫驅(qū)動(dòng)程序和內(nèi)核同步機(jī)制時(shí)非常有用。
3.1.1.3 local_bh_enable 與 local_bh_disable

local_bh_enable()和local_bh_disable()函數(shù)用于在Linux內(nèi)核中臨時(shí)啟用和禁用**底半部(Bottom Half)**處理。
原型為:

#include <asm/irq.h>

void local_bh_enable(void);
void local_bh_disable(void);

這兩個(gè)函數(shù)的作用是:

  • local_bh_enable(): 重新啟用底半部處理,允許待處理的軟中斷和任務(wù)調(diào)度被執(zhí)行。
  • local_bh_disable(): 禁用底半部處理,禁止執(zhí)行任何軟中斷處理程序和任務(wù)調(diào)度。

底半部處理通常由兩個(gè)部分組成:

  1. 軟中斷處理:對應(yīng)內(nèi)核定時(shí)器中斷,用于處理超時(shí)和延遲機(jī)制。
  2. 任務(wù)調(diào)度:當(dāng)中斷被重新啟用時(shí),如果有更高優(yōu)先級的任務(wù)就緒,則調(diào)度程序會(huì)選擇它運(yùn)行。

這兩個(gè)函數(shù)通過修改本地的嵌套計(jì)數(shù)來禁用或重新啟用底半部處理。只有當(dāng)嵌套計(jì)數(shù)變?yōu)?時(shí),底半部機(jī)制才會(huì)被重新啟用。

使用示例:

void do_some_thing(void)
{
    /* 首先禁用底半部處理 */
    local_bh_disable();   
    
    /* 開始處理一個(gè)硬中斷 */
    handle_hard_irq();
    
    /* 硬中斷處理完成,檢查是否接收到信號 */
    if (signal_pending(current)) {
        /* 收到信號,禁止底半部機(jī)制并處理信號 */ 
        local_bh_disable();  
        handle_signal();
        local_bh_enable();  // 信號處理完成,重新啟用底半部
    }
    
    /* 其他與硬中斷處理相關(guān)的操作 */
    do_some_task_1();
    do_some_task_2();
    
    /* 檢查軟中斷是否到期,如果到期則立即處理 */
    if (softirq_pending(smp_processor_id()))
        invoke_softirq();
        
    /* 所有操作完成,重新啟用底半部機(jī)制 */ 
    local_bh_enable();  
}

這個(gè)例子展示了如何在不同的場景下使用local_bh_enable()和local_bh_disable():

  1. 一開始禁用底半部機(jī)制,以避免硬中斷處理過程中出現(xiàn)任務(wù)調(diào)度或軟中斷。
  2. 在硬中斷處理完成后,如果收到信號也首先禁用底半部機(jī)制,然后處理信號,最后再重新啟用。這是因?yàn)樾盘柼幚硪膊荒苓M(jìn)行任務(wù)調(diào)度與軟中斷。
  3. 處理與硬中斷相關(guān)的其它操作時(shí),底半部機(jī)制仍然被禁用。
  4. 如果在此期間軟中斷到期,則立即處理。因?yàn)檐浿袛嗟某瑫r(shí)時(shí)間很短,必須先處理。
  5. 所有操作完成后,重新啟用底半部機(jī)制,允許任務(wù)調(diào)度與軟中斷處理。
    所以,這段代碼展示了根據(jù)運(yùn)行上下文與需求,采取啟用或禁用底半部機(jī)制的方法。這可以最大限度地避免不同的處理過程之間出現(xiàn)干擾,實(shí)現(xiàn)良好的同步與時(shí)序控制。

這兩個(gè)函數(shù)通常用于:

  1. 臨時(shí)禁止軟中斷和任務(wù)調(diào)度,從而防止關(guān)鍵代碼或資源被打亂。
  2. 處理收到的信號,避免在信號處理期間進(jìn)行任務(wù)調(diào)度。
  3. 實(shí)現(xiàn)同步機(jī)制,根據(jù)需要選擇性啟用或禁用底半部處理。
    如果對底半部機(jī)制與這兩個(gè)函數(shù)還不太理解,可以參考我的前面的相關(guān)介紹。理解內(nèi)核的不同運(yùn)行上下文、調(diào)度機(jī)制與同步手段是成為Linux內(nèi)核開發(fā)高手的基礎(chǔ)。

3.2、并發(fā)控制機(jī)制–原子變量

原子變量: 存取時(shí)不可被打斷的特殊整型變量。
適用場合: 共享資源為單個(gè)整型變量的互斥場合。

3.2.1 相關(guān)函數(shù)

對原子變量的操作必須用下面這些專用宏或函數(shù):

3.2.1.1 atomic_t 原子量類型

頭文件 /arch/arm/include/asm/atomic.h

#include <linux/types.h

typedef struct {
	int counter;
} atomic_t;

該類型本質(zhì)是一個(gè)數(shù)據(jù)結(jié)構(gòu)。

3.2.1.2 宏ATOMIC_INIT 創(chuàng)建原子變量

ATOMIC_INIT(i)是一個(gè)宏,用于初始化一個(gè)原子變量為值i。
它定義在頭文件中,展開后的定義為:

#include <asm/atomic.h> 

#define ATOMIC_INIT(i)    { (i) }

也就是將傳入的值i放在一個(gè)括號對{}中。

這個(gè)宏用于靜態(tài)初始化一個(gè)原子變量,語法為:

atomic_t my_atomic = ATOMIC_INIT(10);

這會(huì)將my_atomic初始化為10。

原子變量是Linux內(nèi)核實(shí)現(xiàn)同步機(jī)制的基礎(chǔ),它可以用于實(shí)現(xiàn)自旋鎖、計(jì)數(shù)器、標(biāo)志位等。ATOMIC_INIT宏提供了一個(gè)簡單的方法來初始化原子變量。
在內(nèi)核開發(fā)中,我們通常會(huì)定義如下變量使用ATOMIC_INIT初始化:

atomic_t counter = ATOMIC_INIT(0);    // 計(jì)數(shù)器
atomic_t spinlock = ATOMIC_INIT(0);   // 自旋鎖 
atomic_t ready = ATOMIC_INIT(0);      // 標(biāo)志位

然后可以使用如下方法操作這些原子變量:

atomic_inc(&counter);    // 計(jì)數(shù)器+1
atomic_dec(&counter);    // 計(jì)數(shù)器-1

atomic_set(&spinlock, 1); // 獲得自旋鎖
atomic_set(&spinlock, 0); // 釋放自旋鎖

atomic_set(&ready, 1);   // 設(shè)置標(biāo)志位 
atomic_read(&ready);     // 讀取標(biāo)志位

所以,ATOMIC_INIT宏提供了初始化原子變量的簡單方法,配合其它原子操作宏可以實(shí)現(xiàn)各種同步機(jī)制與計(jì)數(shù)功能。

3.2.1.3 宏atomic_set 設(shè)置原子量的值

頭文件 /arch/arm/include/asm/atomic.h
原碼:

#include <asm/atomic.h> 

#define atomic_set(v,i)	(((v)->counter) = (i))

所以,可以如下定義原子量的初始值

atomic_t v = ATOMIC_INIT(0); //定義原子變量v并初始化為0
void atomic_set(atomic_t *v,int i); //設(shè)置原子量的值為i

atomic_t my_atomic;
atomic_set(&my_atomic, 10);

而像:v = 10;這樣做是錯(cuò)誤的, 因?yàn)椴荒軐υ幼兞抠x值操作,所以這里是錯(cuò)誤的

3.2.1.4 宏atomic_read 獲取原子量的值

這個(gè)宏用于讀取一個(gè)原子變量的值
頭文件 /arch/arm/include/asm/atomic.h
原碼:

#include <asm/atomic.h> 

#define atomic_read(v)	(*(volatile int *)&(v)->counter)

返回值:

返回原子變量*v的counter成員的值 , int類型。

用法為:

atomic_t my_atomic;
int val = atomic_read(&my_atomic);

這會(huì)將my_atomic的值讀取到val變量中。

atomic_read宏是操作原子變量的基本方法之一,它可以直接讀取原子變量的值而不需要任何同步機(jī)制。這是因?yàn)樵幼兞吭趯?shí)現(xiàn)上包含了必要的鎖或其它同步手段,可以保證各個(gè)CPU對其的操作是串行的。

在內(nèi)核開發(fā)中,我們常會(huì)使用atomic_read宏:

  1. 讀取自旋鎖的狀態(tài),判斷是否上鎖:
atomic_t lock = ATOMIC_INIT(0);  
if (atomic_read(&lock)) {
    /* 上鎖,需要處理 */ 
}
  1. 讀取標(biāo)志位的狀態(tài):
atomic_t ready = ATOMIC_INIT(0);
if (atomic_read(&ready)) {
    /* 標(biāo)志位已置,進(jìn)行相關(guān)處理 */ 
}
  1. 直接獲取計(jì)數(shù)器的值:
atomic_t count = ATOMIC_INIT(0);
int val = atomic_read(&count);

所以,atomic_read宏提供了一種簡單的方法來讀取原子變量的值,配合其它原子操作宏可以實(shí)現(xiàn)讀取各種同步機(jī)制與狀態(tài)。

3.2.1.5 宏atomic_add 與 atomic_sub原子變量加減

頭文件 /arch/arm/include/asm/atomic.h
原碼:

#include <asm/atomic.h> 

>#define atomic_add(i, v)	(void) atomic_add_return(i, v)
>static inline int atomic_add_return(int i, atomic_t *v)
{
	unsigned long flags;
	int val;
	raw_local_irq_save(flags);
	val = v->counter;
	v->counter = val += i;
	raw_local_irq_restore(flags);
	return val;
}

atomic_add宏利用atomic_add_return()函數(shù)來實(shí)現(xiàn)真正的ADD操作,后者會(huì)根據(jù)體系結(jié)構(gòu)采用基于鎖或無鎖的機(jī)制來同步該操作。對使用者來說,atomic_add提供了一個(gè)簡單的接口來原子更新原子變量的值。

#include <asm/atomic.h>
>#define atomic_sub(i, v)	(void) atomic_sub_return(i, v)
>static inline int atomic_sub_return(int i, atomic_t *v)
{
	unsigned long flags;
	int val;
	raw_local_irq_save(flags);
	val = v->counter;
	v->counter = val -= i;
	raw_local_irq_restore(flags);
	return val;
}

所以可以這樣用:

void atomic_add(int i,atomic_t *v);//原子變量增加I,返回V的Int類型值
void atomic_sub(int i,atomic_t *v);//原子變量減少I,返回V的Int類型值
3.2.1.6 宏 atomic_inc 和 atomic_dec 原子變量自增自減

頭文件 /arch/arm/include/asm/atomic.h
原型:

#include <asm/atomic.h>
>#define atomic_inc(v)		atomic_add(1, v)
>#define atomic_dec(v)		atomic_sub(1, v)

atomic_inc和atomic_dec宏利用atomic_add()和atomic_sub()來實(shí)現(xiàn)加減一操作,這兩個(gè)函數(shù)會(huì)根據(jù)體系結(jié)構(gòu)采用基于鎖或無鎖的機(jī)制來同步該操作。所以,對使用者來說,atomic_inc和atomic_dec提供了簡單的接口來原子更新原子變量的值。

所以可以這樣用:

void atomic_inc(atomic_t *v);//原子變量增加1,返回V的Int類型值
void atomic_dec(atomic_t *v);//原子變量減少1,返回V的Int類型值
3.2.1.7 操作并測試

頭文件 /arch/arm/include/asm/atomic.h

原型:

#define atomic_inc_and_test(v)	(atomic_add_return(1, v) == 0)
#define atomic_dec_and_test(v)	(atomic_sub_return(1, v) == 0)
#define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0)

所以可以這樣使用函數(shù):

運(yùn)算后結(jié)果為0則返回真,否則返回假

int atomic_inc_and_test(atomic_t *v);
int atomic_dec_and_test(atomic_t *v);
int atomic_sub_and_test(int i,atomic_t *v);
3.2.1.8 ATOMIC_BITOP原子位操作方法

ATOMIC_BITOP()宏用于在一個(gè)原子變量上執(zhí)行位操作,如設(shè)置、清除和翻轉(zhuǎn)指定的位。
頭文件:/arch/arm/include/asm/bitops.h
原碼:

#include <asm/bitops.h>
#define ATOMIC_BITOP(name,nr,p)             (__builtin_constant_p(nr) ? ____atomic_##name(nr, p) : _##name(nr,p))

這個(gè)宏定義的意思是:

如果nr(代表bit number,即位號)是一個(gè)常數(shù),則使用____atomic_##name形式的位操作函數(shù)。
否則,使用_##name形式的位操作函數(shù)。

舉個(gè)例子,如果定義如下宏:

#define SET_BIT(nr, p) ATOMIC_BITOP(set_bit, nr, p)

那么當(dāng)調(diào)用SET_BIT(5, ptr)時(shí),如果5是一個(gè)常數(shù),會(huì)展開為:

____atomic_set_bit(5, ptr)

否則,會(huì)展開為:

_set_bit(nr, ptr)

==============================================================
補(bǔ)充:
在宏定義中使用“##”運(yùn)算符可以將兩個(gè)符號連接成一個(gè)符號。這被稱為“宏連接(Macro Concatenation)”。
例如在這個(gè)宏定義中:
#define ATOMIC_BITOP(name,nr,p) (__builtin_constant_p(nr) ? ____atomic_##name(nr, p) : _##name(nr,p))
“##”運(yùn)算符被用于:
____atomic_##name: 將name和____atomic_連接為一個(gè)符號,例如____atomic_set_bit
“_##name”:將_和name連接為一個(gè)符號,例如_set_bit
所以如果定義:
#define SET_BIT(nr, p) ATOMIC_BITOP(set_bit, nr, p)
那么這個(gè)宏可以展開為:
(__builtin_constant_p(nr) ? ____atomic_set_bit(nr, p) : _set_bit(nr,p))
=============================================================

3.2.1.9 用ATOMIC_BITOP構(gòu)造所位操作函數(shù)

原型
頭文件:/arch/arm/include/asm/bitops.h

#define set_bit(nr,p)			ATOMIC_BITOP(set_bit,nr,p)
#define clear_bit(nr,p)			ATOMIC_BITOP(clear_bit,nr,p)
#define change_bit(nr,p)		ATOMIC_BITOP(change_bit,nr,p)
#define test_and_set_bit(nr,p)		ATOMIC_BITOP(test_and_set_bit,nr,p)
#define test_and_clear_bit(nr,p)	ATOMIC_BITOP(test_and_clear_bit,nr,p)
#define test_and_change_bit(nr,p)	ATOMIC_BITOP(test_and_change_bit,nr,p)

解釋:

  • set_bit(nr,p):設(shè)置p中的第nr位,等價(jià)于p |= (1 << nr)
  • clear_bit(nr,p):清除p中的第nr位,等價(jià)于p &= ~(1 << nr)
  • change_bit(nr,p):翻轉(zhuǎn)p中的第nr位,等價(jià)于p ^= (1 << nr)
  • test_and_set_bit(nr,p):原子地測試第nr位是否為0,如果是則設(shè)置該位,并返回原值
  • test_and_clear_bit(nr,p):原子地測試第nr位是否為1,如果是則清除該位,并返回原值
  • test_and_change_bit(nr,p):原子地測試第nr位,并翻轉(zhuǎn)該位,返回原值

所以可以這么用:

  • 設(shè)置位
    void set_bit(nr, void *addr); //設(shè)置addr所指向的數(shù)據(jù)的第nr位為1
  • 清除位
    void clear_bit(nr , void *addr);//清除addr所指向的數(shù)據(jù)的第nr位為0
  • 改變位
    void change_bit(nr , void *addr);//改變addr所指向的數(shù)據(jù)的第nr位為1
  • 測試位
    void test_bit(nr , void *addr); //測試addr所指向的數(shù)據(jù)的第nr位是否為1

3.2.2 實(shí)例

要求:字符驅(qū)動(dòng)只能被一個(gè)應(yīng)用進(jìn)程使用。即只能被一個(gè)任務(wù)open,其它任務(wù)在同一時(shí)間要打開該設(shè)備時(shí)會(huì)出錯(cuò)提示。

/*************************************************************************
	> File Name: atomic-only.c
    > 作用:以原子變量做為并發(fā)控制的手段,使本驅(qū)動(dòng)同時(shí)只能被一個(gè)應(yīng)用層進(jìn)程所open
 ************************************************************************/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/types.h>

/*1、定義重要的變量及結(jié)構(gòu)體*/
struct x_dev_t {
    struct cdev  my_dev;  //cdev設(shè)備描述結(jié)構(gòu)體變量
    atomic_t have_open;   //記錄驅(qū)動(dòng)是否被打開的原子變量,在init()時(shí)初始化,設(shè)置初值。在open()時(shí)打開
};

struct x_dev_t *pcdev;

/*所有驅(qū)動(dòng)函數(shù)聲明*/
int open (struct inode *, struct file *);
int close (struct inode *, struct file *);

//驅(qū)動(dòng)操作函數(shù)結(jié)構(gòu)體,成員函數(shù)為需要實(shí)現(xiàn)的設(shè)備操作函數(shù)指針
//簡單版的模版里,只寫了open與release兩個(gè)操作函數(shù)。
struct file_operations fops={
    .open = open,
    .release = close,
};


static int __init my_init(void){
    int unsucc =0;
    dev_t devno;
    int major,minor;
    pcdev = kzalloc(sizeof(struct x_dev_t), GFP_KERNEL);
    /*2、創(chuàng)建 devno */
    unsucc = alloc_chrdev_region(&devno , 0 , 1 , "atomic-char");
    if (unsucc){
        printk(" creating devno  faild\n");
        return -1;
    }
    major = MAJOR(devno);
    minor = MINOR(devno);
    printk("devno major = %d ; minor = %d;\n",major , minor);

    /*3、初始化 cdev結(jié)構(gòu)體,并將cdev結(jié)構(gòu)體與file_operations結(jié)構(gòu)體關(guān)聯(lián)起來*/
    /*這樣在內(nèi)核中就有了設(shè)備描述的結(jié)構(gòu)體cdev,以及設(shè)備操作函數(shù)的調(diào)用集合file_operations結(jié)構(gòu)體*/
    cdev_init(&pcdev->my_dev , &fops);
    pcdev->my_dev.owner = THIS_MODULE;
    /*4、注冊cdev結(jié)構(gòu)體到內(nèi)核鏈表中*/
    unsucc = cdev_add(&pcdev->my_dev,devno,1);
    if (unsucc){
        printk("cdev add faild \n");
        return 1;
    }
    //初始化原子量have_open為1
    atomic_set(&pcdev->have_open,1);
    
    printk("the driver atomic-char initalization completed\n");


    return 0;
}


static void  __exit my_exit(void)
{
    cdev_del(&pcdev->my_dev);
    unregister_chrdev_region(pcdev->my_dev.dev , 1);
    printk("***************the driver atomic-char exit************\n");
}
/*5、驅(qū)動(dòng)函數(shù)的實(shí)現(xiàn)*/
/*file_operations結(jié)構(gòu)全成員函數(shù).open的具體實(shí)現(xiàn)*/
int open(struct inode *pnode , struct file *pf){
    struct x_dev_t *p = container_of(pnode->i_cdev,struct x_dev_t , my_dev);
    pf->private_data = (void *)p;
    //在open函數(shù)中對原子量have_open進(jìn)行減1并檢測。=0,允許打開文件,<0則不允許打開
    if (atomic_dec_and_test(&p->have_open)){
        printk("atomic-char is opened\n");
        return 0;
    }else{
        printk("device atomic-char can't be opened again\n");
        atomic_inc(&p->have_open);//原子量=-1,記得這里要把原子量加回到0
        return -1;
    }   
}
/*file_operations結(jié)構(gòu)全成員函數(shù).release的具體實(shí)現(xiàn)*/
int close(struct inode *pnode , struct file *pf){
    struct x_dev_t *p = (struct x_dev_t *)pf->private_data;
    printk("atomic-char is closed \n");
    atomic_set(&p->have_open,1);
    return 0;
}


module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("");

3.3、并發(fā)控制機(jī)制–自旋鎖

3.3.1 概念

\qquad 自旋鎖(Spin Lock)是一種典型的對臨界資源進(jìn)行互斥訪問的手段,其名稱來源于它的工作方式。為了獲得一個(gè)自旋鎖,在某個(gè)CPU上運(yùn)行的代碼需先執(zhí)行一個(gè)原子操作,該操作測試并設(shè)置某個(gè)內(nèi)存變量。由于它是原子操作,所以在該操作完成之前其它執(zhí)行單元不可能訪問這個(gè)內(nèi)存變量。如果測試結(jié)果表明鎖已經(jīng)空閑,則程序獲得這個(gè)自旋鎖并繼續(xù)執(zhí)行;如果測試結(jié)果表明鎖仍被占用,程序?qū)⒃谝粋€(gè)小的循環(huán)內(nèi)重復(fù)這個(gè)“測試并設(shè)置”操作,即進(jìn)行所謂的“自旋”,通俗說就是“在原地打轉(zhuǎn)”。當(dāng)自旋鎖 持有者通過重置該變量釋放這個(gè)自旋鎖后,某個(gè)等待的“測試并設(shè)置”操作向其調(diào)用者報(bào)告鎖已釋放。

\qquad 自旋鎖是基于忙等待的并發(fā)控制機(jī)制,由于在獲取不到資源鎖時(shí)會(huì)進(jìn)入忙等待,不會(huì)進(jìn)入睡眠狀態(tài)。忙等待是占時(shí)間片的,因此這個(gè)機(jī)制不要用于過長的臨界區(qū)執(zhí)行。也因此,這個(gè)互斥鎖可用于異常上下文,不會(huì)使異常進(jìn)入阻塞狀態(tài)。

適用場合:

  1. 異常上下文之間或異常上下文與任務(wù)上下文之間共享資源時(shí)
  2. 任務(wù)上下文之間且臨界區(qū)執(zhí)行時(shí)間很短時(shí)
  3. 互斥問題

3.3.2 函數(shù)

頭文件: /include/linux/spinlock_types.h

3.3.2.1.定義自旋鎖

原碼:

#include <linux/spinlock.h>

typedef struct spinlock {
	union {
		struct raw_spinlock rlock;

#ifdef CONFIG_DEBUG_LOCK_ALLOC
# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))
		struct {
			u8 __padding[LOCK_PADSIZE];
			struct lockdep_map dep_map;
		};
#endif
	};
} spinlock_t;
  1. rlock - 這個(gè)是Raw spinlock,用于實(shí)際的自旋鎖定。它包含lock和unlock操作,這是真正控制互斥鎖的部分。
  2. dep_map - 這個(gè)是依賴關(guān)系映射,在開啟CONFIG_DEBUG_LOCK_ALLOC宏的情況下使用。它用于記錄自旋鎖獲取和釋放之間的依賴關(guān)系,以檢測潛在的死鎖問題。
    所以spinlock_t中嵌入了兩個(gè)結(jié)構(gòu):
  • raw_spinlock - 用于實(shí)際的鎖定,所有配置都會(huì)包含這個(gè)結(jié)構(gòu)。
  • lockdep_map - 僅在debug配置中包含,用于記錄鎖依賴關(guān)系,以便檢測死鎖。
    所以使用前先定義自旋鎖:
    spinlock_t lock;
3.3.2.2 初始化自旋鎖

源碼

#define <linux/spinlock.h>

#define spin_lock_init(_lock)				\
do {							\
	spinlock_check(_lock);				\
	raw_spin_lock_init(&(_lock)->rlock);		\
} while (0)
  • spinlock_check()用來檢查_lock是否指向一個(gè)正確的spinlock_t對象。
  • raw_spin_lock_init()用來初始化raw_spinlock部分,將lock置0等。

用法

spin_lock_init(spinlock_t *);

3.3.2.3 獲得自旋鎖(即P操作,置資源為被用)

原型:

#include <linux/spinlock.h>

static inline void spin_lock(spinlock_t *lock)
{
	raw_spin_lock(&lock->rlock);
}

static inline void spin_lock_bh(spinlock_t *lock)
{
	raw_spin_lock_bh(&lock->rlock);
}

static inline int spin_trylock(spinlock_t *lock)
{
	return raw_spin_trylock(&lock->rlock);
}

static inline void spin_lock_irq(spinlock_t *lock)
{
	raw_spin_lock_irq(&lock->rlock);
}

解釋:

  • spin_lock():成功獲得自旋鎖立即返回,否則自旋在那里,直到該自旋鎖的保持者釋放。禁止中斷。
  • spin_lock_bh():獲取自旋鎖。禁止軟中斷。
  • spin_trylock():成功獲得自旋鎖立即返回真,否則返回假,而不是像上一個(gè)那樣"在原地打轉(zhuǎn)”
  • spin_lock_irq: 獲取自旋鎖lock,禁止中斷,并保存中斷標(biāo)志位。

它們的定義均通過調(diào)用raw_spinlock部分的對應(yīng)函數(shù)來實(shí)現(xiàn)實(shí)際的鎖定操作。raw_spinlock部分實(shí)現(xiàn)最基本的自旋鎖機(jī)制,這三個(gè)接口在它的基礎(chǔ)上實(shí)現(xiàn)了Disable中斷,Disable軟中斷等額外功能。

spin_lock(lock);        // 禁止中斷,上鎖
// 臨界區(qū)代碼  
spin_unlock(lock);      // 解鎖,開中斷

spin_lock_bh(lock);     // 禁止軟中斷,上鎖 
// 臨界區(qū)代碼
spin_unlock_bh(lock);   // 解鎖,開軟中斷

spin_lock_irq(lock);
// 臨界區(qū)代碼
spin_unlock_irq(lock);  // 釋放鎖,根據(jù)之前保存的狀態(tài)決定是否開中斷

if (spin_trylock(lock)) { // 嘗試上鎖,成功則進(jìn)入臨界區(qū)
    // 臨界區(qū)代碼
}  else {
    // 獲取鎖失敗,做其他事情
}

這幾個(gè)接口與自旋鎖spinlock_t的初始化和銷毀等一起,提供了Linux內(nèi)核中的自旋鎖機(jī)制。它們在許多需要互斥訪問共享資源的場景中得到使用,是內(nèi)核同步的基本方法之一。

重點(diǎn)

在中斷服務(wù)程序(ISR)中選擇合適的自旋鎖接口,需要考慮單核與多核的差異:
在單核下:
- spin_lock()就足夠,它會(huì)禁止中斷,保證ISR的互斥訪問。
- spin_lock_irq()和spin_lock_irqsave()則沒必要,因?yàn)橹挥幸粋€(gè)CPU,不會(huì)有競爭的ISR同時(shí)訪問臨界資源。
在多核下:
- spin_lock()不夠,它僅禁止中斷,無法在多個(gè)CPU之間提供互斥。
- 應(yīng)選擇spin_lock_irq()或spin_lock_irqsave()。
    - spin_lock_irq()會(huì)禁止本地CPU的中斷,并通過自旋等待在其他CPU上執(zhí)行的競爭ISR,進(jìn)而實(shí)現(xiàn)互斥。
    - spin_lock_irqsave()同spin_lock_irq(),但可以提供更大的靈活性,因?yàn)橹袛酄顟B(tài)被保存在外部變量中,可以在任意時(shí)刻進(jìn)行恢復(fù)。
所以,總結(jié)如下:
在單核下,ISR中可以直接使用spin_lock()。
在多核下,ISR應(yīng)使用spin_lock_irq()或spin_lock_irqsave()以實(shí)現(xiàn)多個(gè)CPU之間的互斥訪問。
選擇spin_lock_irq()還是spin_lock_irqsave()需要根據(jù)具體的使用場景而定。如果需要在鎖定外實(shí)現(xiàn)更復(fù)雜的中斷狀態(tài)控制流程,應(yīng)選擇spin_lock_irqsave(),否則spin_lock_irq()就足夠簡單高效。

3.3.2.4 釋放自旋鎖(即V操作,置資源為可用)

原型

#include <linux/spinlock.h>


static inline void spin_unlock(spinlock_t *lock)
{
	raw_spin_unlock(&lock->rlock);
}

static inline void spin_unlock_bh(spinlock_t *lock)
{
	raw_spin_unlock_bh(&lock->rlock);
}

static inline void spin_unlock_irq(spinlock_t *lock)
{
	raw_spin_unlock_irq(&lock->rlock);
}
  • spin_unlock(): 釋放自旋鎖,開中斷。
  • spin_unlock_bh(): 釋放自旋鎖,開軟中斷。

它們通過調(diào)用raw_spinlock部分的對應(yīng)接口來實(shí)現(xiàn)實(shí)際的解鎖操作。整個(gè)流程為:

spin_lock(lock);   // 上鎖,禁止中斷
// 臨界區(qū)代碼
spin_unlock(lock); // 解鎖,開中斷  

spin_lock_bh(lock);// 上鎖,禁止軟中斷
// 臨界區(qū)代碼
spin_unlock_bh(lock); // 解鎖,開軟中斷

spin_lock_irq(lock); 
// 臨界區(qū)代碼
spin_unlock_irq(lock);   // 釋放鎖,并根據(jù)spin_lock_irq()保存的狀態(tài) 
             // 決定是否開中斷

與獲取自旋鎖spin_lock()和spin_lock_bh()相對應(yīng),這兩個(gè)接口實(shí)現(xiàn)了自旋鎖的釋放,并恢復(fù)中斷和軟中斷。

3.3.3 實(shí)例

要求:用自旋鎖實(shí)現(xiàn)上節(jié)中一個(gè)驅(qū)動(dòng)同一時(shí)只能被打開一次。

/*************************************************************************
	> File Name: spinlock-only.c
    > 作用:以自旋鎖做為并發(fā)控制的手段,使本驅(qū)動(dòng)同時(shí)只能被一個(gè)應(yīng)用層進(jìn)程所open
 ************************************************************************/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/spinlock.h>

/*1、定義重要的變量及結(jié)構(gòu)體*/
struct x_dev_t {
    struct cdev  my_dev;  //cdev設(shè)備描述結(jié)構(gòu)體變量
    spinlock_t lock; //自旋鎖
    int have_open;   //開啟標(biāo)志,0為已開啟,1為未開啟

};

struct x_dev_t *pcdev;

/*所有驅(qū)動(dòng)函數(shù)聲明*/
int open (struct inode *, struct file *);
int close (struct inode *, struct file *);

//驅(qū)動(dòng)操作函數(shù)結(jié)構(gòu)體,成員函數(shù)為需要實(shí)現(xiàn)的設(shè)備操作函數(shù)指針
//簡單版的模版里,只寫了open與release兩個(gè)操作函數(shù)。
struct file_operations fops={
    .open = open,
    .release = close,
};


static int __init my_init(void){
    int unsucc =0;
    dev_t devno;
    int major,minor;
    pcdev = kzalloc(sizeof(struct x_dev_t), GFP_KERNEL);
    /*2、創(chuàng)建 devno */
    unsucc = alloc_chrdev_region(&devno , 0 , 1 , "spinlock-char");
    if (unsucc){
        printk(" driver: creating devno  faild\n");
        return -1;
    }
    major = MAJOR(devno);
    minor = MINOR(devno);
    printk("driver : devno major = %d ; minor = %d;\n",major , minor);

    /*3、初始化 cdev結(jié)構(gòu)體,并將cdev結(jié)構(gòu)體與file_operations結(jié)構(gòu)體關(guān)聯(lián)起來*/
    /*這樣在內(nèi)核中就有了設(shè)備描述的結(jié)構(gòu)體cdev,以及設(shè)備操作函數(shù)的調(diào)用集合file_operations結(jié)構(gòu)體*/
    cdev_init(&pcdev->my_dev , &fops);
    pcdev->my_dev.owner = THIS_MODULE;
    /*4、注冊cdev結(jié)構(gòu)體到內(nèi)核鏈表中*/
    unsucc = cdev_add(&pcdev->my_dev,devno,1);
    if (unsucc){
        printk("driver : cdev add faild \n");
        return 1;
    }
    //初始化自旋鎖have_open 和 開啟標(biāo)志 
    spin_lock_init(&pcdev->lock);
    pcdev->have_open = 1;

    printk("driver : the driver spinlock-char initalization completed\n");


    return 0;
}


static void  __exit my_exit(void)
{
    cdev_del(&pcdev->my_dev);
    unregister_chrdev_region(pcdev->my_dev.dev , 1);
    printk("***************the driver spinlock-char  exit************\n");
}
/*5、驅(qū)動(dòng)函數(shù)的實(shí)現(xiàn)*/
/*file_operations結(jié)構(gòu)全成員函數(shù).open的具體實(shí)現(xiàn)*/
int open(struct inode *pnode , struct file *pf){
    struct x_dev_t *p = container_of(pnode->i_cdev,struct x_dev_t , my_dev);
    pf->private_data = (void *)p;
    //在open函數(shù)中對原子量have_open進(jìn)行減1并檢測。=0,允許打開文件,<0則不允許打開
    spin_lock(&p->lock);
    if (p->have_open == 1)
    {
        p->have_open = 0;
        printk("driver : spinlock-char  is opened\n");
        spin_unlock(&p->lock);
        return 0;
    }else{ //已被打開
        printk("driver : device spinlock-char Could not opend again\n");
        spin_unlock(&p->lock);
        return -1;
    }
}
/*file_operations結(jié)構(gòu)全成員函數(shù).release的具體實(shí)現(xiàn)*/
int close(struct inode *pnode , struct file *pf){
    printk("driver : spinlock-char is closed \n");
    return 0;
}
    


module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("");

3.3.4 自旋鎖的衍生與注意事項(xiàng)

【嵌入式環(huán)境下linux內(nèi)核及驅(qū)動(dòng)學(xué)習(xí)筆記-(5-驅(qū)動(dòng)的并發(fā)控制機(jī)制)】

【嵌入式環(huán)境下linux內(nèi)核及驅(qū)動(dòng)學(xué)習(xí)筆記-(5-驅(qū)動(dòng)的并發(fā)控制機(jī)制)】
【嵌入式環(huán)境下linux內(nèi)核及驅(qū)動(dòng)學(xué)習(xí)筆記-(5-驅(qū)動(dòng)的并發(fā)控制機(jī)制)】

【嵌入式環(huán)境下linux內(nèi)核及驅(qū)動(dòng)學(xué)習(xí)筆記-(5-驅(qū)動(dòng)的并發(fā)控制機(jī)制)】

3.4 、并發(fā)控制機(jī)制–信號量

信號量(Semaphore)是操作系統(tǒng)中最典型的用于同步和互斥的手段,信號量的值可以是0、1或者n。信號量與操作系統(tǒng)中的經(jīng)典概念PV操作對應(yīng)。

  • P(S): 將信號量S的值減1,即S=S-1; 如果S>=0,則該進(jìn)程繼續(xù)執(zhí)行;否則該進(jìn)程置為睡眠狀態(tài)(阻塞),進(jìn)入等待隊(duì)列。
  • V(S):將信號量S的值加1,即S=S+1; 如果S>0,喚醒隊(duì)列中阻塞而等待信號量的進(jìn)程。

基于阻塞的并發(fā)控制機(jī)制,當(dāng)要取資源時(shí),如遇資源不足,則會(huì)使本任務(wù)進(jìn)入阻塞狀態(tài)。即P操作不成功時(shí)會(huì)進(jìn)入阻塞睡眠狀態(tài)。

適用場合:只能用于任務(wù)上下文之間且臨界區(qū)執(zhí)行時(shí)間較長時(shí)的互斥或同步問題

3.4.1 函數(shù)

#include <linux/semaphore.h>

3.4.1.1 定義信號量

原碼:

#include <linux/semaphore.h>
struct semaphore {
	raw_spinlock_t		lock;
	unsigned int		count;
	struct list_head	wait_list;
};
  • lock:自旋鎖,用于保護(hù)semaphore的訪問。
  • count:資源的數(shù)量或可用數(shù)量。
  • wait_list:等待獲取semaphore的進(jìn)程鏈表。

主要接口:

  • void sema_init(struct semaphore *sem, int val); 初始化semaphore,count為val。
  • void down(struct semaphore *sem); 獲取semaphore,如果sem不可用,進(jìn)入睡眠。
  • int down_interruptible(struct semaphore *sem); 獲取semaphore,如果 sem不可用,進(jìn)入可中斷睡眠。
  • int down_trylock(struct semaphore *sem); 嘗試獲取semaphore,如果獲取不到立即返回。
  • void up(struct semaphore *sem); 釋放semaphore,喚醒等待進(jìn)程。
3.4.1.2 初始化信號量

原型:

#include <linux/semaphore.h>

static inline void sema_init(struct semaphore *sem, int val)
{
	static struct lock_class_key __key;
	*sem = (struct semaphore) __SEMAPHORE_INITIALIZER(*sem, val);
	lockdep_init_map(&sem->lock.dep_map, "semaphore->lock", &__key, 0);
}

sema_init()函數(shù)用于初始化一個(gè)semaphore結(jié)構(gòu)。

它主要做了兩件事:

  1. 使用__SEMAPHORE_INITIALIZER宏初始化semaphore的各個(gè)成員。該宏的定義為:
#define __SEMAPHORE_INITIALIZER(name, n)                \
{                                    \
    .lock        = __RAW_SPIN_LOCK_UNLOCKED(name.lock),    \
    .count        = n,                        \
    .wait_list    = LIST_HEAD_INIT((name).wait_list)        \ 
}

它會(huì)初始化:

  • lock為解鎖狀態(tài)
  • count為傳入的n值
  • wait_list為空
  1. 使用lockdep_init_map()為semaphore的lock成員指定依賴關(guān)系跟蹤信息。這個(gè)主要用于lockdep(鎖依賴檢測)機(jī)制。

所以,總結(jié)來說,sema_init()的作用是對一個(gè)semaphore結(jié)構(gòu)進(jìn)行初始化,給其一個(gè)初始的count值,并為其lock成員lockdep設(shè)置需要的信息。這樣,在后續(xù)的使用過程中,lockdep就可以根據(jù)lock的依賴關(guān)系跟蹤semaphore的加鎖順序,檢查是否存在潛在的死鎖問題。
這個(gè)函數(shù)是使用semaphore結(jié)構(gòu)的第一步,在創(chuàng)建semaphore實(shí)例后,必須首先調(diào)用sema_init()進(jìn)行必要的初始化,否則semaphore處于未定義狀態(tài),使用結(jié)果未知。

用法

struct semaphore empty, full;
sema_init(&empty, 10);    // 初始化為空的緩沖區(qū)數(shù)為10 
sema_init(&full, 0);      // 初始化為滿的緩沖區(qū)數(shù)為0
3.4.1.3 獲得信號量P
#include <linux/semaphore.h>

void down(struct semaphore *sem);
int __must_check down_interruptible(struct semaphore *sem);
int __must_check down_killable(struct semaphore *sem);
int __must_check down_trylock(struct semaphore *sem);
int __must_check down_timeout(struct semaphore *sem, long jiffies);

參數(shù):

  • sem:要獲取的semaphore結(jié)構(gòu)體指針。
  • jiffies:等待超時(shí)時(shí)間,以jiffies為單位。

返回值

  • 0:獲取成功
    -EINTR:等待過程被致命信號中斷(針對down()、down_interruptible()、down_killable())
    -EBUSY:semaphore不可用 (針對down_trylock())
    -ETIMEOUT:等待超時(shí)未能獲取semaphore (針對down_timeout())

區(qū)別:

  • void down(struct semaphore *sem); 獲得semaphore,如果不可用則睡眠,直到semaphore變?yōu)榭捎?。這是最基本的down操作,會(huì)一直阻塞進(jìn)程。
  • int down_interruptible(struct semaphore *sem); 獲得semaphore,如果不可用則進(jìn)入可中斷睡眠。如果在睡眠中被信號中斷,會(huì)返回-EINTR。這在一些需要對睡眠進(jìn)行精細(xì)控制的場景下很有用。
  • int down_killable(struct semaphore *sem); 獲得semaphore,如果不可用則進(jìn)入可殺死的睡眠。如果在睡眠中被致命信號中斷,會(huì)返回-EINTR。
  • int down_trylock(struct semaphore *sem); 嘗試獲得semaphore,如果獲得成功則返回0,否則立即返回-EBUSY。這在需要嘗試獲取但不必等待的情況下使用。
  • int down_timeout(struct semaphore *sem, long jiffies); 嘗試獲得semaphore,如果在jiffies設(shè)定的超時(shí)時(shí)間內(nèi)未獲得,則返回-ETIMEOUT。如果在此超時(shí)時(shí)間內(nèi)semaphore變?yōu)榭捎脛t返回0。這在需要等待一定時(shí)間后再使用其他手段的場景使用。
    以上接口為semaphore提供了豐富的down行為,使用它們可以實(shí)現(xiàn)對semaphore獲取過程精細(xì)化的控制,滿足各種同步場景的需要。理解這幾個(gè)接口的區(qū)別有助于根據(jù)具體需求選擇最適合的semaphore獲取方式。

int down(struct semaphore *sem);//深度睡眠

int down_interruptible(struct semaphore *sem);//淺度睡眠

一旦信號量sem的值為0時(shí),則該函數(shù)會(huì)進(jìn)入睡眠狀態(tài)。直到sem值大于0后。

3.4.1.4 釋放信號量V

void up(struct semaphore *sem)函數(shù)用于釋放一個(gè)semaphore信號量。它的作用是:

  • 如果有進(jìn)程等待該semaphore,則喚醒最先等待的進(jìn)程。
  • 如果沒有等待進(jìn)程,則semaphore的count值加1。

原型:

#include <linux/semaphoreh>


void up(struct semaphore *sem) 
{
    if (list_empty(&sem->wait_list))
        sem->count++;
    else
        __up(sem);
}

它首先檢查sem的wait_list是否為空,如果是則直接增加sem的count值。否則就通過__up()喚醒wait_list中的最先進(jìn)入睡眠的進(jìn)程。

3.4.1.5 semaphore 使有和模式

1. 生產(chǎn)者-消費(fèi)者問題:

struct semaphore empty, full;
sema_init(&empty, 10);    // 初始化為空的緩沖區(qū)數(shù)為10 
sema_init(&full, 0);      // 初始化為滿的緩沖區(qū)數(shù)為0

void producer() {
    while (1) {
        // 生產(chǎn)一個(gè)項(xiàng) 
        down(&empty);     // 獲取一個(gè)空緩沖區(qū)
        // 將項(xiàng)放入緩沖區(qū)
        up(&full);        // 發(fā)出一個(gè)滿緩沖區(qū)信號
    }
}

void consumer() {
    while (1) {
        down(&full);      // 獲取一個(gè)滿緩沖區(qū)
        // 從緩沖區(qū)中取走一項(xiàng)
        up(&empty);       // 發(fā)出一個(gè)空緩沖區(qū)信號
    } 
}

empty和full兩個(gè)semaphore控制了緩沖區(qū)的空滿狀態(tài),實(shí)現(xiàn)了生產(chǎn)者和消費(fèi)者之間的同步。

2. 讀者-寫者問題:

struct semaphore mutex, wr_sem;
sema_init(&mutex, 1);     // 互斥鎖,初始化為1
sema_init(&wr_sem, 1);    // 寫者信號量,初始化為1

void writer() {
    down(&wr_sem);        // 獲取寫者信號量
    down(&mutex);         // 獲取互斥鎖     
    // 寫操作
    up(&mutex);           // 釋放互斥鎖
    up(&wr_sem);          // 釋放寫者信號量
}

void reader() {
    down(&mutex);         // 獲取互斥鎖
    // 讀操作
    up(&mutex);           // 釋放互斥鎖
}

mutex實(shí)現(xiàn)互斥,保證同一時(shí)刻只有一個(gè)讀者或?qū)懻?。wr_sem實(shí)現(xiàn)寫者優(yōu)先,在進(jìn)行寫操作時(shí)阻塞其他讀者或?qū)懻摺?/p>

3.4.2 實(shí)例

要求:一個(gè)內(nèi)存緩沖虛擬一個(gè)設(shè)備。該設(shè)備允許應(yīng)用層多個(gè)進(jìn)程同時(shí)對該設(shè)備進(jìn)行讀寫操作。這樣就需要驅(qū)動(dòng)對并發(fā)場景進(jìn)行控制。

/*************************************************************************
	> File Name: semaphore-memory.c
 ************************************************************************/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>


/*1、定義重要的變量及結(jié)構(gòu)體*/

#define  MEM_SIZE 500

struct mem_dev_t{
    dev_t devno;
    struct cdev  my_dev;  //cdev設(shè)備描述結(jié)構(gòu)體變量
    char  mem[MEM_SIZE]; //內(nèi)存池,當(dāng)成虛擬設(shè)備
    struct semaphore sem; //信號量
};

struct mem_dev_t *mem_dev;

/*驅(qū)動(dòng)函數(shù)聲明*/

ssize_t read (struct file *, char __user *, size_t, loff_t *);
ssize_t write (struct file *, const char __user *, size_t, loff_t *);
int open (struct inode *, struct file *);
int release (struct inode *, struct file *);
//驅(qū)動(dòng)操作函數(shù)結(jié)構(gòu)體,成員函數(shù)為需要實(shí)現(xiàn)的設(shè)備操作函數(shù)指針
//簡單版的模版里,只寫了open與release兩個(gè)操作函數(shù)。
struct file_operations fops={
    .open = open,
    .release = release,
    .read = read,
    .write = write,
};

/*3、初始化 cdev結(jié)構(gòu)體,并將cdev結(jié)構(gòu)體與file_operations結(jié)構(gòu)體關(guān)聯(lián)起來*/
/*這樣在內(nèi)核中就有了設(shè)備描述的結(jié)構(gòu)體cdev,以及設(shè)備操作函數(shù)的調(diào)用集合file_operations結(jié)構(gòu)體*/
static int cdev_setup(struct mem_dev_t *mem_dev  ){
    int unsucc =0;
    cdev_init(&mem_dev->my_dev , &fops);
    mem_dev->my_dev.owner = THIS_MODULE;
    /*4、注冊cdev結(jié)構(gòu)體到內(nèi)核鏈表中*/
    unsucc = cdev_add(&mem_dev->my_dev,mem_dev->devno,1);
    if (unsucc){
        printk("cdev add faild \n");
        return -1;
    }
	/*********************************************************/
    sema_init( &mem_dev->sem,1); //初始化信號量,為1,意味著有資源mem
    /*********************************************************/
    return 0;

}

static int __init my_init(void){
    int major , minor;
    int unsucc =0;
    mem_dev = kzalloc(sizeof(struct mem_dev_t) , GFP_KERNEL);
    if (!mem_dev){
        printk(" allocating memory is  failed");
        return  -1;
    }

    /*2、創(chuàng)建 devno */
    unsucc = alloc_chrdev_region(&mem_dev->devno , 0 , 1 , "operate_memory");
    if (unsucc){
        printk(" creating devno  is failed\n");
        return -1;
    }else{

        major = MAJOR(mem_dev->devno);
        minor = MINOR(mem_dev->devno);
        printk("major = %d  ; minor = %d\n",major,minor);
    }
    /*3、 初始化cdev結(jié)構(gòu)體,并聯(lián)cdev結(jié)構(gòu)體與file_operations.*/
    /*4、注冊cdev結(jié)構(gòu)體到內(nèi)核鏈表中*/
    if (cdev_setup(mem_dev) == 0){
        printk("the driver operate_memory  initalization completed\n");
        return 0;   
    } else
        return -1;
}


static void  __exit my_exit(void)
{
    cdev_del(&mem_dev->my_dev);
    unregister_chrdev_region(mem_dev->devno , 1);
    printk("***************the driver operate_memory exit************\n");
}


/*5、驅(qū)動(dòng)函數(shù)的實(shí)現(xiàn)*/
/*file_operations結(jié)構(gòu)全成員函數(shù).open的具體實(shí)現(xiàn)*/

int open(struct inode *pnode , struct file *pf){
    pf->private_data = (void*)mem_dev;  //把全局變量指針放入到struct file結(jié)構(gòu)體里
    printk("operate_memory is opened\n");
    return 0;


}
/*file_operations結(jié)構(gòu)全成員函數(shù).release的具體實(shí)現(xiàn)*/
int release(struct inode *pnode , struct file *pf){
    printk("operate_memory is closed \n");
    return 0;
}
    

/*file_operations結(jié)構(gòu)全成員函數(shù).read的具體實(shí)現(xiàn)*/
ssize_t read (struct file * pf, char __user * buf, size_t size , loff_t * ppos){
    struct mem_dev_t *pdev = pf->private_data;
    int count = 0;
    //判斷偏移量的有效性
    if (*ppos >= MEM_SIZE){
        return 0;
    }
    //判斷能夠讀到的字節(jié)數(shù)量
    if  (size > MEM_SIZE - *ppos){
        count = MEM_SIZE - *ppos;
    }else{
        count = size;
    }

    //copy_from_user返回值大于0失敗
    /*********************************************************/
    down(&pdev->sem);   //信號量P操作,表示占用資源
    if ( copy_to_user(buf , &pdev->mem[*ppos] , count )){
        up(&pdev->sem);   //退出前釋放信號量,V操作
        return 0;
    }else{
        *ppos += count;
        up(&pdev->sem); //退出前釋放信號量,V操作
        return count;
    }
    /*********************************************************/    
}

/*file_operations結(jié)構(gòu)全成員函數(shù).write的具體實(shí)現(xiàn)*/
ssize_t write (struct file * pf, const char __user *buf, size_t size , loff_t *ppos){
    struct mem_dev_t *pdev = pf->private_data;
    int count = 0;
    //判斷偏移量的有效性
    if (*ppos >=MEM_SIZE ){
        return 0;
    }
    //判斷能夠?qū)懭氲淖止?jié)數(shù)量
    if (size > MEM_SIZE-*ppos){
        count = MEM_SIZE-*ppos;
    }else{
        count = size;
    }
    //copy_from_user返回值大于0失敗
    /*********************************************************/
    down(&pdev->sem);   //信號量P操作
    if ( copy_from_user(&pdev->mem[*ppos] , buf , count)){
        up(&pdev->sem); //退出前釋放信號量,V操作
        return 0;
    }else{
        *ppos +=count;
        up(&pdev->sem); //退出前釋放信號量,V操作
        return count;
    }
	/*********************************************************/
}

module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("");

3.5、互斥鎖

基于阻塞的互斥機(jī)制。

=適用場合:任務(wù)上下文之間且臨界區(qū)執(zhí)行時(shí)間較長時(shí)的互斥問題

3.5.1 函數(shù)

#include <linux/mutex.h>

struct mutex

原型:

struct mutex {
	/* 1: unlocked, 0: locked, negative: locked, possible waiters */
	atomic_t		count;
	spinlock_t		wait_lock;
	struct list_head	wait_list;
#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP)
	struct task_struct	*owner;
#endif
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
	void			*spin_mlock;	/* Spinner MCS lock */
#endif
#ifdef CONFIG_DEBUG_MUTEXES
	const char 		*name;
	void			*magic;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lockdep_map	dep_map;
#endif
};

參數(shù):

  • count 來表示鎖定狀態(tài),
  • wait_list保存等待的進(jìn)程,
  • ,owner記錄mutex的持有者。

主要接口:

  • void mutex_init(struct mutex *lock); 初始化mutex。
  • void mutex_lock(struct mutex *lock); 獲得mutex,如果已加鎖則睡眠。
  • int mutex_lock_interruptible(struct mutex *lock); 可中斷版本的mutex_lock。
  • int mutex_trylock(struct mutex *lock); 嘗試獲得mutex,如果未獲得立即返回。
  • void mutex_unlock(struct mutex *lock); 釋放mutex,喚醒等待進(jìn)程。
3.5.1.1 初始化

void mutex_init(struct mutex *lock)函數(shù)用于初始化一個(gè)mutex鎖。

#include <linux/mutex.h>
# define mutex_init(mutex) \
do {							\
	static struct lock_class_key __key;		\
							\
	__mutex_init((mutex), #mutex, &__key);		\
} while (0)

使用:

struct mutex  my_mutex;
mutex_init(&my_mutex);
3.5.1.2 獲取互斥體

原型:

#include <linux/mutex.h>

void mutex_lock(struct mutex *lock);

void mutex_lock(struct mutex *lock)函數(shù)用于鎖住一個(gè)mutex鎖。它的主要功能是:

  1. 通過atomic_dec_if_positive()嘗試將mutex的count值減1,如果減1成功,則獲得鎖,函數(shù)返回。
  2. 如果count值減1失敗,則表示鎖已被其他進(jìn)程持有。此時(shí),調(diào)用會(huì)被加入到wait_list等待鏈表,并睡眠。
  3. 一旦鎖被釋放,睡眠的進(jìn)程會(huì)被喚醒,再次嘗試獲取鎖。重復(fù)該流程,直到獲得鎖為止。
  4. 獲取鎖后,lock->owner會(huì)設(shè)置為當(dāng)前進(jìn)程,用于調(diào)試目的。
    mutex_lock()是獲取mutex鎖的最基本接口,它實(shí)現(xiàn)了等待已持有鎖的進(jìn)程,喚醒并重新調(diào)度等機(jī)制。理解這個(gè)接口有助于正確使用mutex實(shí)現(xiàn)同步互斥。
    使用示例:
void func() 
{
    mutex_lock(&my_mutex);   // 獲取鎖
    // 此區(qū)域互斥
    mutex_unlock(&my_mutex); // 釋放鎖
}
3.5.1.3 釋放互斥體

原型:

#include <linux/mutex.h>
void mutex_unlock(struct mutex *lock);

void mutex_unlock(struct mutex *lock)函數(shù)用于釋放一個(gè)mutex鎖。它的主要功能是:

  1. 通過atomic_inc()將mutex的count值增加1,表示釋放鎖。
  2. 如果wait_list不為空,則喚醒其中的第一個(gè)進(jìn)程。被喚醒的進(jìn)程將重新嘗試獲取鎖。
  3. mutex的owner成員被設(shè)置為NULL。這個(gè)成員僅在開啟相關(guān)配置時(shí)使用,用于調(diào)試目的。
  4. 如果mutex使用遞歸鎖定,則調(diào)用mutex_release()釋放鎖。這個(gè)函數(shù)將遞歸鎖定的count減1,如果減到0則真正釋放鎖。

使用步驟:

  1. 定義對應(yīng)類型的變量
  2. 初始化對應(yīng)變量

P/加鎖
臨界區(qū)
V/解鎖

3.5.2 范例

這里舉兩個(gè)使用mutex的例子:

  1. 保護(hù)共享資源
struct mutex my_mutex;

void shared_resource_access(void) 
{
    mutex_lock(&my_mutex);
    // 操作共享資源
    mutex_unlock(&my_mutex);
}

多個(gè)進(jìn)程通過mutex保護(hù)對共享資源的訪問,實(shí)現(xiàn)同步互斥。
2. 讀-寫鎖

struct mutex rw_mutex;

void reader(void) 
{
    mutex_lock(&rw_mutex);     // 獲取讀鎖
    // 讀操作
    mutex_unlock(&rw_mutex);   // 釋放讀鎖
}

void writer(void) 
{
    mutex_lock(&rw_mutex);     // 獲取寫鎖,阻塞其他讀者和寫者
    // 寫操作
    mutex_unlock(&rw_mutex);    // 釋放寫鎖,喚醒等待進(jìn)程 
}

讀鎖和寫鎖通過同一個(gè)mutex實(shí)現(xiàn),但優(yōu)先級不同。寫鎖的請求會(huì)導(dǎo)致其他讀鎖和寫鎖等待,實(shí)現(xiàn)寫優(yōu)先。
3. 遞歸mutex

struct mutex recursive_mutex;

void recursive_func(void)
{
    mutex_lock(&recursive_mutex);
    // 遞歸調(diào)用自身
    recursive_func(); 
    mutex_unlock(&recursive_mutex); 
}

遞歸mutex允許同一個(gè)進(jìn)程對其進(jìn)行多次加鎖。鎖需與解鎖次數(shù)匹配,否則會(huì)發(fā)生死鎖。

4、選擇并發(fā)控制機(jī)制的原則

  1. 不允許睡眠的上下文(異常上下文)需要采用忙等待類(自旋鎖,原子變量),可以睡眠的上下文可以采用阻塞類(信號量,互斥鎖)。在異常上下文中訪問的競爭資源一定采用忙等待類。

  2. 臨界區(qū)操作較長的應(yīng)用建議采用阻塞類,臨界區(qū)很短的操作建議采用忙等待類。

  3. 中斷屏蔽僅在有與中斷上下文共享資源時(shí)使用。

  4. 共享資源僅是一個(gè)簡單整型量時(shí)用原子變量文章來源地址http://www.zghlxwxcb.cn/news/detail-419840.html

到了這里,關(guān)于【嵌入式環(huán)境下linux內(nèi)核及驅(qū)動(dòng)學(xué)習(xí)筆記-(5-驅(qū)動(dòng)的并發(fā)控制機(jī)制)】的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 嵌入式Linux驅(qū)動(dòng)開發(fā) 02:將驅(qū)動(dòng)程序添加到內(nèi)核中

    嵌入式Linux驅(qū)動(dòng)開發(fā) 02:將驅(qū)動(dòng)程序添加到內(nèi)核中

    在上一篇文章 《嵌入式Linux驅(qū)動(dòng)開發(fā) 01:基礎(chǔ)開發(fā)與使用》 中我們已經(jīng)實(shí)現(xiàn)了最基礎(chǔ)的驅(qū)動(dòng)功能。在那篇文章中我們的驅(qū)動(dòng)代碼是獨(dú)立于內(nèi)核代碼存放的,并且我們的驅(qū)動(dòng)編譯后也是一個(gè)獨(dú)立的模塊。在實(shí)際使用中將驅(qū)動(dòng)代碼放在內(nèi)核代碼中,并將驅(qū)動(dòng)編譯到內(nèi)核中也是比較

    2023年04月09日
    瀏覽(51)
  • 【嵌入式Linux學(xué)習(xí)筆記】platform設(shè)備驅(qū)動(dòng)和input子系統(tǒng)

    【嵌入式Linux學(xué)習(xí)筆記】platform設(shè)備驅(qū)動(dòng)和input子系統(tǒng)

    對于Linux這種龐大的操作系統(tǒng),代碼重用性非常重要,所以需要有相關(guān)的機(jī)制來提升效率,去除重復(fù)無意義的代碼,尤其是對于驅(qū)動(dòng)程序,所以就有了platform和INPUT子系統(tǒng)這兩種工作機(jī)制。 學(xué)習(xí)視頻地址:【正點(diǎn)原子】STM32MP157開發(fā)板 platform 驅(qū)動(dòng)框架分為總線、設(shè)備和驅(qū)動(dòng)???/p>

    2024年02月07日
    瀏覽(25)
  • 【嵌入式Linux內(nèi)核驅(qū)動(dòng)】05_IIC子系統(tǒng) | 硬件原理與常見面試問題 | 應(yīng)用編程 | 內(nèi)核驅(qū)動(dòng) | 總體框架

    【嵌入式Linux內(nèi)核驅(qū)動(dòng)】05_IIC子系統(tǒng) | 硬件原理與常見面試問題 | 應(yīng)用編程 | 內(nèi)核驅(qū)動(dòng) | 總體框架

    1.1 IIC 基礎(chǔ) IIC協(xié)議簡介—學(xué)習(xí)筆記_iic標(biāo)準(zhǔn)協(xié)議_越吃越胖的黃的博客-CSDN博客 I2C(Inter-Integrated Circuit)是一種串行通信協(xié)議,用于連接微控制器、傳感器、存儲(chǔ)器和其他外設(shè)。 I2C使用兩條線(SDA和SCL)進(jìn)行通信,可以連接多個(gè)設(shè)備,每個(gè)設(shè)備都有一個(gè)唯一的地址。I2C總線上的

    2024年02月09日
    瀏覽(93)
  • 【嵌入式Linux內(nèi)核驅(qū)動(dòng)】04_Jetson nano GPIO應(yīng)用 | 驅(qū)動(dòng)開發(fā) | 官方gpiolib、設(shè)備樹與chip_driver

    【嵌入式Linux內(nèi)核驅(qū)動(dòng)】04_Jetson nano GPIO應(yīng)用 | 驅(qū)動(dòng)開發(fā) | 官方gpiolib、設(shè)備樹與chip_driver

    0.暴露給應(yīng)用層 應(yīng)用 解決調(diào)試目錄為空的問題 調(diào)試信息 1.最簡讀寫文件(在/SYS下) 設(shè)備樹 驗(yàn)證測試 編譯文件 驅(qū)動(dòng) of_get_named_gpio_flags //獲取設(shè)備樹節(jié)點(diǎn)的屬性 gpio_is_valid //判斷是否合法 devm_gpio_request //申請使用gpio,并調(diào)用設(shè)置pinctrl device_create_file //根據(jù)設(shè)備樹節(jié)點(diǎn)屬性,創(chuàng)建

    2024年02月07日
    瀏覽(53)
  • 嵌入式內(nèi)核及驅(qū)動(dòng)開發(fā)高級

    嵌入式內(nèi)核及驅(qū)動(dòng)開發(fā)高級

    僅devfs,導(dǎo)致開發(fā)不方便以及一些功能難以支持: 熱插拔 不支持一些針對所有設(shè)備的統(tǒng)一操作(如電源管理) 不能自動(dòng)mknod 用戶查看不了設(shè)備信息 設(shè)備信息硬編碼,導(dǎo)致驅(qū)動(dòng)代碼通用性差,即沒有分離設(shè)備和驅(qū)動(dòng) uevent機(jī)制:sysfs + uevent + udevd(上層app) sysfs用途:(類似于

    2024年02月16日
    瀏覽(36)
  • 嵌入式開發(fā)之linux內(nèi)核移植

    嵌入式開發(fā)之linux內(nèi)核移植

    目錄 ?前言 一、下載內(nèi)核源碼 1.1 下載linux-3.0.1 1.2 解壓源碼文件 二、 內(nèi)核添加yaffs2文件系統(tǒng)支持 2.1 下載yaffs2 2.2 內(nèi)核添加yaffs2文件補(bǔ)丁 三、配置開發(fā)板 3.1 修改機(jī)器ID 3.2 添加開發(fā)板初始化文件 3.3 配置NandFalsh 3.3.1 添加NandFlash設(shè)備 3.3.2 添加NandFlash驅(qū)動(dòng) 3.3 修改Kconfig(支持

    2024年02月07日
    瀏覽(103)
  • 嵌入式培訓(xùn)機(jī)構(gòu)四個(gè)月實(shí)訓(xùn)課程筆記(完整版)-Linux ARM驅(qū)動(dòng)編程第五天-ARM Linux編程之字符設(shè)備驅(qū)動(dòng)(物聯(lián)技術(shù)666)

    嵌入式培訓(xùn)機(jī)構(gòu)四個(gè)月實(shí)訓(xùn)課程筆記(完整版)-Linux ARM驅(qū)動(dòng)編程第五天-ARM Linux編程之字符設(shè)備驅(qū)動(dòng)(物聯(lián)技術(shù)666)

    鏈接:https://pan.baidu.com/s/1V0E9IHSoLbpiWJsncmFgdA?pwd=1688 提取碼:1688 教學(xué)內(nèi)容: 1 、內(nèi)核模塊的簡單框架: __init __exit 執(zhí)行完后就釋放空間 簡單框架:包含三個(gè)部分 1)模塊初始化和模塊退出函數(shù) 2)注冊模塊函數(shù) 3)模塊許可 //*************************************************** #include linux

    2024年02月21日
    瀏覽(21)
  • 嵌入式Linux底層系統(tǒng)開發(fā) +系統(tǒng)移植+內(nèi)核文件系統(tǒng)(基礎(chǔ))

    嵌入式Linux底層系統(tǒng)開發(fā) +系統(tǒng)移植+內(nèi)核文件系統(tǒng)(基礎(chǔ))

    搭建交叉編譯開發(fā)環(huán)境 bootloader的選擇和移植 kernel的配置、編譯、移植和調(diào)試 根文件系統(tǒng)的制作 前兩個(gè)要點(diǎn)通常芯片廠家提供。后邊兩個(gè)要點(diǎn)是公司的工作重點(diǎn)。 學(xué)習(xí)方法:先整體后局部,層層推進(jìn) 如何編譯—如何添加命令和功能—如何定義自己的開發(fā)板。 移植的基本步

    2024年02月03日
    瀏覽(101)
  • 修改嵌入式 ARM Linux 內(nèi)核映像中的文件系統(tǒng)

    修改嵌入式 ARM Linux 內(nèi)核映像中的文件系統(tǒng)

    zImage 是編譯內(nèi)核后在 arch/arm/boot 目錄下生成的一個(gè)已經(jīng)壓縮過的內(nèi)核映像。通常我們不會(huì)使用編譯生成的原始內(nèi)核映像 vmlinux ,因其體積很大。因此, zImage 是我們最常見的內(nèi)核二進(jìn)制,可以直接嵌入到固件,也可以直接使用 qemu 進(jìn)行調(diào)試。當(dāng)然,在 32 位嵌入式領(lǐng)域還能見到

    2024年02月10日
    瀏覽(35)
  • 【嵌入式Linux】編譯應(yīng)用和ko內(nèi)核模塊Makefile使用記錄

    【嵌入式Linux】編譯應(yīng)用和ko內(nèi)核模塊Makefile使用記錄

    在Makefile中,變量的賦值可以使用以下幾種方式: = :最基本的賦值符號,表示簡單的延遲展開(lazy expansion)方式。變量的值將會(huì)在使用變量的時(shí)候進(jìn)行展開。 := :立即展開(immediate expansion)的賦值方式。變量的值在賦值的時(shí)候立即展開,并且在后續(xù)的使用中不再改變。

    2024年02月08日
    瀏覽(24)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包