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

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

內核mmap_sem鎖的危害和相關優(yōu)化

Linux閱碼場 ? 來源:Linux閱碼場 ? 2023-02-07 16:01 ? 次閱讀

mmap_sem鎖簡介

mmap_sem鎖是進程為了保護自身虛擬地址空間不受多線程并發(fā)訪問影響而設計的。

多線程環(huán)境下,如果想訪問進程的虛擬地址空間(比如find_vma等),是要先持有該mmap_sem鎖才能訪問的,這樣可以避免多線程并發(fā)修改進程vma區(qū)域造成的沖突。

mmap_sem鎖的一些問題總結

內核mmap_sem鎖設計目前存在一些問題,簡單總結如下:

1:保護的東西太多,范圍太廣了。

mmap_sem目前保護:

1)Rbtree of VMA,比如做find_vma()時

arm64系統(tǒng)上,由于虛擬地址空間增大,進程的vma數(shù)量會特別多,每個vma操作幾乎都要首先獲取一把這樣的mmap_sem大鎖。

這樣會造成鎖的粒度太大,鎖整個進程的vma地址空間的。

2)VMA list,會Lock the whole address space for even touching one byte

3) VMA flags, 會Need hold write lock to update vm_flags

4)Most of the fields of the mm_struct are protected using the mm.mmap_sem

fields can be arg_start, arg_end, env_start, env_end等。

2: 內核會頻繁做page fault, 這樣會頻繁獲取mmap_sem鎖。

soft page fault的描述:

Page faults can be quite expensive, especially those which must be resolved by reading data from disk. On a typical system, though, there are a lot of page faults which do not require I/O. A page fault happens because a specific process does not have a valid page table entry for the needed page, but that page might already be in the page cache, in which case handling the fault is just a matter of fixing the page table entry and increasing the page's reference count; this happens often with shared pages or those brought in via the readahead mechanism. Faults for new anonymous pages (application data and stack space, mostly), instead, can be handled through the allocation of a zero-filled page. In either case, the fault is quickly taken care of with no recourse to backing store required.

In many workloads, this kind of "soft" fault happens much more often than hard faults requiring actual I/O. So it's important that they be executed quickly. Various developers had concluded that the kernel was, in fact, not handling this kind of fault quickly enough, and they identified the use of the mmap_sem reader/writer semaphore as the core of the problem.

Contention wasn't the issue in this case - only a reader lock is required for page fault handling - but the cache line bouncing caused by continual acquisition of the lock was killing performance. As the number of cores in systems increases, this kind of problem can get worse.

內核里面這樣的soft page fault會發(fā)生很多,勢必造成mmap_sem獲取很頻繁,引起多核cache顛簸,對多核程序性能也不好。

所以Linux kernel很多地方架構設計不怎么匹配如今的多核cpu架構。

3: 一旦有個寫請求在排隊了,該mmap_sem就會變成互斥意義上的鎖了。

mmap_sem這種讀寫鎖是有好處,可以實現(xiàn)一些并發(fā)的多線程讀訪問。

但是它的這種并發(fā)讀訪問是有條件的:

如果一個讀寫信號量當前沒有被寫者擁有并且也沒有寫者等待讀者釋放信號量,那么任何讀者都可以成功獲得該讀寫信號量,否則,讀者必須被掛起直到寫者釋放該信號量。

如果一個讀寫信號量當前沒有被讀者或寫者擁有并且也沒有寫者等待該信號量,那么一個寫者可以成功獲得該讀寫信號量,否則寫者將被掛起,直到?jīng)]有任何訪問者。

簡單來看個問題場景:

線程1以讀者身份持mmap_sem,然后該線程由于某種原因sleep了。

下來線程2以寫者身份請求持該mmap_sem鎖,因為該鎖已經(jīng)被線程1持有,所以失敗就開始排隊。

再下來該鎖就變成互斥鎖了,再來的read請求(對應線程2,3...)就都得排隊了,不能發(fā)揮讀寫鎖并發(fā)讀的優(yōu)勢了。

結論:

所以綜合上面3個問題特點,在多核,多線程并發(fā)環(huán)境下(比如安卓系統(tǒng)),勢必造成mmap_sem鎖競爭激烈,程序性能不好。

mmap_sem鎖在產品開發(fā)中的優(yōu)化總結

優(yōu)化方向1:方便快捷地找到持鎖線程。

目前很多方法是通過在出現(xiàn)問題時,人為地讓內核崩潰,然后再用crash工具分析內核內存dump鏡像,從而在一大堆等鎖和持鎖線程中,找到

導致問題出現(xiàn)的持鎖線程信息。一旦找到持鎖線程,就明白問題出現(xiàn)的root casue, 就會有優(yōu)化方案了。

但這種crash工具分析方法還是有點笨重,當然由于mmap_sem鎖競爭導致的內核崩潰用這種方法是最好最對口的。

但是很多時候出問題是內核并未崩潰,只是android上層發(fā)生watchdog超時重啟,或者只是某進程工作timeout。

如果是mmap_sem競爭導致的這些問題發(fā)生可以嘗試用些簡單快捷的方法找到持鎖線程。

1) 可以在安卓fwk層發(fā)生watchdog超時時,打印下system server進程中每個線程的內核?;厮菪畔?。

這樣如果對內核代碼熟悉的話,會知道哪些地方會長時間持有mmap_sem鎖,這樣看下?;厮菪畔ⅲ蟾拍懿鲁鰜砟男┚€程在持鎖或者等鎖。

如果信息還不夠,還可以通過sysrq,打印出系統(tǒng)此時所有處于D狀態(tài)和sleep狀態(tài)的線程內核?;厮菪畔?。

因為有持鎖等鎖導致的內核性能問題,基本上都出現(xiàn)在D和sleep狀態(tài)的線程里面,通過對這些信息分析,也可以大概猜出來可能的持鎖者。

2) 如果問題可以復現(xiàn),可以用一些bcc工具找到mmap_sem的持鎖owner信息。

1>輸出持鎖的owner信息

sudo ./trace 'rwsem_down_read_slowpath(struct rw_semaphore *sem, int state) "count=0x%lx owner=%s", sem->count.counter, ((struct task_struct *)((sem->owner.counter)&~0x7))->comm'
/virtual/main.c:44:66: warning: comparison of array '((struct task_struct *)((sem->owner.counter) & ~7))->comm' not equal to a null pointer is
always true [-Wtautological-pointer-compare]
if (((struct task_struct *)((sem->owner.counter)&~0x7))->comm != 0) {
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~ ~
1 warning generated.
PID TID COMM FUNC -
10127 10127 sync rwsem_down_read_slowpath count=0x103 owner=modprobe //是modprobe進程持有該rwsem鎖。

2> 輸出持鎖owner的其他信息

sudo ./trace 'rwsem_down_read_slowpath(struct rw_semaphore *sem, int state) "count=0x%lx fs name=%s", sem->count.counter, (((struct super_block*)((void *)sem-(void*)(&(((struct super_block*)0)->s_umount))))->s_id)'
PID TID COMM FUNC -
10144 10144 sync rwsem_down_read_slowpath count=0x103 fs name=nfsd

從上面可以找到持鎖owner:modprobe進程此時正在掛載 nfsd 模塊。

優(yōu)化方向2:核心路徑中避免對/proc目錄下每進程子目錄做遍歷訪問,規(guī)避mmap_sem導致的問題。

之前碰到一個問題是由于system_server進程發(fā)生watchdog超時導致安卓fwk層重啟。

watchdog超時原因是:

system_server進程一個核心路徑上代碼做了遍歷/proc/每進程/cmdline工作,正常情況下做該工作不會出現(xiàn)問題。

但是由于做該工作,需要down_read獲取每進程的mmap_sem鎖,只要有一個進程的該鎖已經(jīng)被寫者身份持有了,那么再獲取該鎖時,就得等待了。

所以異常就發(fā)生在這個地方,所以該異常會導致該核心路徑中遍歷每進程cmdline工作耗時了,核心路徑一旦性能受影響,就會導致問題出現(xiàn)。

優(yōu)化方法:

避免在核心路徑上做這種潛在的耗時工作:遍歷每進程狀態(tài)。改用其他方法去達到目的。

優(yōu)化方向3:同步社區(qū)一些patch,避免出現(xiàn)因mmap_sem競爭導致的cgroup優(yōu)先級反轉問題。

cgroup v2中容易出現(xiàn)這種優(yōu)先級反轉問題:

一個高優(yōu)先級group里面某個進程A正在做遍歷訪問/proc/每進程下面狀態(tài)信息的工作,所以需要獲取系統(tǒng)中每進程的mmap_sem鎖。

另外一個低優(yōu)先級group里面,某進程B中有些線程在做耗時長的io操作(進入內核filemap_fault函數(shù)里面做的),操作前提前以寫身份獲取了mmap_sem鎖。

所以寫身份先獲取了這把鎖,那么進程A如果想通過訪問獲取進程B的狀態(tài)信息時,就會阻塞在等待進程B的mmap_sem這個地方。

這樣因為進程B是在低優(yōu)先級group里面,io訪問也比高優(yōu)先級group慢,但是此時卻阻塞住了高優(yōu)先級group里面的進程。

優(yōu)化方法:

同步社區(qū)該patch

[RFC][PATCH 0/9][V2] drop the mmap_sem when doing IO in the fault path

優(yōu)化方向4: 其他的一些優(yōu)化持鎖線程的工作負載方法。

前面提了,解決因mmap_sem競爭導致的問題,關鍵是找到持鎖線程信息。找到后,還需要優(yōu)化該持鎖線程在持鎖后的工作負載。

只有保證持鎖過程中,工作時間越短,就越能降低性能問題出現(xiàn)的概率。

1: 比如之前還碰到過一個問題:

某業(yè)務進程包含若干個工作線程和一個數(shù)據(jù)加載線程。數(shù)據(jù)加載線程需要將工作線程不再使用的上一份數(shù)據(jù)釋放掉,具體需要做munmap 大塊內存 (20G+)工作 ,

結果在釋放數(shù)據(jù)過程中,工作線程的性能受到了影響。

2: 這類問題原因也是:

釋放數(shù)據(jù)時,需要做munmap工作,進一步需要以寫著身份拿工作線程的mmap_sem鎖。這樣會導致工作線程獲取自身mmap_sem的等待時間變長。

通過問題進一步分析,發(fā)現(xiàn)munmap中最耗時的是free_pgtables,做這個也需要寫者拿mmap_sem鎖。

網(wǎng)絡上一些好的建議是:實現(xiàn)分段munmap,或者Drop mmap_sem during unmapping large map。

3: 其實還有個好的優(yōu)化方法:

不考慮mmap_sem的影響,munmap工作本身就會比較耗時,所以后來有了madvise MADV_FREE和MADV_DONTNEED的優(yōu)化。

所以這個地方可以嘗試用madvise來代替munmap,會縮短釋放數(shù)據(jù)的時間的。

優(yōu)化方向5: Speculative page faults

前面那些優(yōu)化方向都是正面回避該mmap_sem自身的一些特性問題(詳見上面第二大節(jié)的總結),從側面,從程序自身業(yè)務著手去優(yōu)化解決問題。

這個投機性缺頁異常則是嘗試從正面優(yōu)化該mmap_sem問題。

審核編輯:湯梓紅
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權轉載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 內核
    +關注

    關注

    3

    文章

    1336

    瀏覽量

    40084
  • Linux
    +關注

    關注

    87

    文章

    11123

    瀏覽量

    207919
  • 優(yōu)化
    +關注

    關注

    0

    文章

    219

    瀏覽量

    23823
  • 線程
    +關注

    關注

    0

    文章

    501

    瀏覽量

    19580

原文標題:內核mmap_sem鎖的危害和相關優(yōu)化

文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    拆解mmap內存映射的本質!

    mmap 內存映射里所謂的內存其實指的是虛擬內存,在調用 mmap 進行匿名映射的時候(比如進行堆內存的分配),是將進程虛擬內存空間中的某一段虛擬內存區(qū)域與物理內存中的匿名內存頁進行映射,當調用
    的頭像 發(fā)表于 01-24 14:30 ?914次閱讀
    拆解<b class='flag-5'>mmap</b>內存映射的本質!

    參加搜索引擎營銷SEM培訓的好處?

    1. 可以快速學習搜索引擎營銷(SEM)投放策略、方法和技術,避免在自己摸索中浪費時間;2. 可以快速學習到搜索引擎營銷(SEM)最新優(yōu)化技術,在頂尖SEM
    發(fā)表于 04-11 14:21

    第11章 臨界段,任務和中斷

    轉rtx操作系統(tǒng) 本章教程為大家講解幾個重要的概念,臨界段,任務和中斷。本章教程配套的例子含Cortex-M3內核的STM32F103和Cortex-M4內核的STM32F407。
    發(fā)表于 10-04 19:58

    Linux的mmap文件內存映射機制

    Linux的mmap文件內存映射機制在講述文件映射的概念時, 不可避免的要牽涉到虛存(SVR 4的VM). 實際上, 文件映射是虛存的中心概念, 文件映射一方面給用戶提供了一組措施, 好似用戶將文件
    發(fā)表于 03-08 09:54

    字符設備驅動另一種寫法—mmap方法操作LED

    。經(jīng)過自己的研究之后,我發(fā)現(xiàn)還有另外一種寫法,直接在應用層操作,省去了內核中的地址映射部分,使得用戶可以在應用層直接操作LED。 mmap方法是把設備物理地址直接映射到用戶空間的一種系統(tǒng)調用方法,他使得
    發(fā)表于 01-02 17:38

    芯靈思SinlinxA33開發(fā)板的Linux內核信號量學習

    用戶進程。我們可以從頭文件/usr/src/linux/include/linux/sem.h 中看到內核用來維護信號量狀態(tài)的各個結構的定義。信號量是一個數(shù)據(jù)集合,用戶可以單獨使用這一集合的每個元素。要
    發(fā)表于 02-20 15:50

    芯靈思SinlinxA64開發(fā)板 Linux內核信號量學習

    等待此信號量,則喚醒此進程。     維護信號量狀態(tài)的是Linux內核操作系統(tǒng)而不是用戶進程。我們可以從頭文件/usr/src/linux/include/linux/sem.h 中看到內核用來維護
    發(fā)表于 03-15 16:10

    Linux內核同步機制的自旋原理是什么?

    自旋是專為防止多處理器并發(fā)而引入的一種,它在內核中大量應用于中斷處理等部分(對于單處理器來說,防止中斷處理中的并發(fā)可簡單采用關閉中斷的方式,即在標志寄存器中關閉/打開中斷標志位,不需要自旋
    發(fā)表于 03-31 08:06

    感性負載的危害

    感性負載的危害 先看看車和控制車的大致結構:我們一般用兩個電機
    發(fā)表于 11-21 14:28 ?6615次閱讀

    Linux內核同步機制的自旋原理

    一、自旋 自旋是專為防止多處理器并發(fā)而引入的一種,它在內核中大量應用于中斷處理等部分(對于單處理器來說,防止中斷處理中的并發(fā)可簡單采用關閉中
    發(fā)表于 06-08 14:50 ?1293次閱讀

    linux_mmap_access_performance

    linux 內存訪問提升性能的一片論文,需要理解kernel的mmap方式,比較適合優(yōu)化驅動
    發(fā)表于 02-23 15:48 ?14次下載

    優(yōu)化簡單的OpenCL內核:調整內核優(yōu)化

    Robert Ioffe描述了一系列一致的優(yōu)化,可以提高英特爾?上的OpenCL內核性能Iris?圖形或英特爾?Iris?Pro圖形,使用英特爾?SDKfor OpenCL?應用程序2013。
    的頭像 發(fā)表于 11-07 06:17 ?3230次閱讀

    mmap系統(tǒng)調用和vmalloc獲取地址空間

    mmap()系統(tǒng)調用是在用戶進程與內核之間共享內存區(qū)域的常用方法。我們最近有個程序,需要應用進程能夠讀取內核驅動獲取的數(shù)據(jù),經(jīng)過簡單的調研,決定采用mmap方式。
    的頭像 發(fā)表于 02-02 16:13 ?4201次閱讀

    linux drivers中的mmap實現(xiàn)

    將設備驅動內核空間的內存映射到用戶空間里,可以通過用戶空間中的mmap系統(tǒng)調用代替系統(tǒng)調用write和read。目的是提高讀寫效率。
    發(fā)表于 05-15 10:31 ?1561次閱讀

    信號量和自旋

    。??? Linux 使用的同步機制可以說從2.0到2.6以來不斷發(fā)展完善。從最初的原子操作,到后來的信號量,從大內核到今天的自旋。這些同步機制的發(fā)展伴隨 Linux從單處理器到對稱多處理器的過度
    發(fā)表于 04-02 14:43 ?776次閱讀