0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

函數(shù)的可重入與線程安全有什么關(guān)系

汽車電子技術(shù) ? 來源:宅學(xué)部落 ? 作者: 王利濤 ? 2023-02-17 09:39 ? 次閱讀

嵌入式裸機(jī)時(shí)代,也就是無OS時(shí)代,我們?cè)诼銠C(jī)環(huán)境下編寫C語言程序非常簡(jiǎn)單,實(shí)現(xiàn)一個(gè)函數(shù),然后將函數(shù)接口API提供給其它模塊調(diào)用就可以了。比如下面的函數(shù),我們實(shí)現(xiàn)一個(gè)sum函數(shù),用來求兩個(gè)數(shù)的和:

圖片

但是在一個(gè)運(yùn)行OS的多任務(wù)環(huán)境中,我們?cè)诰帉憇um函數(shù)時(shí)就要注意一些細(xì)節(jié)了:我們編寫的sum函數(shù)可能會(huì)被多個(gè)任務(wù)調(diào)用,而且可能在sum函數(shù)執(zhí)行的過程被打斷,接著在另一個(gè)任務(wù)中再次調(diào)用sum函數(shù)。而在上面的sum函數(shù)實(shí)現(xiàn)中,我們定義了一個(gè)靜態(tài)變量sum用來保存兩個(gè)數(shù)相加的臨時(shí)結(jié)果,靜態(tài)變量是保存到數(shù)據(jù)段中的,大家可以想一想,在一個(gè)任務(wù)A中正在執(zhí)行sum(1,2)函數(shù)中的第4行,此時(shí)任務(wù)被打斷掛起,接著運(yùn)行任務(wù)B,在任務(wù)B中接著執(zhí)行sum(10,20)函數(shù),執(zhí)行結(jié)束后接著運(yùn)行任務(wù)A,A獲得CPU控制權(quán)后繼續(xù)運(yùn)行sum(1,2)的第5行,此時(shí)sum(1,2)的返回結(jié)果就變成了30,而不是正確結(jié)果3。

在一個(gè)多任務(wù)環(huán)境中,如果一個(gè)函數(shù)可以重復(fù)并發(fā)調(diào)用,而且多次調(diào)用并不會(huì)影響函數(shù)的運(yùn)行結(jié)果,那么這個(gè)函數(shù)是可重入的,我們稱這個(gè)函數(shù)為:可重入函數(shù)。在上面的sum函數(shù)實(shí)現(xiàn)中,當(dāng)其被多次并發(fā)調(diào)用時(shí),函數(shù)的運(yùn)行結(jié)果并不確定,我們稱其為不可重入函數(shù)。

我們?nèi)绾稳ヅ卸ㄒ粋€(gè)函數(shù)是可重入的,還是不可重入的呢?很簡(jiǎn)單,當(dāng)一個(gè)函數(shù)滿足下面任一條件,那么這個(gè)函數(shù)就是不可重入函數(shù)。

  • 函數(shù)內(nèi)部使用了全局變量
  • 函數(shù)內(nèi)部使用了靜態(tài)局部變量
  • 函數(shù)返回值為全局變量或靜態(tài)變量
  • 函數(shù)內(nèi)部使用了malloc/free函數(shù)
  • 函數(shù)北部使用了標(biāo)準(zhǔn)I/O函數(shù)
  • 函數(shù)內(nèi)部調(diào)用其它不可重入函數(shù)

不可重入函數(shù)在一個(gè)多任務(wù)環(huán)境中不能被多次并發(fā)調(diào)用,如果一個(gè)函數(shù)可能被多次調(diào)用,那么我們?cè)O(shè)計(jì)這個(gè)函數(shù)時(shí)盡量要將其設(shè)計(jì)為可重入函數(shù)。

  • 不使用/返回靜態(tài)變量、全局變量
  • 不使用標(biāo)準(zhǔn)I/O函數(shù)
  • 不使用malloc/free函數(shù)
  • 不調(diào)用不可重入函數(shù)

在函數(shù)設(shè)計(jì)時(shí),只要注意上面的原則,那么我們就可以將一個(gè)函數(shù)設(shè)計(jì)為可重入函數(shù),可重入函數(shù)在多任務(wù)環(huán)境下可以被多次并發(fā)調(diào)用,是線程安全的,程序員可以放心大膽地調(diào)用。

理想很豐滿,現(xiàn)實(shí)很骨干。我們?cè)?a target="_blank">編程中如果說不用malloc/free、全局變量,那是不現(xiàn)實(shí)的。只要我們使用了這些全局變量,靜態(tài)變量,那么函數(shù)就變成不可重入了,在多任務(wù)環(huán)境下使用這個(gè)函數(shù)就變得線程不安全了,那怎么辦呢?

方法還是有的,一個(gè)函數(shù)之所以變得不可重入,就是因?yàn)楹瘮?shù)內(nèi)有一些資源是全局共享的,在多任務(wù)環(huán)境下多次并發(fā)調(diào)用該函數(shù)時(shí)可能會(huì)破壞掉這些共享的全局資源。我們?nèi)绻堰@些資源在訪問的時(shí)候保護(hù)起來,不讓其它任務(wù)訪問(即互斥訪問),即同一時(shí)刻只允許一個(gè)進(jìn)程訪問就安全了。這些被保護(hù)的資源我們稱為臨界資源,訪問這些臨界資源的代碼段,我們稱之為臨界區(qū)。臨界區(qū)的訪問方式為互斥訪問,即同一時(shí)刻只允許一個(gè)進(jìn)程訪問。

臨界區(qū)的實(shí)現(xiàn)方式有很多種,不同的操作系統(tǒng)可能會(huì)提供不同的實(shí)現(xiàn)方式。我們可以通過下面的操作原語來實(shí)現(xiàn)一個(gè)臨界區(qū):

  • EnterCriticalSection()
  • LeaveCriticalSection()

不同的操作系統(tǒng),具體的實(shí)現(xiàn)手段可能不一樣,常見的方法有:關(guān)中斷;實(shí)現(xiàn)互斥訪問,比如通過信號(hào)量、互斥量、自旋鎖等實(shí)現(xiàn),甚至原子操作等。比如在uc/os操作系統(tǒng)中,我們使用關(guān)中斷的方式來實(shí)現(xiàn)臨界區(qū),確保函數(shù)的線程安全。

圖片

而在linux/windows操作系統(tǒng)中,我們通常使用鎖機(jī)制來實(shí)現(xiàn)臨界區(qū):

圖片

在一個(gè)不可重入函數(shù)中,通過臨界區(qū)來實(shí)現(xiàn)共享全局資源的互斥訪問,那么在多任務(wù)環(huán)境下調(diào)用這個(gè)函數(shù)也就變得安全了,也就是說這個(gè)不可重入函數(shù)是線程安全的。

通過上面的分析,我們可以得出下面的結(jié)論:一個(gè)函數(shù)如果是可重入函數(shù),那么這個(gè)函數(shù)是線程安全的,其它進(jìn)程線程都可以對(duì)這個(gè)函數(shù)并發(fā)訪問,并不會(huì)影響函數(shù)的運(yùn)行結(jié)果。如果一個(gè)函數(shù)是不可重入函數(shù),我們通過臨界區(qū)設(shè)計(jì)對(duì)共享全局資源進(jìn)行互斥訪問,也可以讓這個(gè)函數(shù)變得線程安全,其它進(jìn)程線程也可以放心調(diào)用。由此,我們得出線程安全與可重入之間的關(guān)系如下:

圖片

也就是說,一個(gè)可重入函數(shù)肯定是線程安全的,而線程安全函數(shù)并不一定是可重入函數(shù),不可重入函數(shù)也有可能是線程安全的,比如我們常見的malloc函數(shù),就是不可重入函數(shù),但是是線程安全的,為什么呢?

通過《C語言嵌入式Linux高級(jí)編程》課程學(xué)習(xí),我們已經(jīng)知道,對(duì)于我們使用malloc/free申請(qǐng)釋放的內(nèi)存,glibc在用戶空間實(shí)現(xiàn)了一個(gè)內(nèi)存管理器,將各個(gè)大小的內(nèi)存塊鏈成多個(gè)全局鏈表進(jìn)行管理。

圖片

當(dāng)我們使用malloc/free申請(qǐng)釋放內(nèi)存時(shí),如果申請(qǐng)/釋放的內(nèi)存塊大小符合規(guī)定,一般都是直接對(duì)這些全局鏈表進(jìn)行操作、避免多次系統(tǒng)調(diào)用進(jìn)入內(nèi)核態(tài),減少系統(tǒng)開銷。因?yàn)閙alloc/free函數(shù)對(duì)全局鏈表進(jìn)行了操作,所以malloc/free是不可重入函數(shù)。在訪問這些全局鏈表時(shí),我們需要通過鎖機(jī)制加以保護(hù),每次malloc/free操作全局鏈表時(shí),其它地方就被互斥訪問了,只有當(dāng)malloc/free操作全局鏈表完成退出,其它地方的malloc/free才能對(duì)這個(gè)全局鏈表進(jìn)行訪問。

通過上面的分析,我們可以看到:malloc/free雖然是不可重入函數(shù),但是通過加鎖對(duì)共享全局資源的互斥訪問,也就變得線程安全了,在多任務(wù)環(huán)境下,每個(gè)進(jìn)程都可以放心大膽地調(diào)用它:因?yàn)閙alloc雖然是不可重入函數(shù),但它是線程安全的。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 嵌入式
    +關(guān)注

    關(guān)注

    5052

    文章

    18911

    瀏覽量

    300768
  • C語言
    +關(guān)注

    關(guān)注

    180

    文章

    7581

    瀏覽量

    135571
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4259

    瀏覽量

    62228
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Keil C51處理科重入函數(shù)問題的探討

    在程序設(shè)計(jì)中,變量具體可以分為四種類型: 全局變量 ,靜態(tài)全局變量,局部變量,靜態(tài)局部變量。這幾種變量類型對(duì)函數(shù)重入產(chǎn)生的重大的影響,因?yàn)椴煌木幾g器采用不同的策略。 針對(duì)51的存儲(chǔ)區(qū)有
    發(fā)表于 04-22 21:40

    調(diào)用非安全線程的dll的問題

    在調(diào)用非線程安全的dll時(shí),是不是要選擇在UI線程中運(yùn)行?是不是還必須用不可重入的子VI封裝一下?上述的兩步是不是都要做?這些問題不是很清楚,還請(qǐng)各位大神指點(diǎn)一下
    發(fā)表于 03-14 21:13

    我想問如果我異步調(diào)用重入 參數(shù)是X80會(huì)怎么樣

    這個(gè)問題 因?yàn)槲腋杏X里面有的地方很怪異,我在這些線程里用了全局變量 存了這個(gè)重入的VI的停止事件引用在別的地方去調(diào)用 停止它 就有時(shí)候 感覺并不能停止掉 關(guān)閉工程文件時(shí)候說是有線程
    發(fā)表于 06-06 19:38

    用ERTM關(guān)閉全局中斷來實(shí)現(xiàn)函數(shù)重入性有什么附加影響?

    在編程中,用ERTM關(guān)閉全局中斷來實(shí)現(xiàn)函數(shù)重入性有什么附加影響?
    發(fā)表于 08-09 11:12

    重入函數(shù)相關(guān)資料推薦

    數(shù)碼管點(diǎn)亮?xí)r間約為1~2ms。在數(shù)碼管數(shù)字變化時(shí),先熄滅再更新數(shù)據(jù),稱為消隱。using 0 是第0組寄存器;reentrant聲明的函數(shù)重入函數(shù)。
    發(fā)表于 01-11 07:37

    函數(shù)對(duì)FFT有什么影響?他們是什么關(guān)系

    函數(shù)對(duì)FFT有什么影響?他們是什么關(guān)系?在visualStudio軟建中,要對(duì)音頻信號(hào)進(jìn)行FFT變換時(shí),需要加窗函數(shù)進(jìn)行控制,這是為什么?窗函數(shù)對(duì)FFT有什么影響?窗
    發(fā)表于 11-30 06:24

    Linux 多線程重入函數(shù)

    的相互影響,如果一個(gè)函數(shù)在多線程并發(fā)的環(huán)境中每次被調(diào)用產(chǎn)生的結(jié)果是不確定的,我們就說這個(gè)函數(shù)是"不可重入的"/"線程
    發(fā)表于 05-16 17:41 ?911次閱讀

    重入函數(shù)與不可重入函數(shù)分析

    導(dǎo)致不可預(yù)料的后果。那么什么是重入函數(shù)呢?所謂重入函數(shù)是指一個(gè)可以被多個(gè)任務(wù)調(diào)用的過程,任務(wù)
    發(fā)表于 04-02 14:43 ?771次閱讀

    51單片機(jī)的重入函數(shù)有什么陷阱

    函數(shù)一旦定義為重入, 參數(shù)就會(huì)通過堆棧傳遞。 不要忘記的是, 局部變量也會(huì)在堆棧上分配。 更不能忽略的是, 51的堆棧空間大小是在2^8以內(nèi)的, 所以堅(jiān)決不能在
    發(fā)表于 08-20 17:31 ?0次下載
    51單片機(jī)的<b class='flag-5'>可</b><b class='flag-5'>重入</b><b class='flag-5'>函數(shù)</b>有什么陷阱

    重入和不可重入函數(shù)的詳細(xì)資料和應(yīng)用簡(jiǎn)介

    重入一般可以理解為一個(gè)函數(shù)在同時(shí)多次調(diào)用,例如操作系統(tǒng)在進(jìn)程調(diào)度過程中,或者單片機(jī)、處理器等的中斷的時(shí)候會(huì)發(fā)生重入的現(xiàn)象。一般浮點(diǎn)運(yùn)算都是由專門的硬件來完成,舉個(gè)例子假設(shè)有個(gè)硬件寄存器名字叫做FLOAT,用來計(jì)算和存放浮點(diǎn)數(shù)的中
    發(fā)表于 08-02 17:34 ?0次下載
    <b class='flag-5'>可</b><b class='flag-5'>重入</b>和不可<b class='flag-5'>重入</b><b class='flag-5'>函數(shù)</b>的詳細(xì)資料和應(yīng)用簡(jiǎn)介

    KEIL C51的重入函數(shù)的詳細(xì)資料講解

    重入函數(shù),又叫再入函數(shù),是一種可以在函數(shù)體內(nèi)不直接或間接調(diào)用其自身的一種函數(shù)。再入函數(shù)可被遞歸調(diào)
    發(fā)表于 08-01 17:35 ?0次下載
    KEIL C51的<b class='flag-5'>重入</b><b class='flag-5'>函數(shù)</b>的詳細(xì)資料講解

    Linux中的重入、異步信號(hào)安全線程安全

    下文是在看csapp的時(shí)候引發(fā)的一些思考,其實(shí)之前看anup的時(shí)候也有所了解,不過時(shí)間有點(diǎn)長(zhǎng)了,所以有點(diǎn)忘記了,當(dāng)再次在csapp看到這部分內(nèi)容的時(shí)候有了更多的理解。 重入函數(shù) 當(dāng)一個(gè)被捕獲的信號(hào)
    的頭像 發(fā)表于 11-10 14:45 ?1299次閱讀
    Linux中的<b class='flag-5'>可</b><b class='flag-5'>重入</b>、異步信號(hào)<b class='flag-5'>安全</b>和<b class='flag-5'>線程</b><b class='flag-5'>安全</b>

    為什么中斷處理函數(shù)不能直接調(diào)用不可重入函數(shù)

    中斷丟失和系統(tǒng)位置錯(cuò)誤,這里直接導(dǎo)致嵌入式 linux 系統(tǒng)應(yīng)用進(jìn)程中的所有線程停掉,進(jìn)而導(dǎo)致看門狗進(jìn)程得不到喂狗,設(shè)備重啟。 那什么是不可重入函數(shù)呢? 為什么中斷處理函數(shù)不能直接調(diào)用
    的頭像 發(fā)表于 02-17 09:33 ?5731次閱讀

    &quot;重入&quot;和&quot;線程安全&quot;是兩個(gè)概念 千萬不要搞混了

    ? ? 今天的這篇文章應(yīng)該可以幫助你解決一大部分問題。 ? 01 兩個(gè)概念 1、重入函數(shù)?? 多任務(wù)系統(tǒng)中每個(gè)進(jìn)程或線程都是由多種執(zhí)行流并發(fā)運(yùn)行的,當(dāng)執(zhí)行流同時(shí)進(jìn)入同一個(gè)
    的頭像 發(fā)表于 02-10 17:38 ?3207次閱讀
    &quot;<b class='flag-5'>可</b><b class='flag-5'>重入</b>&quot;和&quot;<b class='flag-5'>線程</b><b class='flag-5'>安全</b>&quot;是兩個(gè)概念 千萬不要搞混了

    CPU的核心數(shù)和線程數(shù)有什么關(guān)系

    1 概念 1.1 背景 當(dāng)看到以下一些名詞,你是否感到過疑惑:他們之間到底有什么關(guān)系? CPU核心數(shù)、線程數(shù)、處理器數(shù)量、每個(gè)處理器的內(nèi)核數(shù)量、處理器內(nèi)核總數(shù)、邏輯核數(shù)… 在安裝linux虛擬機(jī)
    的頭像 發(fā)表于 11-24 16:22 ?5501次閱讀
    CPU的核心數(shù)和<b class='flag-5'>線程</b>數(shù)有<b class='flag-5'>什么關(guān)系</b>