❶ linux中讓子進程執行和父進程不同功能的兩種常用方法
運維
Linux系統進程式控制制
行者111111111111111
原創
關注
0點贊·3人閱讀
1、進程創建
shell命令行啟動程序指令皆是創建了進程,我們通常通過調用fork()函數創建子進程。
1.1、fork()函數用法簡介
調用fork後,操作系統內核將:
分配新的內存塊和內核數據結構給子進程
將父進程部分數據結構內容拷貝至子進程
添加子進程到系統進程列表當中
fork返回,開始調度器調度
1.2、fork函數返回值
子進程返回0,父進程返回的是子進程的pid
原因:fork之後進入內核,申請內存構建子進程PCB、虛擬內存、頁表,將子進程設置R狀態,放入調度隊列,由於創建子進程之後父子進程共享代碼,所以父子進程都會有return返回值。返回值返回給變數本質發生了寫時拷貝,改變了子進程對應頁表的指向,數據映射到了其他區域
1.3、寫時拷貝
由於進程要獨立,代碼不可修改,數據可改,所以默認數據各有一份,但是內存是有限度的,如果把父進程數據全部再拷貝一份,那麼太浪費內存,甚至導致fork失敗。通常通過寫時拷貝實現,就是當父或子進程修改數據時,將要修改的數據拷貝一份,讓子進程頁表指向新的重復數據在發生修改
❷ linux進程內存相關
3種地址:虛擬地址、物理地址、邏輯地址
物理地址:內存的電路地址,對應內存地址線上的高低電平,物理可見的。
虛擬地址: 分頁機制 的產物,也叫線性地址,是進程能看見的地址。
邏輯地址: 分段機制 的產物,屬於inter cpu的歷史遺留問題,linux可以當做不存在。
3種地址的轉換:進程訪問邏輯地址,linux內核根據分段機制裝換成虛擬地址,然後把進程的頁表和虛擬地址都告訴cpu,cpu就可以根據分頁機制將虛擬地址裝換成物理地址,然後訪問內存。
linux內核中巧妙地屏蔽里分段機制,就是邏輯地址等於虛擬地址,訪問內存只需要利用分頁機制把虛擬地址轉換成物理地址。
linux會為每個進程創建自己的虛擬地址空間,就是進程地址空間,64位系統就是128T的內存空間。需要注意的是,虛擬地址就是假的,一開始不和物理地址對應,也就是說不佔用物理內存,只有當虛擬地址有寫入操作是,內核會觸發缺頁,分配真實的物理地址給虛擬地址。物理地址的管理可參考 內核內存管理
從進程空間看,用戶態閑置內存有3塊,Stack、Memory Mapping Region、Heap,Stack是程序函數調用運行時需要的,不可控,能自由分配的內存就剩Memory Mapping Region、Heap了,linux系統提供的內存分配函數就是針對這兩個區域的。
Heap操作函數:int brk(void *addr)、void *sbrk(intptr_t increment)
Memory Mapping Region操作函數:mmap()、munmap()
當然進程可以直接使用系統調用去申請內存,但是如果不管理的話,經過大量的申請和釋放,會把進程空間切割的亂七八糟,導致不能申請大塊的連續空間,為此就出現了內存管理模塊,封裝了系統調用,對進程提供malloc和free等高級函數。實際上,除了一些特殊程序,我們也很少用系統調用,一般都是使用內存管理模塊提供的malloc和free,關系如下圖:
內存管理模塊用各種好處,例如不會每次操作都去執行系統調用,減少內存碎片的產生等等。
當然也有很多實現方式,例如常用的glibc的Ptmalloc,google的tcmalloc,facebook的jemalloc等。各有各的應用場景,blablabla....
使用時,gcc默認會鏈接glibc的,如果想使用其他lib,gcc鏈接時指定就能覆蓋掉glibc的。
我們重點講Ptmalloc,從而啟發程序員在寫程序時多考慮下內存分配情況,可以選擇或自己實現適合自己程序的內存管理lib。
Ptmalloc的歷史發展,blablabla......,Ptmalloc採取內存池管理,進程malloc時,通過brk(小於128K的內存)、mmap(大內存)從系統獲取地址空間,給進程使用,進程free時,不會立即通過brk、munmap將地址空間還給系統,會自己維護起來,叫做空閑內存,這些空閑內存在進程再次malloc時,還會被分出去,並且空閑內存會在特定條件下合並起來還給系統。
內存分配區,管理了一片內存,對外分發和回收,可以理解為一個內存池,分main arena和non main arena。
main arena:最早的分配區,管理著所有可分配的內存,通過brk,mmap等系統調用向系統申請內存。注意只有main arena可以操作Heap。
non main arena:由於多線程的出現,如果多有線程都操作main arena就會有競爭,需要加鎖控制,所以出現了non main arena,通過mmap向main arena申請一大塊內存,然後自己管理,可以理解為內存分銷商。
只有主線程在main arena上申請內存,子線程在non main arena上,non main arena的個數是有上限的,所以non main arena允許多個子線程共用,這樣就涉及到加鎖,所以程序涉及應避免子線程個數太多。
進程申請到的一塊內存叫做一個內存片,arena內部使用chunk數據結構來描述內存片,包括進程正在使用的內存片,和進程free掉的空閑內存片
A:是否main arena內存
M:使用mmap內存
P:上一塊是否被使用
size of previous chunk:上一塊沒有被使用時,表示上塊長度,被使用時是上塊用來存User data的。
Size of chunk:就是下一塊的size of previous chunk,釋放時填上本塊長度,供下塊合並用。
分給進程的內存片arena可以不管,但是進程free回來的,arena需要通過一定方式組織起來,方便進程再次使用。組織方式有下面幾種:
bins是個數組,包含128個bin,每個bin是個鏈表,分small bin和large bin兩種,各64個,small bin中chunk大小固定,兩個相鄰的small bin中的chunk大小相差8bytes,large bin中chunk大小是一定范圍內的,其中的chunk按大小排列。
空閑chunk按大小選擇合適的bin,按新舊順序掛到鏈表上,優先分配舊的chunk。
不大於max_fast (默認值為64B)的chunk被釋放後,首先會被放到fast bins 中,fast bins中的chunk並不改變它的使用標志P。這樣也就無法將它們合並,當需要給用戶分配的chunk小於或等於max_fast時,ptmalloc首先會在fast bins中查找相應的空閑塊。在特定的時候,ptmalloc會遍歷fast bins中的chunk,將相鄰的空閑chunk進行合並,並將合並後的chunk加入unsorted bin中。
進行malloc時,如果在fast bins中沒有找到合適的chunk,則ptmalloc會先在unsorted bin中查找合適的空閑chunk,如果unsorted bin不能滿足分配要求。malloc便會將unsorted bin中的chunk加入bins中。然後再從bins中繼續進行查找和分配過程。從這個過程可以看出來,unsorted bin可以看做是bins的一個緩沖區,增加它只是為了加快分配的速度。
前面的bin中都是回收回來的內存,top chunk才是內存的初始來源,每個arena都有一個top chunk,用來管理Heap的,Heap會在arena第一次分配內存時初始化,會分配一塊(chunk_size + 128K) align 4K的空間(132K)作為初始的Heap,top chunk占據整個空間,每次分配會在低地址出切出一片,如下圖:
回收時,只有和top chunk相連的內存才能和top chunk合並,才能進而還給系統。
子線程Heap:在main arena中mmap出64M的空間,叫做sub-heap,再在sub-heap上初始化Heap。
主線程的Heap才是真Heap,使用進程Heap,使用brk申請內存。
子線程的heap不夠用時,會在申請新的sub-heap,和老的sub-heap單向鏈表連起來,top chunk會搬到新sub-heap上。
描述mmap出來的內存,單獨管理,free時按閾值來決定是否munmap,有動態調整閾值功能,防止太頻繁的mmap和munmap。本文不關注。
即最後一次small request中因分割而得到的剩餘部分,它有利於改進引用局部性,也即後續對 small chunk 的 malloc 請求可能最終被分配得彼此靠近。
當用戶請求 small chunk而無法從small bin和unsorted bin得到時,會在large bin中找最合適的chunk,然後做切割,返回給用戶的User chunk,剩下的是Remainder chunk添加到unsorted bin中。這一Remainder chunk就將成為last remainder chunk。
下一塊為高地址,前一塊為低地址。
Glibc內存管理 華庭(庄明強)
❸ linux中進程的頁表是自動生成的嗎
是的,在進程clone的時候會復制進程頁表設置只讀,訪問的時候觸發缺頁中斷
❹ Linux存儲管理方式
這種方式中,將用戶程序的地址空間,注意,是 用戶程序的地址空間 分為若干個固定大小的區域,成為「頁」或「頁面」。我們可以知道,這也頁其實是不存在的,只是一種劃分內存空間的方法。也就是說,這種方式將用戶的程序 「肢解」 了,分成很多個小的部分,每個部分稱為一個「頁」。
將邏輯地址的前n位作為頁號,後面32-n位作為頁內偏移量。
由於進程的最後一頁經常裝不滿一個塊,從而形成了不可利用的碎片,稱之為 「頁內碎片」 。
作用:實現頁號到物理號的地址映射。
頁表是記錄邏輯空間(虛擬內存)中每一頁在內存中對應的物理塊號。但並非每一頁邏輯空間都會實際對應著一個物理塊,只有實際駐留在物理內存空間中的頁才會對應著物理塊。
系統會為每一個進程建立一張頁表,頁表是需要一直駐留在物理內存中的(多級頁表除外),另外頁表的起址和長度存放在 PCB(Process Control Block)進程式控制制結構體中。
可以在頁表的表項中設置相關的許可權控制欄位,例如設置存取控制欄位,用於保護該存儲塊的讀寫;若存取控制欄位為2位,則可以設置讀/寫、只讀和只執行等存取方式。
物理塊是實實在在存在於內存中的:
由於執行頻率高,要求效率比較高,需要使用硬體實現。
在系統中設置一個 頁表寄存器(PTR) ,其中存放頁表在內存的起始地址和頁表的長度。平時進程未執行的時候,頁表的起始地址和頁表長度放在本進程的PCB中。當調度程序調度到某個進程的時候,才將這兩個數據裝入 頁表寄存器 。
變換過程:
快表的變換機構
為了提高地址變換速度,可在地址變換機構中增設一個具有並行查詢能力的特殊高速緩沖寄存器,又稱為"聯想寄存器"或者「快表」。俗稱TLB。
快表與頁表的功能類似,其實就是將一部分頁表存到 CPU 內部的高速緩沖存儲器 Cache。CPU 定址時先到快表查詢相應的頁表項形成物理地址,如果查詢不到,則到內存中查詢,並將對應頁表項調入到快表中。但,如果快表的存儲空間已滿,則需要通過演算法找到一個暫時不再需要的頁表項,將它換出內存。
由於成本的關系,快表不可能做得很大,通常只存放 16~512 個頁表項,這對中、小型作業來說,已有可能把全部頁表項放在快表中;但對於大型作業而言,則只能將其一部分頁表項放入其中。由於對程序和數據的訪問往往帶有局限性,因此,據統計,從快表中能找到所需頁表項的概率可達 90% 以上。這樣,由於增加了地址變換機構而造成的速度損失可減少到 10% 以下,達到了可接受的程度。
我們可以採用這樣兩個方法來解決這一問題:
① 對於頁表所需的內存空間,可採用離散分配方式,以解決難以找到一塊連續的大內存空間的問題;
② 只將當前需要的部分頁表項調入內存,其餘的頁表項仍駐留在磁碟上,需要時再調入。
二級頁表的頁表項:
過程:
在採用兩級頁表結構的情況下,對於正在運行的進程,必須將其外層頁表調入內存,而對於內頁表則只需調入一頁或幾頁。為了表徵某頁的頁表是否已經調入內存,還應在外層頁表項中增設一個狀態位 S,其值若為 0,表示該頁表分頁不在內存中,否則說明其分頁已調入內存。進程運行時,地址變換機構根據邏輯地址中的 P1去查找外層頁表;若所找到的頁表項中的狀態位為 0,則產生一個中斷信號,請求 OS 將該頁表分頁調入內存。
多級頁表和二級頁表類似。多級頁表和二級頁表是為了節省物理內存空間。使得頁表可以在內存中離散存儲。(單級頁表為了隨機訪問必須連續存儲,如果虛擬內存空間很大,就需要很多頁表項,就需要很大的連續內存空間,但是多級頁表不需要。)
為什麼引入分段存儲管理?
引入效果:
它將用戶程序的地址空間分為若干個大小不同的的段,每個段可以定義一組完整的信息。
段號表示段名,每個段都從0開始編址,並且採用一段連續的地址空間。
在該地址結構中,允許一個作業最長有64K個段,每個段的最大長度為64KB。
在分段式存儲管理系統中,為每一個分段分配一個連續的分區。進程的各個段,可以離散地裝入內存中不同的分區中。
作用:實現從邏輯地址到物理內存區的映射。
為了保證程序能夠正常運行,就必須能夠從物理內存中找出每個邏輯段所對應的位置。為此在系統中會為每一個進程建立一張 段表 。每個段在表中有一個表項,其中記錄了該段在內存中的起始地址和段的長度。一般將段表保存在內存中。
在配置了段表之後,執行的過程可以通過查找段表,找到每一個段所對應的內存區。
為了實現進程從邏輯地址到物理地址的變換功能,在系統設置了段表寄存器,用於存放段表的起始地址和段表長度TL。
在進行地址變換時,系統將邏輯地址中的段號與段表長度TL 進行比較。若 S > TL,表示段號太大,是訪問越界,於是產生越界中斷信號。若未越界,則根據段表的始址和該段的段號,計算出該段對應段表項的位置,從中讀出該段在內存的起始地址。然後,再檢查段內地址 d 是否超過該段的段長 SL。若超過,即 d>SL,同樣發出越界中斷信號。若未越界,則將該段的基址 d 與段內地址相加,即可得到要訪問的內存。
分頁和分段系統相似之處:兩者都採用離散分配方式,且都是通過地址映射機構實現地址變換。
但在概念上兩者完全不同,主要表現在下述三個方面:
分頁系統以頁面作為內存分配的基本單位,能有效地提高內存利用率,而分段系統以段作為內存分配的基本單位,它能夠更好地滿足用戶多方面的需要。
段頁式地址結構由段號、段內頁號及頁內地址三部分所組成
段頁式系統的基本原理是分段和分頁原理的結合,即先將用戶程序分成若干個段,再把每個段分成若干個頁,並為每一個段賦予一個段名。如下圖展示了一個作業地址空間的結構。該作業有三個段:主程序段、子程序段和數據段;頁面大小為 4 KB:
在段頁式系統中,為了實現從邏輯地址到物理地址的變換,系統中需要同時配置段表和頁表。段表的內容與分段系統略有不同,它不再是內存始址和段長,而是頁表始址和頁表長度。下圖展示出了利用段表和頁表進行從用戶地址空間到物理(內存)空間的映射。
在段頁式系統中,為了便於實現地址變換,須配置一個段表寄存器,其中存放段表始址和段長 TL。進行地址變換時,首先利用段號 S,將它與段長 TL 進行比較。若 S < TL,表示未越界,於是利用段表始址和段號來求出該段所對應的段表項在段表中的位置,從中得到該段的頁表始址,並利用邏輯地址中的段內頁號 P 來獲得對應頁的頁表項位置,從中讀出該貝所在的物理塊號 b,再利用塊號 b 和頁內地址來構成物理地址。
在段頁式系統中,為了獲得一條指令或數據,須三次訪問內存。第一次訪問是訪問內存中的段表,從中取得頁表始址;第二次訪問是訪問內存中的頁表,從中取出該頁所在的物理塊號,並將該塊號與頁內地址一起形成指令或數據的物理地址;第三次訪問才是真正從第二次訪問所得的地址中取出指令或數據。
顯然,這使訪問內存的次數增加了近兩倍。為了提高執行速度,在地址變換機構中增設一個高速緩沖寄存器。每次訪問它時,都須同時利用段號和頁號去檢索高速緩存,若找到匹配的表項,便可從中得到相應頁的物理塊號,用來與頁內地址一起形成物理地址:若未找到匹配表項,則仍需第三次訪問內存。
參考鏈接:
❺ Linux 進程頁表存在哪裡
肯定是內核空間
❻ Linux進程內存管理方法
Linux系統提供了復雜的存儲管理系統,使得進程所能訪問的內存達到4GB。在Linux系統中,進程的4GB內存空間被分為兩個部分——用戶空間與內核空間。用戶空間的地址一般分布為0~3GB(即PAGE_OFFSET,在Ox86中它等於OxC0000000),這樣,剩下的3~4GB為內核空間,用戶進程通常只能訪問用戶空間的虛擬地址,不能訪問內核空間的虛擬地址。用戶進程只有通過系統調用(代表用戶進程在內核態執行)等方式才可以訪問到內核空間。每個進程的用戶空間都是完全獨立、互不相乾的,用戶進程各自有不同的頁表。而內核空間是由內核負責映射,它並不會跟著進程改變,是固定的。內核空間的虛擬地址到物理地址映射是被所有進程共享的,內核的虛擬空間獨立於其他程序。Linux中1GB的內核地址空間又被劃分為物理內存映射區、虛擬內存分配區、高端頁面映射區、專用頁面映射區和系統保留映射區這幾個區域。對於x86系統而言,一般情況下,物理內存映射區最大長度為896MB,系統的物理內存被順序映射在內核空間的這個區域中。當系統物理內存大於896MB時,超過物理內存映射區的那部分內存稱為高端內存(而未超過物理內存映射區的內存通常被稱為常規內存),內核在存取高端內存時必須將它們映射到高端頁面映射區。Linux保留內核空間最頂部FIXADDR_TOP~4GB的區域作為保留區。當系統物理內存超過4GB時,必須使用CPU的擴展分頁(PAE)模式所提供的64位頁目錄項才能存取到4GB以上的物理內存,這需要CPU的支持。加入了PAE功能的Intel Pentium Pro及以後的CPU允許內存最大可配置到64GB,它們具備36位物理地址空間定址能力。由此可見,對於32位的x86而言,在3~4GB之間的內核空間中,從低地址到高地址依次為:物理內存映射區隔離帶vmalloc虛擬內存分配器區隔離帶高端內存映射區專用頁面映射區保留區。
❼ linux kernel 內存管理-頁表、TLB
頁表用來把虛擬頁映射到物理頁,並且存放頁的保護位(即訪問許可權)。
在Linux4.11版本以前,Linux內核把頁表分為4級:
頁全局目錄表(PGD)、頁上層目錄(PUD)、頁中間目錄(PMD)、直接頁表(PT) 。
4.11版本把頁表擴展到5級,在頁全局目錄和頁上層目錄之間增加了 頁四級目錄(P4D) 。
各處處理器架構可以選擇使用5級,4級,3級或者2級頁表,同一種處理器在頁長度不同的情況可能選擇不同的頁表級數。可以使用配置宏CONFIG_PGTABLE_LEVELS配置頁表的級數,一般使用默認值。
如果選擇4級頁表,那麼使用PGD,PUD,PMD,PT;如果使用3級頁表,那麼使用PGD,PMD,PT;如果選擇2級頁表,那麼使用PGD和PT。 如果不使用頁中間目錄 ,那麼內核模擬頁中間目錄,調用函數pmd_offset 根據頁上層目錄表項和虛擬地址獲取頁中間目錄表項時 , 直接把頁上層目錄表項指針強制轉換成頁中間目錄表項 。
每個進程有獨立的頁表,進程的mm_struct實例的成員pgd指向頁全局目錄,前面四級頁表的表項存放下一級頁表的起始地址,直接頁表的頁表項存放頁幀號(PFN) 。
內核也有一個頁表, 0號內核線程的進程描述符init_task的成員active_mm指向內存描述符init_mm,內存描述符init_mm的成員pgd指向內核的頁全局目錄swapper_pg_dir 。
ARM64處理器把頁表稱為轉換表,最多4級。ARM64處理器支持三種頁長度:4KB,16KB,64KB。頁長度和虛擬地址的寬度決定了轉換表的級數,在虛擬地址的寬度為48位的條件下,頁長度和轉換表級數的關系如下所示:
ARM64處理器把表項稱為描述符,使用64位的長描述符格式。描述符的0bit指示描述符是不是有效的:0表示無效,1表示有效。第1位指定描述符類型。
在塊描述符和頁描述符中,內存屬性被拆分為一個高屬性和一個低屬性塊。
處理器的MMU負責把虛擬地址轉換成物理地址,為了改進虛擬地址到物理地址的轉換速度,避免每次轉換都需要查詢內存中的頁表,處理器廠商在管理單元里加了稱為TLB的高速緩存,TLB直譯為轉換後備緩沖區,意譯為頁表緩存。
頁表緩存用來緩存最近使用過的頁表項, 有些處理器使用兩級頁表緩存 : 第一級TLB分為指令TLB和數據TLB,好處是取指令和取數據可以並行;第二級TLB是統一TLB,即指令和數據共用的TLB 。
不同處理器架構的TLB表項的格式不同。ARM64處理器的每條TLB表項不僅包含虛擬地址和物理地址,也包含屬性:內存類型、緩存策略、訪問許可權、地址空間標識符(ASID)和虛擬機標識符(VMID)。 地址空間標識符區分不同進程的頁表項 , 虛擬機標識符區分不同虛擬機的頁表項 。
如果內核修改了可能緩存在TLB裡面的頁表項,那麼內核必須負責使舊的TLB表項失效,內核定義了每種處理器架構必須實現的函數。
當TLB沒有命中的時候,ARM64處理器的MMU自動遍歷內存中的頁表,把頁表項復制到TLB,不需要軟體把頁表項寫到TLB,所以ARM64架構沒有提供寫TLB的指令。
為了減少在進程切換時清空頁表緩存的需要,ARM64處理器的頁表緩存使用非全局位區分內核和進程的頁表項(nG位為0表示內核的頁表項), 使用地址空間標識符(ASID)區分不同進程的頁表項 。
ARM64處理器的ASID長度是由具體實現定義的,可以選擇8位或者16位。寄存器TTBR0_EL1或者TTBR1_EL1都可以用來存放當前進程的ASID,通常使用寄存器TCR_EL1的A1位決定使用哪個寄存器存放當前進程的ASID,通常使用寄存器 TTBR0_EL1 。寄存器TTBR0_EL1的位[63:48]或者[63:56]存放當前進程的ASID,位[47:1]存放當前進程的頁全局目錄的物理地址。
在SMP系統中,ARM64架構要求ASID在處理器的所有核是唯一的。假設ASID為8位,ASID只有256個值,其中0是保留值,可分配的ASID范圍1~255,進程的數量可能超過255,兩個進程的ASID可能相同,內核引入ASID版本號解決這個問題。
(1)每個進程有一個64位的軟體ASID, 低8位存放硬體ASID,高56位存放ASID版本號 。
(2) 64位全局變數asid_generation的高56位保存全局ASID版本號 。
(3) 當進程被調度時,比較進程的ASID版本號和全局版本號 。如果版本號相同,那麼直接使用上次分配的ASID,否則需要給進程重新分配硬體ASID。
存在空閑ASID,那麼選擇一個分配給進程。不存在空閑ASID時,把全局ASID版本號加1,重新從1開始分配硬體ASID,即硬體ASID從255回繞到1。因為剛分配的硬體ASID可能和某個進程的ASID相同,只是ASID版本號不同,頁表緩存可能包含了這個進程的頁表項,所以必須把所有處理器的頁表緩存清空。
引入ASID版本號的好處是:避免每次進程切換都需要清空頁表緩存,只需要在硬體ASID回環時把處理器的頁表緩存清空 。
虛擬機裡面運行的客戶操作系統的虛擬地址轉物理地址分兩個階段:
(1) 把虛擬地址轉換成中間物理地址,由客戶操作系統的內核控制 ,和非虛擬化的轉換過程相同。
(2) 把中間物理地址轉換成物理地址,由虛擬機監控器控制 ,虛擬機監控器為每個虛擬機維護一個轉換表,分配一個虛擬機標識符,寄存器 VTTBR_EL2 存放當前虛擬機的階段2轉換表的物理地址。
每個虛擬機有獨立的ASID空間 ,頁表緩存使用 虛擬機標識符 區分不同虛擬機的轉換表項,避免每次虛擬機切換都要清空頁表緩存,在虛擬機標識符回繞時把處理器的頁表緩存清空。