Linux的共享內存與自旋鎖

2020-12-11 閒聊代碼

Linux的共享內存(shared memory)是進程間通信的一種機制之一,用於在多個進程之間共享數據。

共享的數據屬於多個進程的共同資源,類似於多線程的數據一樣,也要使用鎖來保護。

線程是使用pthread_mutex來保護,進程可以使用fcntl()對共享內存的文件描述符加鎖,也可以把共享內存的前4位元組當作自旋鎖spinlock來使用。

自旋鎖,可以像之前的文章提到的,以xchg指令為基礎來實現。

「上古風格」的shmget()系列的共享內存API比較麻煩,已經幾乎不再使用了。

現在,共享內存被實現為內核的tmpfs文件系統,可以像一般的文件一樣使用,貫徹了「unix一切皆是文件」的理念。

1,打開共享內存使用的API是shm_open(),它與打開普通文件的open()函數類似,參數和返回值的意義也差不多。

這是它的man手冊,連接時需要加-lrt。

第一個參數為共享內存的名字字符串,類似open的文件名。

第二個參數是打開時的標誌,O_RDWR表示可讀寫,O_CREAT表示如果不存在則創建,O_TRUNC表示如果存在則把大小截斷為0,也與open函數的這個參數含義一樣,一般是聯合使用這三個。

第三個參數是mode,表示Linux的權限,即讀、寫、執行,為3個八進位數字,一般寫為0666,即所有用戶都可讀寫,不可執行。

它的返回值,就是共享內存對應的文件描述符fd,-1表示出錯,可查看errno分析出錯原因。

2,獲得了共享內存的文件描述符fd之後,要用ftruncate()函數把它設置到需要的大小,第一個參數是fd文件描述符,第二個參數是字節大小。

3,最後一步是把共享內存用mmap()函數映射到當前進程裡,獲得一個共享內存的指針,就可以像正常的內存一樣讀寫它了。

區別就是它是被其他進程共享的,而正常的內存是歸所屬進程獨佔的。

自旋鎖

上圖屬於簡單的用戶態自旋鎖spinlock的實現,以xchg指令為基礎,0表示沒加鎖,1表示加鎖,返回0表示獲取鎖成功,返回1表示獲取鎖失敗,配合著sched_yield()進程調度函數使用。

下圖main()函數的前半部分,是共享內存的使用,跟前面提到的一樣分3步。

這裡我們定義了一個shm_t的結構來管理共享內存,它包含一個作為自旋鎖的volatile int lock變量,用於在多進程並發環境裡保護共享內存的數據。

在寫shm_t的其他數據之前要先獲取鎖。

其他數據就是一個int count作測試用,加volatile是防止編譯器優化。

畢竟這一項是共享數據,有可能被其他進程修改,volatile可以要求編譯器必須去讀內存,而不是讀寄存器保存的上次使用的值。

共享內存

mmap()函數的參數較多,可以具體看man手冊,但常用的方式就是這裡寫的這樣。

第二項為要mmap的字節數,最後一項為要mmap的偏移量,倒第二項為要mmap的文件描述符fd。

可見,共享內存對象也是被當作文件來用的。

PROT_READ表示mmap的內存可讀,PROT_WRITE表示可寫,與打開時的權限選擇是一致的,MAP_SHARED表示以共享方式映射,多個進程對這個內存的讀寫是互相影響的。

fork一個子進程測試一下,100萬次++之後看看shm->count是200萬嗎。

如果不加鎖的情況下,因為++屬於「讀-更新-寫」運算,是存在同步問題的,不會是200萬。

賦值=是原子操作,所以解鎖只需要*lock = 0就行。

++運算也可以寫成原子操作:

asm volatile(「lock; inc %0」

: 「=r」(*p)::);

符號、引號「」都是英文的。

這樣就不需要加鎖了,intel允許在一條指令前面加個lock;表示下一條指令執行時要鎖定內存總線,禁止其他CPU訪問內存,從而讓lock的下一條指令成為原子操作。

在只需要做簡單運算時,原子操作的粒度比鎖更小,可以讓代碼儘快離開互斥區,提高代碼效率。

nginx保護監聽socket,避免epoll驚群的那個鎖,也是優先使用共享內存來實現。在共享內存不被支持時,才使用文件鎖。

相關焦點

  • Linux 自旋鎖spinlock,教你如何把ubuntu弄死鎖
    自旋鎖的使用在linux kernel的實現中,經常會遇到這樣的場景:共享數據被中斷上下文和進程上下文訪問,該如何保護呢?二、再考慮下面的場景(中斷上下文場景):運行在CPU0上的進程A在某個系統調用過程中訪問了共享資源 R運行在CPU1上的進程B在某個系統調用過程中也訪問了共享資源 R外設P的中斷handler中也會訪問共享資源
  • Linux內核同步機制的自旋鎖原理及綜合應用實例
    一、自旋鎖本文引用地址:http://www.eepw.com.cn/article/149309.htm自旋鎖鎖是專為防止多處理器並發而引入的一種鎖,它在內核中大量應用於中斷處理等部分(對於單處理器來說,防止中斷處理中的並發可簡單採用關閉中斷的方式,即在標誌寄存器中關閉/打開中斷標誌位,不需要自旋鎖)。
  • 樂觀鎖&悲觀鎖&自旋鎖
    第二它可以避免在退出循環的時候因內存順序衝突(memory order violation)而引起CPU流水線被清空(CPU pipeline flush),從而提高CPU的執行效率。2.只能保證一個共享變量原子操作;CAS 只對單個共享變量有效,當操作涉及跨多個共享變量時 CAS 無效。
  • 帶您進入內核開發的大門 | 自旋鎖的使用
    大家都知道,在CPU和內存之間包含一級緩存、二級緩存和三級緩存等緩存。原因很簡單,因為CPU訪問緩存的速度更快,將將經常訪問的數據加載到緩存中,可以減少內存訪問的頻率,從而提高計算的性能。CPU、緩存和內存的關係如下圖所示(簡圖,省略了好多內容):
  • 改善Linux內核實時性方法的研究與實現
    實時化後的Linux中,自旋鎖被互斥鎖取代,而中斷處理代碼中大量運用了自旋鎖,中斷處理代碼就有可能因為得不到鎖而需要被掛到該鎖的等待隊列上去。但是只有可調度的進程才可這麼做,如果中斷處理代碼仍然使用原來的自旋鎖,那麼互斥鎖取代自旋鎖改進內核實時性的努力將大打折扣。  線程化的中斷管理可以有效的解決這些問題。
  • 嵌入式Linux內存管理的一些知識點總結
    這個內存管理的知識點還真的需要我們專門的去理解一下,今天大家一起來學習學習嵌入式Linux內存管理的知識。1.不涉及linux內核的彙編知識,僅C語言層面解析1.回答:彙編主要處理的是寄存器地址(包括內容)的計算,進行一部分的地址轉換工作(當然,它是重要的);C語言處理了極大部分的系統內存管理工作。
  • Linux下找出吃內存的方法總結
    linux下查詢進程佔用的內存方法總結,假設現在有一個「php-cgi」的進程 ,進程id為「25282」。
  • Linux如何調試內存洩漏
    內存洩漏是指由於疏忽或錯誤造成程序未能釋放已經不再使用的內存
  • Linux作業系統的簡介
    Linux也是這麼分的,而且對文件、設備、網絡、共享內存等都是以文件的形式管理的,一切都是文件。物理內存,就是個大的一維數組,數組的每個元素對應一個物理內存頁,數據結構大概這樣:struct physical_memory{spinlock_t lock; //保護這個頁的自旋鎖volatile
  • Linux內存、Swap、Cache、Buffer
    在Linux系統下,我們一般不需要去釋放內存,因為系統已經將內存管理的很好,但是凡事也有例外。    used:已經使用的內存大小(這裡面包含cached和buffers和shared部分)。  free:空閒的內存大小。  shared:進程間共享內存(一般不會用,可以忽略)。  buffers:內存中寫完的東西緩存起來,這樣快速響應請求,後面數據再定期刷到磁碟上。
  • 源碼時代java小課堂之線程鎖之自旋鎖
    java中定義了非常多的鎖,很多同學面試對於鎖,感覺非常茫然,於是源碼的老師決定,將這些鎖拆分開來注意分析講解,這篇我們先聊聊自旋鎖1.自旋鎖是基於CAS實現2. synchronized重量級鎖是基於系統內核3.
  • Linux kernel 同步機制(下篇)
    讀寫鎖實際是一種特殊的自旋鎖,它把對共享資源的訪問者劃分成讀者和寫者,讀者只對共享資源進行讀訪問,寫者則需要對共享資源進行寫操作。這種鎖相對於自旋鎖而言,能提高並發性,因為在多處理器系統中,它允許同時有多個讀者來訪問共享資源,最大可能的讀者數為實際的邏輯CPU數。寫者是排他性的,一個讀寫鎖同時只能有一個寫者或多個讀者(與CPU數相關),但不能同時既有讀者又有寫者。
  • 郭健:Linux內存管理系統參數配置之OOM(內存耗盡)
    按照慣例,最後一章是參考文獻,本文的參考文獻都是來自linux內核的Documentation目錄,該目錄下有大量的文檔可以參考,每一篇都值得細細品味。二、什麼是OOMOOM就是out of memory的縮寫,雖然linux kernel有很多的內存管理技巧(從cache中回收、swap out等)來滿足各種應用空間的vm內存需求,但是,當你的系統配置不合理,讓一匹小馬拉大車的時候,linux kernel會運行非常緩慢並且在某個時間點分配page frame的時候遇到內存耗盡、無法分配的狀況
  • 系統內存/進程內存知識掃盲
    的,需要觀察linux的內存使用狀態,首先需要判斷整體系統的內存情況,然後是進程級別的內存情況,你真的搞清楚這裡面的方法了嘛?1、核心概念耳熟能詳的字眼有虛擬內存、共享內存、物理內存,這裡簡要說明,更多複雜的linux內存管理機制,有興趣的同學可以自己深入了解,說實話我也沒有很深入學習。物理內存:就是系統硬體提供的內存大小,是真正的內存,一般叫做內存條,是與CPU直接交換數據的內部存儲器,也叫主存(內存)。
  • Java 中15種鎖的介紹:公平鎖,可重入鎖,獨享鎖,互斥鎖,樂觀鎖,分段鎖,自旋鎖等等
    獨享鎖 / 共享鎖獨享鎖和共享鎖在你去讀C.U.T包下的ReeReentrantLock和ReentrantReadWriteLock你就會發現,它倆一個是獨享一個是共享鎖。獨享鎖:該鎖每一次只能被一個線程所持有。
  • 每個程式設計師都該了解一點 Linux 內存管理知識
    在內存極度緊張的時候, sshd 這種不怎麼佔內存的進程也無法倖免。覆巢之下,焉有完卵。OOM Killer 的全稱叫 Out Of Memory Killer,今天簡單給大家介紹下,它是 linux 內核的內存保護機制。當應用程式大量請求內存導致內存不足的時候,通常會觸發 OOM Killer,OOM Killer 會根據相對簡單粗暴的算法殺掉某個進程以解燃眉之急。
  • linux基礎應用(NFS文件共享)
    NFS客戶端mount掛載NFS伺服器端共享的文件目錄到NFS客戶端本地系統的某一個掛載點下,NFS客戶端本地訪問掛載點的共享目錄時,像是訪問本地的磁碟分區或目錄一樣,實際上訪問的是掛載的NFS伺服器目錄。
  • Ai聘網JAVA面試之深入理解自旋鎖
    什麼是自旋鎖?自旋鎖(AutoGyrationLock):是指當一個線程在獲取鎖的時候,如果鎖已經被其它線程獲取,那麼該線程將循環等待,然後不斷的判斷鎖是否能夠被成功獲取,直到獲取到鎖才會退出循環。它是為實現保護共享資源而提出一種鎖機制。其實,自旋鎖與互斥鎖比較類似,它們都是為了解決對某項資源的互斥使用。無論是互斥鎖,還是自旋鎖,在任何時刻,最多只能有一個保持者,也就說,在任何時刻最多只能有一個執行單元獲得鎖。但是兩者在調度機制上略有不同。對於互斥鎖,如果資源已經被佔用,資源申請者只能進入睡眠狀態。
  • 如何理解互斥鎖、條件變量、讀寫鎖以及自旋鎖?
    其思想簡單粗暴,多線程共享一個互斥量,然後線程之間去競爭。得到鎖的線程可以進入臨界區執行代碼。讀寫鎖有一個別稱叫『共享-獨佔鎖』。不過單看『共享-獨佔鎖』或者『讀寫鎖』這兩個名稱,其實並未區分對於讀和寫,到底誰共享,誰獨佔。可能會讓人誤以為讀寫鎖是一種更為泛化的稱呼,其實不是。讀寫鎖的含義是準確的:是一種 讀共享,寫獨佔的鎖。讀寫鎖的特性:因而適用於多讀少寫的場景。
  • Linux 下的進程間通信:共享存儲 | Linux 中國
    例如,默認情況下,POSIX API 用內存映射文件來實現共享內存:對於一個共享的內存段,系統為相應的內容維護一個備份文件。在 POSIX 規範下共享內存可以被配置為不需要備份文件,但這可能會影響可移植性。我的例子中使用的是帶有備份文件的 POSIX API,這既結合了內存獲取的速度優勢,又獲得了文件存儲的持久性。