當前位置:首頁 » 操作系統 » linux內核共享

linux內核共享

發布時間: 2022-12-30 04:55:01

linux系統的內存管理知識詳解

內存是Linux內核所管理的最重要的資源之一。內存管理系統是操作系統中最為重要的部分,因為系統的物理內存總是少於系統所需要的內存數量。虛擬內存就是為了克服這個矛盾而採用的策略。系統的虛擬內存通過在各個進程之間共享內存而使系統看起來有多於實際內存的內存容量。Linux支持虛擬內存, 就是使用磁碟作為RAM的擴展,使可用內存相應地有效擴大。核心把當前不用的內存塊存到硬碟,騰出內存給其他目的。當原來的內容又要使用時,再讀回內存。以下就是我為大家整理到的詳細LINUX系統內存管理的知識,歡迎大家閱讀!!!

LINUX系統教程:內存管理的知識詳解

一、內存使用情況監測

(1)實時監控內存使用情況

在命令行使用「Free」命令可以監控內存使用情況

代碼如下:

#free

total used free shared buffers cached

Mem: 256024 192284 63740 0 10676 101004

-/+ buffers/cache: 80604 175420

Swap: 522072 0 522072

上面給出了一個256兆的RAM和512兆交換空間的系統情況。第三行輸出(Mem:)顯示物理內存。total列不顯示核心使用的物理內存(通常大約1MB)。used列顯示被使用的內存總額(第二行不計緩沖)。 free列顯示全部沒使用的內存。Shared列顯示多個進程共享的內存總額。Buffers列顯示磁碟緩存的當前大小。第五行(Swap:)對對換空間,顯示的信息類似上面。如果這行為全0,那麼沒使用對換空間。在預設的狀態下,free命令以千位元組(也就是1024位元組為單位)來顯示內存使用情況。可以使用—h參數以位元組為單位顯示內存使用情況,或者可以使用—m參數以兆位元組為單位顯示內存使用情況。還可以通過—s參數使用命令來不間斷地監視內存使用情況:

#free –b –s2

這個命令將會在終端窗口中連續不斷地報告內存的使用情況,每2秒鍾更新一次。

(2)組合watch與 free命令用來實時監控內存使用情況:

代碼如下:

#watch -n 2 -d free

Every 2.0s: free Fri Jul 6 06:06:12 2007

total used free shared buffers cached

Mem: 233356 218616 14740 0 5560 64784

-/+ buffers/cache: 148272 85084

Swap: 622584 6656 615928

watch命令會每兩秒執行 free一次,執行前會清除屏幕,在同樣位置顯示數據。因為 watch命令不會卷動屏幕,所以適合出長時間的監測內存使用率。可以使用 -n選項,控制執行的頻率;也可以利用 -d選項,讓命令將每次不同的地方顯示出來。Watch命令會一直執行,直到您按下 [Ctrl]-[C] 為止。

二、虛擬內存的概念

(1)Linux虛擬內存實現機制

Linux虛擬內存的實現需要六種機制的支持:地址映射機制、內存分配回收機制、緩存和刷新機制、請求頁機制、交換機制、內存共享機制。

首先內存管理程序通過映射機制把用戶程序的邏輯地址映射到物理地址,在用戶程序運行時如果發現程序中要用的虛地址沒有對應的物理內存時,就發出了請求頁要求;如果有空閑的內存可供分配,就請求分配內存(於是用到了內存的分配和回收),並把正在使用的物理頁記錄在緩存中(使用了緩存機制)。 如果沒有足夠的內存可供分配,那麼就調用交換機制,騰出一部分內存。另外在地址映射中要通過TLB(翻譯後援存儲器)來尋找物理頁;交換機制中也要用到交換緩存,並且把物理頁內容交換到交換文件中後也要修改頁表來映射文件地址。

(2)虛擬內存容量設定

也許有人告訴你,應該分配2倍於物理內存的虛擬內存,但這是個不固定的規律。如果你的物理保存比較小,可以這樣設定。如果你有1G物理內存或更多的話,可以縮小一下虛擬內存。Linux會把大量的內存用做Cache的,但在資源緊張時回收回.。你只要看到swap為0或者很小就可以放心了,因為內存放著不用才是最大的浪費。

三、使甩vmstat命令監視虛擬內存使用情況

vmstat是Virtual Meomory Statistics(虛擬內存統計)的縮寫,可對操作系統的虛擬內存、進程、CPU活動進行監視。它是對系統的整體情況進行統計,不足之處是無法對某個進程進行深入分析。通常使用vmstat 5 5(表示在5秒時間內進行5次采樣)命令測試。將得到一個數據匯總它可以反映真正的系統情況。

代碼如下:

#vmstat 5 5

procs -----------memory---------- ---swap-- -----io---- --system-- ----cpu----

r b swpd free buff cache si so bi bo in cs us sy id wa

1 0 62792 3460 9116 88092 6 30 189 89 1061 569 17 28 54 2

0 0 62792 3400 9124 88092 0 0 0 14 884 434 4 14 81 0

0 0 62792 3400 9132 88092 0 0 0 14 877 424 4 15 81 0

1 0 62792 3400 9140 88092 0 0 0 14 868 418 6 20 74 0

1 0 62792 3400 9148 88092 0 0 0 15 847 400 9 25 67 0

vmstat命令輸出分成六個部分:

(1)進程procs:

r:在運行隊列中等待的進程數 。

b:在等待io的進程數 。

(2)內存memoy:

swpd:現時可用的交換內存(單位KB)。

free:空閑的內存(單位KB)。

buff: 緩沖去中的內存數(單位:KB)。

cache:被用來做為高速緩存的內存數(單位:KB)。

(3) swap交換頁面

si: 從磁碟交換到內存的交換頁數量,單位:KB/秒。

so: 從內存交換到磁碟的交換頁數量,單位:KB/秒。

(4) io塊設備:

bi: 發送到塊設備的塊數,單位:塊/秒。

bo: 從塊設備接收到的塊數,單位:塊/秒。

(5)system系統:

in: 每秒的中斷數,包括時鍾中斷。

cs: 每秒的環境(上下文)切換次數。

(6)cpu中央處理器:

cs:用戶進程使用的時間 。以百分比表示。

sy:系統進程使用的時間。 以百分比表示。

id:中央處理器的空閑時間 。以百分比表示。

如果 r經常大於 4 ,且id經常小於40,表示中央處理器的負荷很重。 如果bi,bo 長期不等於0,表示物理內存容量太小。

四、Linux 伺服器的內存泄露和回收內存的方法

1、內存泄漏的定義:

一般我們常說的內存泄漏是指堆內存的泄漏。堆內存是指程序從堆中分配的,大小任意的(內存塊的大小可以在程序運行期決定),使用完後必須顯示釋放的內存。應用程序一般使用malloc,realloc,new等函數從堆中分配到一塊內存,使用完後,程序必須負責相應的調用free或釋放該內存塊,否則,這塊內存就不能被再次使用,我們就說這塊內存泄漏了。

2、內存泄露的危害

從用戶使用程序的角度來看,內存泄漏本身不會產生什麼危害,作為一般的用戶,根本感覺不到內存泄漏的存在。真正有危害的`是內存泄漏的堆積,這會最終消耗盡系統所有的內存。從這個角度來說,一次性內存泄漏並沒有什麼危害,因為它不會堆積,而隱式內存泄漏危害性則非常大,因為較之於常發性和偶發性內存泄漏它更難被檢測到。存在內存泄漏問題的程序除了會佔用更多的內存外,還會使程序的性能急劇下降。對於伺服器而言,如果出現這種情況,即使系統不崩潰,也會嚴重影響使用。

3、內存泄露的檢測和回收

對於內存溢出之類的麻煩可能大家在編寫指針比較多的復雜的程序的時候就會遇到。在 Linux 或者 unix 下,C、C++語言是最使用工具。但是我們的 C++ 程序缺乏相應的手段來檢測內存信息,而只能使用 top 指令觀察進程的動態內存總額。而且程序退出時,我們無法獲知任何內存泄漏信息。

使用kill命令

使用Linux命令回收內存,我們可以使用Ps、Kill兩個命令檢測內存使用情況和進行回收。在使用超級用戶許可權時使用命令「Ps」,它會列出所有正在運行的程序名稱,和對應的進程號(PID)。Kill命令的工作原理是:向Linux操作系統的內核送出一個系統操作信號和程序的進程號(PID)。

應用例子:

為了高效率回收內存可以使用命令ps 參數v:

代碼如下:

[root@www ~]# ps v

PID TTY STAT TIME MAJFL TRS DRS RSS %MEM COMMAND

2542 tty1 Ss+ 0:00 0 8 1627 428 0.1 /sbin/mingetty tty1

2543 tty2 Ss+ 0:00 0 8 1631 428 0.1 /sbin/mingetty tty2

2547 tty3 Ss+ 0:00 0 8 1631 432 0.1 /sbin/mingetty tty3

2548 tty4 Ss+ 0:00 0 8 1627 428 0.1 /sbin/mingetty tty4

2574 tty5 Ss+ 0:00 0 8 1631 432 0.1 /sbin/mingetty tty5

2587 tty6 Ss+ 0:00 0 8 1627 424 0.1 /sbin/mingetty tty6

2657 tty7 Ss+ 1:18 12 1710 29981 7040 3.0 /usr/bin/Xorg :0 -br -a

2670 pts/2 Ss 0:01 2 682 6213 1496 0.6 -bash

3008 pts/4 Ss 0:00 2 682 6221 1472 0.6 /bin/bash

3029 pts/4 S+ 0:00 2 32 1783 548 0.2 ping 192.168.1.12

3030 pts/2 R+ 0:00 2 73 5134 768 0.3 ps v

然後如果想回收Ping命令的內存的話,使用命令:

代碼如下:

# Kill -9 3029

使用工具軟體

Memprof是一個非常具有吸引力且非常易於使用的軟體,它由Red Hat的Owen Talyor創立。這個工具是用於GNOME前端的Boehm-Demers-Weiser垃圾回收器。這個工具直接就可以執行,並且其工作起來無需對源代碼進行任何修改。在程序執行時,這個工具會以圖形化的方式顯示內存的使用情況。

相關介紹:Linux

嚴格來講,Linux這個詞本身只表示Linux內核,但人們已經習慣了用Linux來形容整個基於Linux內核,並且使用GNU 工程各種工具和資料庫的操作系統。

Linux擁有以下特性:類似於Unix的基本思想,支持完全免費與自由傳播,完全兼容POSIX1.0標准,支持多用戶、多任務、有著良好的界面、支持多種平台。Linux 能運行主要的UNIX工具軟體、應用程序和網路協議。它支持32位和64位硬體。Linux繼承了Unix以網路為核心的設計思想,是一個性能穩定的多用戶網路操作系統。

Linux有著許多不同的版本,但它們都使用了Linux內核。Linux可安裝在各種計算機硬體設備中,比如手機、平板電腦、路由器、視頻游戲控制台、台式計算機、大型機和超級計算機。

② 什麼是linux內核

Linux是由Linus Torvalds開發的類UNIX的操作系統,Linux主要特點是開源的,因此我們可以免費使用來當做伺服器。

Linux嚴格分為兩個含義。

1.廣泛的Linux是指Linux發行版

2.狹義的Linux是指Linux內核

Linux內核是操作系統的基礎,介於硬體和軟體之間,並且內核位於操作系統中,操作系統將在硬體和軟體之間進行調解,Linux內核是操作系統核心部分的功能。

二:內核的基本性能

1.流程管理

在Linux內核中,程序的執行狀態以進程為單位進行管理。此外,內核為每個進程准備一個名為task_stract結構的數據結構。

2.進程調度程序

可執行狀態等待哪個進程以哪個順序執行,由於基本上不可能運行比CPU數量更多的進程,因此運行過程的效率非常重要。

3.內存管理

在Linux內核中,使用物理內存和虛擬內存管理數據。通過分配對應於物理存儲器的虛擬地址,而不是實際為每個進程分配物理存儲器地址,可以使用容量遠大於實際物理存儲器容量的存儲器。它使它成為可能。此外,由於每個進程都分配了自己的虛擬地址,因此每個進程的內存空間是獨立的,並且不會違反其他進程的內存。

3.文件系統

它以文件的形式提供存儲數據的訪問方法。所有數據都以文件的形式進行管理。/ Directory(根目錄)作為頂點,內核本身作為文件和目錄的集合存在。

③ linux為什麼一定要把內核空間劃分到高1G

Linux虛擬內存的大小為2^32(在32位的x86機器上),內核將這4G位元組的空間分為兩部分。最高的1G位元組(從虛地址
0xC0000000到0xFFFFFFFF)供內核使用,稱為「內核空間」。而較低的3G位元組(從虛地址0x00000000到
0xBFFFFFFF),供各個進程使用,稱為「用戶空間」。因為每個進程可以通過系統調用進入內核,因此,Linux內核空間由系統內的所有進程共享。
於是,從具體進程的角度來看,每個進程可以擁有4G位元組的虛擬地址空間(也叫虛擬內存).

每個進程有各自的私有用戶空間(0~3G),這個空間對系統中的其他進程是不可見的。最高的1GB內核空間則為所有進程以及內核所共享。另外,進程的「用戶空間」也叫「地址空間」,在後面的敘述中,我們對這兩個術語不再區分。

用戶空間不是進程共享的,而是進程隔離的。每個進程最大都可以有3GB的用戶空間。一個進程對其中一個地址的訪問,與其它進程對於同一地址的訪問絕不沖
突。比如,一個進程從其用戶空間的地址0x1234ABCD處可以讀出整數8,而另外一個進程從其用戶空間的地址0x1234ABCD處可以讀出整數
20,這取決於進程自身的邏輯。
因此Linux對用戶空間與內核空間的劃分起到了一定程度上的沖突避免。

④ linux內核同步問題

Linux內核設計與實現 十、內核同步方法

手把手教Linux驅動5-自旋鎖、信號量、互斥體概述

== 基礎概念: ==

並發 :多個執行單元同時進行或多個執行單元微觀串列執行,宏觀並行執行

競態 :並發的執行單元對共享資源(硬體資源和軟體上的全局變數)的訪問而導致的竟態狀態。

臨界資源 :多個進程訪問的資源

臨界區 :多個進程訪問的代碼段

== 並發場合: ==

1、單CPU之間進程間的並發 :時間片輪轉,調度進程。 A進程訪問列印機,時間片用完,OS調度B進程訪問列印機。

2、單cpu上進程和中斷之間並發 :CPU必須停止當前進程的執行中斷;

3、多cpu之間

4、單CPU上中斷之間的並發

== 使用偏向: ==

==信號量用於進程之間的同步,進程在信號量保護的臨界區代碼裡面是可以睡眠的(需要進行進程調度),這是與自旋鎖最大的區別。==

信號量又稱為信號燈,它是用來協調不同進程間的數據對象的,而最主要的應用是共享內存方式的進程間通信。本質上,信號量是一個計數器,它用來記錄對某個資源(如共享內存)的存取狀況。它負責協調各個進程,以保證他們能夠正確、合理的使用公共資源。它和spin lock最大的不同之處就是:無法獲取信號量的進程可以睡眠,因此會導致系統調度。

1、==用於進程與進程之間的同步==

2、==允許多個進程進入臨界區代碼執行,臨界區代碼允許睡眠;==

3、信號量本質是==基於調度器的==,在UP和SMP下沒有區別;進程獲取不到信號量將陷入休眠,並讓出CPU;

4、不支持進程和中斷之間的同步

5、==進程調度也是會消耗系統資源的,如果一個int型共享變數就需要使用信號量,將極大的浪費系統資源==

6、信號量可以用於多個線程,用於資源的計數(有多種狀態)

==信號量加鎖以及解鎖過程:==

sema_init(&sp->dead_sem, 0); / 初始化 /

down(&sema);

臨界區代碼

up(&sema);

==信號量定義:==

==信號量初始化:==

==dowm函數實現:==

==up函數實現:==

信號量一般可以用來標記可用資源的個數。

舉2個生活中的例子:

==dowm函數實現原理解析:==

(1)down

判斷sem->count是否 > 0,大於0則說明系統資源夠用,分配一個給該進程,否則進入__down(sem);

(2)__down

調用__down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);其中TASK_UNINTERRUPTIBLE=2代表進入睡眠,且不可以打斷;MAX_SCHEDULE_TIMEOUT休眠最長LONG_MAX時間;

(3)list_add_tail(&waiter.list, &sem->wait_list);

把當前進程加入到sem->wait_list中;

(3)先解鎖後加鎖;

進入__down_common前已經加鎖了,先把解鎖,調用schele_timeout(timeout),當waiter.up=1後跳出for循環;退出函數之前再加鎖;

Linux內核ARM構架中原子變數的底層實現研究

rk3288 原子操作和原子位操作

原子變數適用於只共享一個int型變數;

1、原子操作是指不被打斷的操作,即它是最小的執行單位。

2、最簡單的原子操作就是一條條的匯編指令(不包括一些偽指令,偽指令會被匯編器解釋成多條匯編指令)

==常見函數:==

==以atomic_inc為例介紹實現過程==

在Linux內核文件archarmincludeasmatomic.h中。 執行atomic_read、atomic_set這些操作都只需要一條匯編指令,所以它們本身就是不可打斷的。 需要特別研究的是atomic_inc、atomic_dec這類讀出、修改、寫回的函數。

所以atomic_add的原型是下面這個宏:

atomic_add等效於:

result(%0) tmp(%1) (v->counter)(%2) (&v->counter)(%3) i(%4)

注意:根據內聯匯編的語法,result、tmp、&v->counter對應的數據都放在了寄存器中操作。如果出現上下文切換,切換機制會做寄存器上下文保護。

(1)ldrex %0, [%3]

意思是將&v->counter指向的數據放入result中,並且(分別在Local monitor和Global monitor中)設置獨占標志。

(2)add %0, %0, %4

result = result + i

(3)strex %1, %0, [%3]

意思是將result保存到&v->counter指向的內存中, 此時 Exclusive monitors會發揮作用,將保存是否成功的標志放入tmp中。

(4) teq %1, #0

測試strex是否成功(tmp == 0 ??)

(5)bne 1b

如果發現strex失敗,從(1)再次執行。

Spinlock 是內核中提供的一種比較常見的鎖機制,==自旋鎖是「原地等待」的方式解決資源沖突的==,即,一個線程獲取了一個自旋鎖後,另外一個線程期望獲取該自旋鎖,獲取不到,只能夠原地「打轉」(忙等待)。由於自旋鎖的這個忙等待的特性,註定了它使用場景上的限制 —— 自旋鎖不應該被長時間的持有(消耗 CPU 資源),一般應用在==中斷上下文==。

1、spinlock是一種死等機制

2、信號量可以允許多個執行單元進入,spinlock不行,一次只能允許一個執行單元獲取鎖,並且進入臨界區,其他執行單元都是在門口不斷的死等

3、由於不休眠,因此spinlock可以應用在中斷上下文中;

4、由於spinlock死等的特性,因此臨界區執行代碼盡可能的短;

==spinlock加鎖以及解鎖過程:==

spin_lock(&devices_lock);

臨界區代碼

spin_unlock(&devices_lock);

==spinlock初始化==

==進程和進程之間同步==

==本地軟中斷之間同步==

==本地硬中斷之間同步==

==本地硬中斷之間同步並且保存本地中斷狀態==

==嘗試獲取鎖==

== arch_spinlock_t結構體定義如下: ==

== arch_spin_lock的實現如下: ==

lockval(%0) newval(%1) tmp(%2) &lock->slock(%3) 1 << TICKET_SHIFT(%4)

(1)ldrex %0, [%3]

把lock->slock的值賦值給lockval;並且(分別在Local monitor和Global monitor中)設置獨占標志。

(2)add %1, %0, %4

newval =lockval +(1<<16); 相當於next+1;

(3)strex %2, %1, [%3]

newval =lockval +(1<<16); 相當於next+1;

意思是將newval保存到 &lock->slock指向的內存中, 此時 Exclusive monitors會發揮作用,將保存是否成功的標志放入tmp中。

(4) teq %2, #0

測試strex是否成功

(5)bne 1b

如果發現strex失敗,從(1)再次執行。

通過上面的分析,可知關鍵在於strex的操作是否成功的判斷上。而這個就歸功於ARM的Exclusive monitors和ldrex/strex指令的機制。

(6)while (lockval.tickets.next != lockval.tickets.owner)

如何lockval.tickets的next和owner是否相等。相同則跳出while循環,否則在循環內等待判斷;

* (7)wfe()和smp_mb() 最終調用#define barrier() asm volatile ("": : :"memory") *

阻止編譯器重排,保證編譯程序時在優化屏障之前的指令不會在優化屏障之後執行。

== arch_spin_unlock的實現如下: ==

退出鎖時:tickets.owner++

== 出現死鎖的情況: ==

1、擁有自旋鎖的進程A在內核態阻塞了,內核調度B進程,碰巧B進程也要獲得自旋鎖,此時B只能自旋轉。 而此時搶占已經關閉,(單核)不會調度A進程了,B永遠自旋,產生死鎖。

2、進程A擁有自旋鎖,中斷到來,CPU執行中斷函數,中斷處理函數,中斷處理函數需要獲得自旋鎖,訪問共享資源,此時無法獲得鎖,只能自旋,產生死鎖。

== 如何避免死鎖: ==

1、如果中斷處理函數中也要獲得自旋鎖,那麼驅動程序需要在擁有自旋鎖時禁止中斷;

2、自旋鎖必須在可能的最短時間內擁有

3、避免某個獲得鎖的函數調用其他同樣試圖獲取這個鎖的函數,否則代碼就會死鎖;不論是信號量還是自旋鎖,都不允許鎖擁有者第二次獲得這個鎖,如果試圖這么做,系統將掛起;

4、鎖的順序規則(a) 按同樣的順序獲得鎖;b) 如果必須獲得一個局部鎖和一個屬於內核更中心位置的鎖,則應該首先獲取自己的局部鎖 ;c) 如果我們擁有信號量和自旋鎖的組合,則必須首先獲得信號量;在擁有自旋鎖時調用down(可導致休眠)是個嚴重的錯誤的;)

== rw(read/write)spinlock: ==

加鎖邏輯:

1、假設臨界區內沒有任何的thread,這個時候任何的讀線程和寫線程都可以鍵入

2、假設臨界區內有一個讀線程,這時候信賴的read線程可以任意進入,但是寫線程不能進入;

3、假設臨界區有一個寫線程,這時候任何的讀、寫線程都不可以進入;

4、假設臨界區內有一個或者多個讀線程,寫線程不可以進入臨界區,但是寫線程也無法阻止後續的讀線程繼續進去,要等到臨界區所有的讀線程都結束了,才可以進入,可見:==rw(read/write)spinlock更加有利於讀線程;==

== seqlock(順序鎖): ==

加鎖邏輯:

1、假設臨界區內沒有任何的thread,這個時候任何的讀線程和寫線程都可以鍵入

2、假設臨界區內沒有寫線程的情況下,read線程可以任意進入;

3、假設臨界區有一個寫線程,這時候任何的讀、寫線程都不可以進入;

4、假設臨界區內只有read線程的情況下,寫線程可以理解執行,不會等待,可見:==seqlock(順序鎖)更加有利於寫線程;==

讀寫速度 CPU > 一級緩存 > 二級緩存 > 內存 ,因此某一個CPU0的lock修改了,其他的CPU的lock就會失效;那麼其他CPU就會依次去L1 L2和主存中讀取lock值,一旦其他CPU去讀取了主存,就存在系統性能降低的風險;

mutex用於互斥操作。

互斥體只能用於一個線程,資源只有兩種狀態(佔用或者空閑)

1、mutex的語義相對於信號量要簡單輕便一些,在鎖爭用激烈的測試場景下,mutex比信號量執行速度更快,可擴展

性更好,

2、另外mutex數據結構的定義比信號量小;、

3、同一時刻只有一個線程可以持有mutex

4、不允許遞歸地加鎖和解鎖

5、當進程持有mutex時,進程不可以退出。

• mutex必須使用官方API來初始化。

• mutex可以睡眠,所以不允許在中斷處理程序或者中斷下半部中使用,例如tasklet、定時器等

==常見操作:==

struct mutex mutex_1;

mutex_init(&mutex_1);

mutex_lock(&mutex_1)

臨界區代碼;

mutex_unlock(&mutex_1)

==常見函數:==

=

⑤ linux兩種共享內存的區別

持久性不同、文件反射方式不同。
1、持久性不同。sysvshm是持久化的,除非被一個進程明確的刪除,否則它始終存在於內存里,直到系統關機。mmap映射的內存在不是持久化的,假如進程關閉,映射隨即失效,除非事前已經映射到了一個文件上。
2、文件反射方式不同。前者用COW的方式,把文件映射到當前的進程空間,修改操作不會改動源文件。後者直接把文件映射到當前的進程空間,所有的修改會直接反應到文件的page cache,而後由內核自動同步到映射文件上。

⑥ linux內核如何勾選共享內存

所謂共享內存就是使得多個進程可以訪問同一塊內存空間,是最快的可用IPC形式。是針對其他通信機制運行效率較低而設計的。往往與其它通信機制,如信號量結合使用,來達到進程間的同步及互斥。其他進程能把同一段共享內存段「連接到」他們自己的地址空間里去。所有進程都能訪問共享內存中的地址。如果一個進程向這段共享內存寫了數據,所做的改動會即時被有訪問同一段共享內存的其他進程看到。共享內存的使用大大降低了在大規模數據處理過程中內存的消耗,但是共享內存的使用中有很多的陷阱,一不注意就很容易導致程序崩潰。

1.超過共享內存的大小限制?

在一個linux伺服器上,共享內存的總體大小是有限制的,這個大小通過SHMMAX參數來定義(以位元組為單位),您可以通過執行以下命令來確定 SHMMAX 的值:

# cat /proc/sys/kernel/shmmax

如果機器上創建的共享內存的總共大小超出了這個限制,在程序中使用標准錯誤perror可能會出現以下的信息:

unable to attach to shared memory

>解決方法:

1、設置 SHMMAX

SHMMAX 的默認值是 32MB 。一般使用下列方法之一種將 SHMMAX 參數設為 2GB :

通過直接更改 /proc 文件系統,你不需重新啟動機器就可以改變 SHMMAX 的默認設置。我使用的方法是將以下命令放入 />etc/rc.local 啟動文件中:

echo "2147483648" > /proc/sys/kernel/shmmax

您還可以使用 sysctl 命令來更改 SHMMAX 的值:sysctl -w kernel.shmmax=2147483648

最後,通過將該內核參數插入到 /etc/sysctl.conf 啟動文件中,您可以使這種更改永久有效:echo "kernel.shmmax=2147483648" >> /etc/sysctl.conf

2、設置 SHMMNI

⑦ 一文搞懂 , Linux內核—— 同步管理(下)

上面講的自旋鎖,信號量和互斥鎖的實現,都是使用了原子操作指令。由於原子操作會 lock,當線程在多個 CPU 上爭搶進入臨界區的時候,都會操作那個在多個 CPU 之間共享的數據 lock。CPU 0 操作了 lock,為了數據的一致性,CPU 0 的操作會導致其他 CPU 的 L1 中的 lock 變成 invalid,在隨後的來自其他 CPU 對 lock 的訪問會導致 L1 cache miss(更准確的說是communication cache miss),必須從下一個 level 的 cache 中獲取。

這就會使緩存一致性變得很糟,導致性能下降。所以內核提供一種新的同步方式:RCU(讀-復制-更新)。

RCU 解決了什麼

RCU 是讀寫鎖的高性能版本,它的核心理念是讀者訪問的同時,寫者可以更新訪問對象的副本,但寫者需要等待所有讀者完成訪問之後,才能刪除老對象。讀者沒有任何同步開銷,而寫者的同步開銷則取決於使用的寫者間同步機制。

RCU 適用於需要頻繁的讀取數據,而相應修改數據並不多的情景,例如在文件系統中,經常需要查找定位目錄,而對目錄的修改相對來說並不多,這就是 RCU 發揮作用的最佳場景。

RCU 例子

RCU 常用的介面如下圖所示:

為了更好的理解,在剖析 RCU 之前先看一個例子:

#include<linux/kernel.h>#include<linux/mole.h>#include<linux/init.h>#include<linux/slab.h>#include<linux/spinlock.h>#include<linux/rcupdate.h>#include<linux/kthread.h>#include<linux/delay.h>structfoo{inta;structrcu_headrcu;};staticstructfoo*g_ptr;staticintmyrcu_reader_thread1(void*data)//讀者線程1{structfoo*p1=NULL;while(1){if(kthread_should_stop())break;msleep(20);rcu_read_lock();mdelay(200);p1=rcu_dereference(g_ptr);if(p1)printk("%s: read a=%d\n",__func__,p1->a);rcu_read_unlock();}return0;}staticintmyrcu_reader_thread2(void*data)//讀者線程2{structfoo*p2=NULL;while(1){if(kthread_should_stop())break;msleep(30);rcu_read_lock();mdelay(100);p2=rcu_dereference(g_ptr);if(p2)printk("%s: read a=%d\n",__func__,p2->a);rcu_read_unlock();}return0;}staticvoidmyrcu_del(structrcu_head*rh)//回收處理操作{structfoo*p=container_of(rh,structfoo,rcu);printk("%s: a=%d\n",__func__,p->a);kfree(p);}staticintmyrcu_writer_thread(void*p)//寫者線程{structfoo*old;structfoo*new_ptr;intvalue=(unsignedlong)p;while(1){if(kthread_should_stop())break;msleep(250);new_ptr=kmalloc(sizeof(structfoo),GFP_KERNEL);old=g_ptr;*new_ptr=*old;new_ptr->a=value;rcu_assign_pointer(g_ptr,new_ptr);call_rcu(&old->rcu,myrcu_del);printk("%s: write to new %d\n",__func__,value);value++;}return0;}staticstructtask_struct*reader_thread1;staticstructtask_struct*reader_thread2;staticstructtask_struct*writer_thread;staticint__initmy_test_init(void){intvalue=5;printk("figo: my mole init\n");g_ptr=kzalloc(sizeof(structfoo),GFP_KERNEL);reader_thread1=kthread_run(myrcu_reader_thread1,NULL,"rcu_reader1");reader_thread2=kthread_run(myrcu_reader_thread2,NULL,"rcu_reader2");writer_thread=kthread_run(myrcu_writer_thread,(void*)(unsignedlong)value,"rcu_writer");return0;}staticvoid__exitmy_test_exit(void){printk("goodbye\n");kthread_stop(reader_thread1);kthread_stop(reader_thread2);kthread_stop(writer_thread);if(g_ptr)kfree(g_ptr);}MODULE_LICENSE("GPL");mole_init(my_test_init);mole_exit(my_test_exit);

執行結果是:

myrcu_reader_thread2:reada=0myrcu_reader_thread1:reada=0myrcu_reader_thread2:reada=0myrcu_writer_thread:writetonew5myrcu_reader_thread2:reada=5myrcu_reader_thread1:reada=5myrcu_del:a=0

RCU 原理

可以用下面一張圖來總結,當寫線程 myrcu_writer_thread 寫完後,會更新到另外兩個讀線程 myrcu_reader_thread1 和 myrcu_reader_thread2。讀線程像是訂閱者,一旦寫線程對臨界區有更新,寫線程就像發布者一樣通知到訂閱者那裡,如下圖所示。

寫者在拷貝副本修改後進行 update 時,首先把舊的臨界資源數據移除(Removal);然後把舊的數據進行回收(Reclamation)。結合 API 實現就是,首先使用 rcu_assign_pointer 來移除舊的指針指向,指向更新後的臨界資源;然後使用 synchronize_rcu 或 call_rcu 來啟動 Reclaimer,對舊的臨界資源進行回收(其中 synchronize_rcu 表示同步等待回收,call_rcu 表示非同步回收)。

為了確保沒有讀者正在訪問要回收的臨界資源,Reclaimer 需要等待所有的讀者退出臨界區,這個等待的時間叫做寬限期(Grace Period)。

Grace Period

中間的黃色部分代表的就是 Grace Period,中文叫做寬限期,從 Removal 到 Reclamation,中間就隔了一個寬限期,只有當寬限期結束後,才會觸發回收的工作。寬限期的結束代表著 Reader 都已經退出了臨界區,因此回收工作也就是安全的操作了。

寬限期是否結束,與 CPU 的執行狀態檢測有關,也就是檢測靜止狀態 Quiescent Status。

Quiescent Status

Quiescent Status,用於描述 CPU 的執行狀態。當某個 CPU 正在訪問 RCU 保護的臨界區時,認為是活動的狀態,而當它離開了臨界區後,則認為它是靜止的狀態。當所有的 CPU 都至少經歷過一次 Quiescent Status 後,寬限期將結束並觸發回收工作。

因為 rcu_read_lock 和 rcu_read_unlock 分別是關閉搶占和打開搶占,如下所示:

staticinlinevoid__rcu_read_lock(void){preempt_disable();}

staticinlinevoid__rcu_read_unlock(void){preempt_enable();}

所以發生搶占,就說明不在 rcu_read_lock 和 rcu_read_unlock 之間,即已經完成訪問或者還未開始訪問。

Linux 同步方式的總結

資料免費領

學習直通車

⑧ linux 線程間共享內核棧嗎

首先,我們知道所有線程共享主線程的虛擬地址空間(current->mm指向同一個地址),且都有自己的用戶態堆棧(共享父進程的地址空間,再在裡面分配自己的獨立棧,默認2M)。這是毫無疑問的,但還有一點我沒搞明白,內核棧是共享還是獨立的?猜測:獨立的。理由:要不然內核棧對應的thread_info中的tast_struct沒有辦法與每個線程對應起來,因為現在已經有多個task_struct了,但保存內核棧的thread_info(其實是thread_union聯合體)中只能保存一個task_struct。所以理論上分析,雖然可以共享地址空間,但每個線程還是需要一個單獨的內核棧的。看代碼:分析創建線程最終肯定會走到內核函數do_fork()中來的,所以從此函數看起。do_fork()->_process()->p_task_struct()fork.c中p_task_struct()的實現:static struct task_struct *p_task_struct(struct task_struct *orig){struct task_struct *tsk;struct thread_info *ti;unsigned long *stackend;int node = tsk_fork_get_node(orig);int err;tsk = alloc_task_struct_node(node);if (!tsk)return NULL;ti = alloc_thread_info_node(tsk, node);/*就是這里,果然分配內核棧了*/if (!ti)goto free_tsk;err = arch_p_task_struct(tsk, orig);/*這里分配task_struct結構*/if (err)goto free_ti;tsk->stack = ti; ...}

熱點內容
長虹安卓電視關閉網路在哪裡 發布:2025-05-10 14:37:04 瀏覽:142
ubuntuhttp伺服器的搭建 發布:2025-05-10 14:33:06 瀏覽:37
微信找回密碼申訴要多少時間 發布:2025-05-10 14:14:05 瀏覽:435
大眾寶來速騰選哪個配置 發布:2025-05-10 14:10:53 瀏覽:128
數字機頂盒密碼是多少 發布:2025-05-10 14:10:06 瀏覽:334
取消訪問網路需要密碼 發布:2025-05-10 13:44:20 瀏覽:64
shell編程運行 發布:2025-05-10 13:37:54 瀏覽:640
win7訪問xp共享需要密碼 發布:2025-05-10 13:34:10 瀏覽:344
飯團看書為什麼緩存不了小說 發布:2025-05-10 13:17:03 瀏覽:13
如何配置登錄源地址限制 發布:2025-05-10 13:12:52 瀏覽:591