linux內存池
㈠ 操作系統真象還原怎麼樣,操作系統真象還原好不好
當人按下筆記本開機鍵時.cpu的cs寄存器(基址)跟ip(偏移量)寄存器加電.被強制初始化為(jmp xxx:xxx) 跳轉到bios所在的地址.
接著bios開機自檢(這個不需要了解,只需了解最後跳轉到0x7c00處即可.對於寫kernel的人來說也是透明的.除非你是寫bios的).它將自動從0盤1扇區載入mbr(主引導程序,512位元組必須是以0x55,0xaa結尾..如果不夠,可以用times 510-($-$$) db 0.很多都是這樣定義的.) 載入到0x7c00處.因為bios執行的最後一條指令時jmp 0x7c00處.
緊接著用匯編編寫mbr.. Mbr的功能是也是從硬碟讀取loader(載入內核的程序). Loader存放的位置跟載入到的內存地址由自己決定...mbr就是根據硬碟提供的介面(,0x1f2~0x1f7)讀取內容.movsb之類的指令復制到內存.最後一個jmp指令跳轉到loader載入的地址
(注:以上的地址都是物理地址)
緊接著編寫loader程序(載入內核程序).在loader程序中.我打算通過bios中斷獲取整個計算機的物理內存.並且進入保護模式.因此在loader.S中..我要定義代碼段,跟數據段.顯示段(顯示字元.一般是0xb8000位置.)還是定義頁表(採用二級頁表
--------------------------------------------------------------------
數據段的內容是定義
全局描述符表跟代碼段數據段顯示段描述符..還有定義各種段選擇子.位置自己定義.(這里我深深體會到了intel為了跟前代保持兼容多麼拼了..定義的描述符真的好惡心.....的說
代碼段:
通過int 15h中斷獲取物理內存.保存在某一固定物理位置.緊接著打開A20 Gate.載入gdt(全局描述符表).將cpu的cr0的pe位置置為1(由此進入保護模式,cs,ds的值不再是段基址.而是段選擇子)
緊接著將各種段選擇子復制到段寄存器中(保護模式)................緊接著..就開始創建二級頁表(位置.自己固定.不過我看知乎上..一般是在1m物理內存以下).. .創建好以後.用sgdt將描述符表地址(現在是物理地址)載入到寄存器中(注意在創建頁目錄表的時候...一般我看知乎是將地址映射到0xc000000處.(windows是採用這地址)........開啟映射前後....內核的虛擬地址(一個原本的物理地址變成了線性地址),還有0xc000000..這2個線性地址都要映射到1m以下物理內存.等於768頁目錄跟0頁目錄(定址方式.一個虛擬地址的前10位為頁目錄表索引.中間10位為頁表索引.最後10位為頁框偏移量)都指向一個頁表.
緊接著棧指針也加上0xc000000處...為了映射).緊接著將cr0寄存器的31位置為1,表示開啟分頁機制..注意之前的載入的gdt是物理地址.現在改為線性地址.需要重新載入(其實就是原來的地址+0xc000000處..一個lgdt[gdt_ptr].ok...最後.......跳轉到內核所在的地址(自己定義內核所在的地址).......內核編寫需要了解elf(格式.推薦<<程序員的自我修養>>..我沒看..只是網友推薦罷了).定義elf頭.程序頭表.節頭表.內核大小偏移量.等這些定義好了後.就可以載入內核了.一jmp指令就搞定了.....其實載入的內核一開始就是int main(void){while(1); return 0}....沒辦法.啥系統調用都沒實現.啥都要自己實現....沒辦法...輪子是一步一步造出來的嘛.
保護模式的話.主要體現在cpu的特權級...有了操作系統後.其實一個程序分為用戶部分跟內核部分(許可權不同..一般用戶部分是3特權級,0是最高特權級.還有IO特權級這個就懶得解釋了都一樣.哪些埠可以訪問許可權).........主要從低特權級轉移到高特權級(用戶態到內核態轉換)只能通過各種門(任務們,調用門.陷阱門.中斷門等) 條件.訪問門時 cpl<=dpl[門] &cpl>=dpl[段] 這個表示r3可以使用內核服務訪問段max(cpl,rpl)<=dpl[段] 這樣就可以訪問段. 從高特權級向低特權級返回的話.用ret彈出ip.或者retf cs:ip.......
PS:門描述符也是定義在GDT中.Call 調用門選擇子.參數就是選擇子.選擇子找到門描述符.結果門描述符定義了代碼段的基址(段選擇子)跟偏移量..接著..找到內核常式.............坑爹.....對了..從用戶態到內核態.所用的代碼段跟數據段許可權是不同的.so.所對應的棧也不同.有個TSS(任務狀態段就是用來保存上下文的..其實一般只保存ss:sp... TSS效率比較低.linux只用了一個TSS並不切換).
緊接著要了解函數調用約定.查了wiki.c語言用的是cdecl約定(eax,edx,ecx由調用者保存.其他是被調用者保存.返回值在eax中.由調用者清理棧空間(就是esp+xx.實際不能說清理),參數壓棧從右到左.例子自己google查吧
緊接著...卧槽..總該用c語言了吧.....之前一直用匯編..開始實現列印hello os.然後就....想順便學學內聯匯編..結果我了個大去...內聯匯編是.AT&T匯編語法跟x86匯編有很大不同.沒辦法.....google對比下.一個一個指令弄唄......接著..想實現一個printf函數..除了 bios中斷....算了..自己實現..一查顯卡埠控制..我勒個去..嚇得半死..VGA寄存器..一大堆..從0懶得實現了......從github抄了別人實現可以游標跟蹤的輸出函數....不過也懂得其原理.一個讀入寫出寄存器一個控制寄存器...忘了....不過不重要.略過
緊接著實現中斷......哥現在理解的所謂操作系統就是由中斷驅動的.所謂的時間片不過是(時間中斷產生的間隔時間罷了),等於躺著被調用的代碼....中斷根據網上資料cpu提供統一的介面為中斷信號的公共路線..畢竟沒則么多引腳.分為INTR跟NMI(災難性錯誤).只管INTR就可以了.編寫中斷向量表(IDT).並且要保存在IDTR寄存器中.一般發出的信號就是中斷向量號.這個號就是中斷描述符表的索引,加上IDTR得到某中斷描述符具體位置.接著.中斷描述符保存中斷處理程序的選擇子跟偏移量..接著道GDT中找.到代碼段的起始地址再加上偏移量(保存在IDT描述符中).最後還要了解8259A(外設的可屏蔽中斷的代理.包括鍵盤.時間中斷).編程設置它初始化.設置主片蔥片級聯方式........最後編寫中斷處理程序(參考 unix v6實現)..最後時間中斷....網上一般採用計數器8253設置時間中斷發生的頻率.提速降速.隨你便.2333333 接下來就是哥認為最興奮的一筆.內存管理.通過點陣圖映射來管理內存.點陣圖的每一位映射一個4k大小的頁.0表示該頁未被使用.1表示已經被使用..點陣圖相關處理函數寫好後.正式開始規劃內存了..
內存池的劃分參考知乎某大牛說的答案.內存池分為虛擬內存池跟物理內存池.虛擬內存池管理虛擬地址.物理內存管理物理地址.......................我的話把物理內存一半給用戶,一半給內核........虛擬內存池(每個用戶進程都有一個).但內核虛擬地址只有一個(因為內核只有一個.對不對).有關點陣圖的位置就是自己定義了.......虛擬地址映射物理地址.完成此操作就OK了(不過這里還沒實現用戶進程.so.只完成了內核相關虛擬地址映射.).其實malloc的..就是用鏈表管理更小的內存塊.我這里最小的內存塊...是4k...太大了..要很小(16位元組.32位元組.64位元組.等).用內存塊描述符管理更小相同規格的內存塊.用塊描述符鏈表將整個鏈接起來.
接下來實現線程..線程哎..說實話..也沒啥.就一個執行流(有自己的棧.寄存器映像)..不過只能使用進程的虛擬地址(沒資源).沒啥很高深的..總之進程跟線程就是人為搞的代碼塊.各種狀態也是人自己劃分的.自圓其說.2333333333333.(注:我實現的是內核線程.不然..還要自己實現.用戶線程調度..太麻煩了.不然這些都交給內核調度豈不美哉.
一開始呢.要實現PCB(不然..你的上下文保存在哪?.TSS,cpu只自動獲取ss:sp,需要自己手動壓入當前各種寄存器狀態.因為如果用TSS(在內存)切換效率相當低.so.基本linux也不用TSS).TCB分為2部分(一個是被中斷打斷的棧空間.一個是線程自己空間,說明一點.一般發現線程基本都是個函數.函數的地址....嘿嘿沒錯就是當運行這個線程時cs:ip指向函數地址.就通過賦值語句.)忘說了.pcb需要佔用一頁大小內存.so.從內核內存池中申請一頁大小.)...有關內核調度.只不過時間中斷處理程序調用了scele函數(開始要判斷是否該線程時間片用完了沒).........用鏈表管理就緒隊列.跟總隊列..其實我搞的這簡單內核最復雜不過是用來鏈表罷了.所以寫個簡單內核..初中生差不多都可以完成.當然linus那種大神....不是一般人可以達到的.
最後說說鎖...保證原子性(不能被打斷).其實最基本的就是一條關中斷cli匯編指令.實現的.只不過封裝了一層..哎.....沒想到這么簡單吧
最後說說系統調用(參考linux最初版本,實現的這功能其實現在已經被棄用了)...mov eax,子功能號. Int 號碼.調用syscall_table[eax].(其中system_table保存的是子功能的函數名(地址)........)...最後就是無聊的抄...系統調用實現的代碼了.............
最後要實現用戶進程.用戶進程最主要區別就是特權級是最低特權級3.還有有自己的虛擬地址.流程(向內核申請一頁大小.創建pcb.初始化pcb.創建管理虛擬地址用戶點陣圖...緊接著創建線程...線程中創建頁目錄表.添加到就緒隊列中...等待調度......這里最關鍵是要讓用戶進程在特權級3下運行..so.其用戶進程所使用的代碼段跟數據段都必須是3特權級......完結.
參考<<一個操作系統的實現>>,<<x86從實模式到保護模式>>.<<現代操作系統>>
(上面其實已經寫的省略了很多.這是我一口氣寫完的..可能忘了很多....如果有啥不對.請求指正)
文件系統跟shell部分.(...文件系統打算照搬unix v6的..我讀都沒讀完..shell參考學院的教材(unix/linux編程實踐)......
好了.很慚愧.只做了一點微小的工作.謝謝大家.
㈡ 研究生課題 linux內核怎麼樣 2015
圖2-1顯示了基於x86計算機Linux系統的啟動順序。第一步是BIOS從啟動設備中導入主引導記錄(MBR),接下來MBR中的代碼查看分區表並從活動分區讀取GRUB、LILO或SYSLINUX等引導裝入程序,之後引導裝入程序會載入壓縮後的內核映像並將控制權傳遞給它。內核取得控制權後,會將自身解壓縮並投入運轉。
基於x86的處理器有兩種操作模式:實模式和保護模式。在實模式下,用戶僅可以使用1 MB內存,並且沒有任何保護。保護模式要復雜得多,用戶可以使用更多的高級功能(如分頁)。CPU 必須中途將實模式切換為保護模式。但是,這種切換是單向的,即不能從保護模式再切換回實模式。
內核初始化的第一步是執行實模式下的匯編代碼,之後執行保護模式下init/main.c文件(上一章修改的源文件)中的 start_kernel()函數。start_kernel()函數首先會初始化CPU子系統,之後讓內存和進程管理系統就位,接下來啟動外部匯流排和 I/O設備,最後一步是激活初始化(init)程序,它是所有Linux進程的父進程。初始化進程執行啟動必要的內核服務的用戶空間腳本,並且最終派生控制台終端程序以及顯示登錄(login)提示。
圖2-1 基於x86硬體上的Linux的啟動過程
本節內的3級標題都是圖2-2中的一條列印信息,這些信息來源於基於x86的筆記本 電腦的Linux啟動過程。如果在其他體系架構上啟動內核,消息以及語義可能會有所不同。
2.1.1 BIOS-provided physical RAM map
內核會解析從BIOS中讀取到的系統內存映射,並率先將以下信息列印出來:
BIOS-provided physical RAM map:
BIOS-e820: 0000000000000000 - 000000000009f000 (usable)
...
BIOS-e820: 00000000ff800000 - 0000000100000000 (reserved)
實模式下的初始化代碼通過使用BIOS的int 0x15服務並執行0xe820號函數(即上面的BIOS-e820字元串)來獲得系統的內存映射信息。內存映射信息中包含了預留的和可用的內存,內核將隨後使用這些信息創建其可用的內存池。在附錄B的B.1節,我們會對BIOS提供的內存映射問題進行更深入的講解。
圖2-2 內核啟動信息
2.1.2 758MB LOWMEM available
896 MB以內的常規的可被定址的內存區域被稱作低端內存。內存分配函數kmalloc()就是從該區域分配內存的。高於896 MB的內存區域被稱為高端內存,只有在採用特殊的方式進行映射後才能被訪問。
在啟動過程中,內核會計算並顯示這些內存區內總的頁數。
2.1.3 Kernel command line: ro root=/dev/hda1
Linux的引導裝入程序通常會給內核傳遞一個命令行。命令行中的參數類似於傳遞給C程序中main()函數的argv[]列表,唯一的不同在於它們是傳遞給內核的。可以在引導裝入程序的配置文件中增加命令行參數,當然,也可以在運行過程中修改引導裝入程序的提示行[1]。如果使用的是GRUB 這個引導裝入程序,由於發行版本的不同,其配置文件可能是/boot/grub/grub.conf或者是/boot/grub/menu.lst。如果使用的是LILO,配置文件為/etc/lilo.conf。下面給出了一個grub.conf文件的例子(增加了一些注釋),看了緊接著title kernel 2.6.23的那行代碼之後,你會明白前述列印信息的由來。
default 0 #Boot the 2.6.23 kernel by default
timeout 5 #5 second to alter boot order or parameters
title kernel 2.6.23 #Boot Option 1
#The boot image resides in the first partition of the first disk
#under the /boot/ directory and is named vmlinuz-2.6.23. 'ro'
#indicates that the root partition should be mounted read-only.
kernel (hd0,0)/boot/vmlinuz-2.6.23 ro root=/dev/hda1
#Look under section "Freeing initrd memory:387k freed"
initrd (hd0,0)/boot/initrd
#...
命令行參數將影響啟動過程中的代碼執行路徑。舉一個例子,假設某命令行參數為bootmode,如果該參數被設置為1,意味著你希望在啟動過程中列印一些調試信息並在啟動結束時切換到runlevel的第3級(初始化進程的啟動信息列印後就會了解runlevel的含義);如果bootmode 參數被設置為0,意味著你希望啟動過程相對簡潔,並且設置runlevel為2。既然已經熟悉了init/main.c文件,下面就在該文件中增加如下修改:
static unsigned int bootmode = 1 ;
static int __init
is_bootmode_setup( char * str)
{
get_option( & str, & bootmode);
return 1 ;
}
/* Handle parameter "bootmode=" */
__setup( " bootmode= " , is_bootmode_setup);
if (bootmode) {
/* Print verbose output */
/* ... */
}
/* ... */
/* If bootmode is 1, choose an init runlevel of 3, else
switch to a run level of 2 */
if (bootmode) {
argv_init[ ++ args] = " 3 " ;
} else {
argv_init[ ++ args] = " 2 " ;
}
/* ... */
請重新編譯內核並嘗試運行新的修改。
2.1.4 Calibrating delay...1197.46 BogoMIPS (lpj=2394935)
在啟動過程中,內核會計算處理器在一個jiffy時間內運行一個內部的延遲循環的次數。jiffy的含義是系統定時器2個連續的節拍之間的間隔。正如所料,該計算必須被校準到所用CPU的處理速度。校準的結果被存儲 在稱為loops_per_jiffy的內核變數中。使用loops_per_jiffy的一種情況是某設備驅動程序希望進行小的微秒級別的延遲的時候。
為了理解延遲—循環校準代碼,讓我們看一下定義於init/calibrate.c文件中的calibrate_ delay()函數。該函數靈活地使用整型運算得到了浮點的精度。如下的代碼片段(有一些注釋)顯示了該函數的開始部分,這部分用於得到一個 loops_per_jiffy的粗略值:
loops_per_jiffy = ( 1 << 12 ); /* Initial approximation = 4096 */
printk(KERN_DEBUG 「Calibrating delay loop...「);
while ((loops_per_jiffy <<= 1 ) != 0 ) {
ticks = jiffies; /* As you will find out in the section, 「Kernel
Timers," the jiffies variable contains the
number of timer ticks since the kernel
started, and is incremented in the timer
interrupt handler */
while (ticks == jiffies); /* Wait until the start of the next jiffy */
ticks = jiffies;
/* Delay */
__delay(loops_per_jiffy);
/* Did the wait outlast the current jiffy? Continue if it didn't */
ticks = jiffies - ticks;
if (ticks) break ;
}
loops_per_jiffy >>= 1 ; /* This fixes the most significant bit and is
the lower-bound of loops_per_jiffy */
上述代碼首先假定loops_per_jiffy大於4096,這可以轉化為處理器速度大約為每秒100萬條指令,即1 MIPS。接下來,它等待jiffy被刷新(1個新的節拍的開始),並開始運行延遲循環__delay(loops_per_jiffy)。如果這個延遲循環持續了1個jiffy以上,將使用以前的loops_per_jiffy值(將當前值右移1位)修復當前loops_per_jiffy的最高位;否則,該函數繼續通過左移loops_per_jiffy值來探測出其最高位。在內核計算出最高位後,它開始計算低位並微調其精度:
loopbit = loops_per_jiffy;
/* Graally work on the lower-order bits */
while (lps_precision -- && (loopbit >>= 1 )) {
loops_per_jiffy |= loopbit;
ticks = jiffies;
while (ticks == jiffies); /* Wait until the start of the next jiffy */
ticks = jiffies;
/* Delay */
__delay(loops_per_jiffy);
if (jiffies != ticks) /* longer than 1 tick */
loops_per_jiffy &= ~ loopbit;
}
上述代碼計算出了延遲循環跨越jiffy邊界時loops_per_jiffy的低位值。這個被校準的值可被用於獲取BogoMIPS(其實它是一個並非科學的處理器速度指標)。可以使用BogoMIPS作為衡量處理器運行速度的相對尺度。在1.6G Hz 基於Pentium M的筆記本 電腦上,根據前述啟動過程的列印信息,循環校準的結果是:loops_per_jiffy的值為2394935。獲得BogoMIPS的方式如下:
BogoMIPS = loops_per_jiffy * 1秒內的jiffy數 * 延遲循環消耗的指令數(以百萬為單位)
= ( 2394935 * HZ * 2 ) / ( 1000000 )
= ( 2394935 * 250 * 2 ) / ( 1000000 )
= 1197.46 (與啟動過程列印信息中的值一致)
在2.4節將更深入闡述jiffy、HZ和loops_per_jiffy。
2.1.5 Checking HLT instruction
由於Linux內核支持多種硬體平台,啟動代碼會檢查體系架構相關的bug。其中一項工作就是驗證停機(HLT)指令。
x86處理器的HLT指令會將CPU置入一種低功耗睡眠模式,直到下一次硬體中斷發生之前維持不變。當內核想讓CPU進入空閑狀態時(查看 arch/x86/kernel/process_32.c文件中定義的cpu_idle()函數),它會使用HLT指令。對於有問題的CPU而言,命令行參數no-hlt可以禁止HLT指令。如果no-hlt被設置,在空閑的時候,內核會進行忙等待而不是通過HLT給CPU降溫。
當init/main.c中的啟動代碼調用include/asm-your-arch/bugs.h中定義的check_bugs()時,會列印上述信息。
2.1.6 NET: Registered protocol family 2
Linux套接字(socket)層是用戶空間應用程序訪問各種網路 協議的統一介面。每個協議通過include/linux/socket.h文件中定義的分配給它的獨一無二的系列號注冊。上述列印信息中的Family 2代表af_inet(互聯網協議)。
啟動過程中另一個常見的注冊協議系列是AF_NETLINK(Family 16)。網路鏈接套接字提供了用戶進程和內核通信 的方法。通過網路鏈接套接字可完成的功能還包括存取路由表和地址解析協議(ARP)表(include/linux/netlink.h文件給出了完整的用法列表)。對於此類任務而言,網路鏈接套接字比系統調用更合適,因為前者具有採用非同步機制、更易於實現和可動態鏈接的優點。
內核中經常使能的另一個協議系列是AF_Unix或Unix-domain套接字。X Windows等程序使用它們在同一個系統上進行進程間通信。
2.1.7 Freeing initrd memory: 387k freed
initrd是一種由引導裝入程序載入的常駐內存的虛擬磁碟映像。在內核啟動後,會將其掛載為初始根文件系統,這個初始根文件系統中存放著掛載實際根文件系統磁碟分區時所依賴的可動態連接的模塊。由於內核可運行於各種各樣的存儲控制器硬體平台上,把所有可能的磁碟驅動程序都直接放進基本的內核映像中並不可行。你所使用的系統的存儲設備的驅動程序被打包放入了initrd中,在內核啟動後、實際的根文件系統被掛載之前,這些驅動程序才被載入。使用 mkinitrd命令可以創建一個initrd映像。
2.6內核提供了一種稱為initramfs的新功能,它在幾個方面較initrd更為優秀。後者模擬了一個磁碟(因而被稱為 initramdisk或initrd),會帶來Linux塊I/O子系統的開銷(如緩沖);前者基本上如同一個被掛載的文件系統一樣,由自身獲取緩沖 (因此被稱作initramfs)。
不同於initrd,基於頁緩沖建立的initramfs如同頁緩沖一樣會動態地變大或縮小,從而減少了其內存消耗。另外,initrd要求你的內核映像包含initrd所使用的文件系統(例如,如果initrd為EXT2文件系統,內核必須包含EXT2驅動程序),然而initramfs不需要文件系統支持。再者,由於initramfs只是頁緩沖之上的一小層,因此它的代碼量很小。
用戶可以將初始根文件系統打包為一個cpio壓縮包[1],並通過initrd=命令行參數傳遞給內核。當然,也可以在內核配置過程中通過 INITRAMFS_SOURCE選項直接編譯進內核。對於後一種方式而言,用戶可以提供cpio壓縮包的文件名或者包含initramfs的目錄樹。在啟動過程中,內核會將文件解壓縮為一個initramfs根文件系統,如果它找到了/init,它就會執行該頂層的程序。這種獲取初始根文件系統的方法對於嵌入式系統而言特別有用,因為在嵌入式系統中系統資源非常寶貴。使用mkinitramfs可以創建一個initramfs映像,查看文檔 Documentation/filesystems/ramfs- rootfs-initramfs.txt可獲得更多信息。
在本例中,我們使用的是通過initrd=命令行參數向內核傳遞初始根文件系統cpio壓縮包的方式。在將壓縮包中的內容解壓為根文件系統後,內核將釋放該壓縮包所佔據的內存(本例中為387 KB)並列印上述信息。釋放後的頁面會被分發給內核中的其他部分以便被申請。
在嵌入式系統開發過程中,initrd和initramfs有時候也可被用作嵌入式設備上實際的根文件系統。
2.1.8 io scheler anticipatory registered (default)
I/O調度器的主要目標是通過減少磁碟的定位次數來增加系統的吞吐率。在磁碟定位過程中,磁頭需要從當前的位置移動到感興趣的目標位置,這會帶來一定的延遲。2.6內核提供了4種不同的I/O調度器:Deadline、Anticipatory、Complete Fair Queuing以及NOOP。從上述內核列印信息可以看出,本例將Anticipatory 設置為了默認的I/O調度器。
2.1.9 Setting up standard PCI resources
啟動過程的下一階段會初始化I/O匯流排和外圍控制器。內核會通過遍歷PCI匯流排來探測PCI硬體,接下來再初始化其他的I/O子系統。從圖2-3中我們會看到SCSI子系統、USB控制器、視頻 晶元(855北橋晶元組信息中的一部分)、串列埠(本例中為8250 UART)、PS/2鍵盤 和滑鼠 、軟碟機 、ramdisk、loopback設備、IDE控制器(本例中為ICH4南橋晶元組中的一部分)、觸控板、乙太網控制器(本例中為e1000)以及PCMCIA控制器初始化的啟動信息。圖2-3中 符號指向的為I/O設備的標識(ID)。
圖2-3 在啟動過程中初始化匯流排和外圍控制器
本書會以單獨的章節討論大部分上述驅動程序子系統,請注意如果驅動程序以模塊的形式被動態鏈接到內核,其中的一些消息也許只有在內核啟動後才會被顯示。
2.1.10 EXT3-fs: mounted filesystem
EXT3文件系統已經成為Linux事實上的文件系統。EXT3在退役的EXT2文件系統基礎上增添了日誌層,該層可用於崩潰後文件系統的快速恢復。它的目標是不經由耗時的文件系統檢查(fsck)操作即可獲得一個一致的文件系統。EXT2仍然是新文件系統的工作引擎,但是EXT3層會在進行實際的磁碟改變之前記錄文件交互的日誌。EXT3向後兼容於EXT2,因此,你可以在你現存的EXT2文件系統上加上EXT3或者由EXT3返回到EXT2 文件系統。
EXT3會啟動一個稱為kjournald的內核輔助線程(在接下來的一章中將深入討論內核線程)來完成日誌功能。在EXT3投入運轉以後,內核掛載根文件系統並做好「業務」上的准備:
EXT3-fs: mounted filesystem with ordered data mode
kjournald starting. Commit interval 5 seconds
VFS: Mounted root (ext3 filesystem).
2.1.11 INIT: version 2.85 booting
所有Linux進程的父進程init是內核完成啟動序列後運行的第1個程序。在init/main.c的最後幾行,內核會搜索一個不同的位置以定位到init:
if (ramdisk_execute_command) { /* Look for /init in initramfs */
run_init_process(ramdisk_execute_command);
}
if (execute_command) { /* You may override init and ask the kernel
to execute a custom program using the
"init=" kernel command-line argument. If
you do that, execute_command points to the
specified program */
run_init_process(execute_command);
}
/* Else search for init or sh in the usual places .. */
run_init_process( " /sbin/init " );
run_init_process( " /etc/init " );
run_init_process( " /bin/init " );
run_init_process( " /bin/sh " );
panic( " No init found. Try passing init= option to kernel. " );
init會接受/etc/inittab的指引。它首先執行/etc/rc.sysinit中的系統初始化腳本,該腳本的一項最重要的職責就是激活對換(swap)分區,這會導致如下啟動信息被列印:
Adding 1552384k swap on /dev/hda6
讓我們來仔細看看上述這段話的意思。Linux用戶進程擁有3 GB的虛擬地址空間(見2.7節),構成「工作集」的頁被保存在RAM中。但是,如果有太多程序需要內存資源,內核會釋放一些被使用了的RAM頁面並將其存儲到稱為對換空間(swap space)的磁碟分區中。根據經驗法則,對換分區的大小應該是RAM的2倍。在本例中,對換空間位於/dev/hda6這個磁碟分區,其大小為1 552 384 KB。
接下來,init開始運行/etc/rc.d/rcX.d/目錄中的腳本,其中X是inittab中定義的運行級別。runlevel是根據預期的工作模式所進入的執行狀態。例如,多用戶文本模式意味著runlevel為3,X Windows則意味著runlevel為5。因此,當你看到INIT: Entering runlevel 3這條信息的時候,init就已經開始執行/etc/rc.d/rc3.d/目錄中的腳本了。這些腳本會啟動動態設備命名子系統(第4章中將討論 udev),並載入網路、音頻、存儲設備等驅動程序所對應的內核模塊:
Starting udev: [ OK ]
Initializing hardware... network audio storage [Done]
...
最後,init發起虛擬控制台終端,你現在就可以登錄了。
㈢ linux內存池能分配連續物理內存嗎
處理器通過地址訪問內存單元,程序中用到的基址加偏移地址是線性地址,需要通過MMU將虛擬地址映射成物理地址。這給分配和釋放內存帶來方便:1)物理地址不連續的空間可以映射為邏輯上連續的虛擬地址。2)進程可以獲得比實際內存大的"空間",虛擬內存使得進程在這種情況下仍可正常運行。
linux內核為驅動程序提供了一致的內存管理介面,因此不用考慮不同體系結構如何管理內存的。
在linux內核中分配內存用kmalloc和kfree。
kmalloc分配時可以被阻塞,且不對所獲得的區域清零。它分配的區域在物理內存中也是連續的。
原型:
#include<linux/slab.h>
void *kmalloc(size_t size,int flags); //參數為分配大小及分配標志
flags參數:
GFP_KERNEL:內核內存通用分配方法,表示內存分配是由運行在內核空間的進程執行的。可休眠,所以使用GFP_KERNEL分配內存的函數必須是可重入的。
GFP_ATOMIC:用於在中斷處理常式或者運行在進程上下文之外的代碼中分配內存,不可休眠。內核通常會為原子性的分配預留一些空閑頁面。
所有標志定義在 <linux/gfp.h>中。
size參數:
內核是基於頁技術分配內存,以最佳的利用系統的RAM。
linux處理內存分配的方法是:創建一系列的內存對象池,每個池的內存大小事固定的,處理分配請求時,就直接在包含足夠大的內存塊中傳遞一個整款給請求者。內核只能分配一些預定義的固定大小的位元組數組。kmalloc能處理的的最小內存塊是32或者64,不大於128KB。
內存區段:
linux內核把內存分為3個區段:可用於DMA的內存,常規內存以及高端內存。kmalloc不能分配高端內存。內存區段在mm/page_alloc.c中實現。區段的初始化在對應的arch樹下的mm/init.c中。
後備高速緩存 (lookaside cache)
內核中普通對象進行初始化所需的時間超過了對其進行分配和釋放所需的時間,因此不應該將內存釋放回一個全局的內存池,而是將內存保持為針對特定目而初始化的狀態。例如,如果內存被分配給了一個互斥鎖,那麼只需在為互斥鎖首次分配內存時執行一次互斥鎖初始化函數(mutex_init)即可。後續的內存分配不需要執行這個初始化函數,因為從上次釋放和調用析構之後,它已經處於所需的狀態中了。
linux2.6中USB和SCSI驅動程序使用了這種高速緩存,是為一些反復使用的塊增加某些特殊的內存池。後背高速緩存管理也叫slab分配器,相關函數和類型在<linux/slab.h>中申明。
slab分配器實現高速緩存具有kmem_cache_t類型。
kmem_cache_t * kmem_cache_create( const char *name, size_t size, size_t align,
unsigned long flags;
void (*constructor)(void*,kmem_cache_t *, unsigned long),
void (*destructor)(void*, kmem_cache_t *, unsigned long));
用於創建一個新的高速緩存對象。
constructor用於初始化新分配的對象,destructor用於清除對象。
一旦某個對象的高速緩存被創建以後,就可以調用kmem_cache_alloc從中分配內存對象。
void * kmem_cache_alloc(kmem_cache_t *cache,int flags);
釋放內存對象使用kmem_cache_free
void kmem_cache_free(kmem_cache_t *cache,const void *obj);
在內存空間都被釋放後,模塊被卸載前,驅動程序應當釋放他的高速緩存。
int kmem_cache_destory(kmem_cache_t *cache);
要檢查其返回狀態,如果失敗,表明莫塊中發生了內存泄露。
基於slab的高速緩存scullc
kmem_cache_t *scullc_cache;
scullc_cache=kmem_cache_creat("scullc",scullc_quantum,0,SLAB_HWCACHE_ALIGN,NULL,NULL);
if(!scullc_cache)
{
scullc_cleanup();
return -ENOMEM;
}
if(!dpte->data[s_pos])
{
dptr->data[s_pos]=kmem_cache_alloc(scullc_cache,GFP_KERNEL);
if(!dptr->data[s_pos])
goto nomem;
memset(dptr->data[s_pos],0,scullc_quantum);
}
for(i=0;i<qset;i++)
{
if(dptr->data[i])
kmem_cache_free(scullc_cache,dptr->data[i]);
}
if(scullc_cache)
kmem_cache_destory(scullc_cache);
內存池:
內核中有些地方的內存分配是不允許失敗的,為確保能分配成功,內核建立一種稱為內存池的抽象,他試圖始終保持空閑狀態,以便緊急情況使用。
mempool_t * mempool_creat(int min_nr,
mempool_alloc_t *alloc_fn, //對象分分配 mempool_alloc_slab
mempool_free_t *free_fn, //釋放 mempool_free_slab
void *pool_data);
可以用如下代碼來構造內存池
cache=kmem_cache_creat(...); //創建一個高速緩存
pool=mempool_creat(MY_POOL_MINIMUM,mempool_alloc_slab,mempool_free_slab,cache);//建立內存池對象
void *mempool_alloc(mempool_t *poll,int gfp_mask);//分配對象
void *mempool_free(void *element,mempool_t *poll);//釋放對象
void mempool_destroy(mempool_t *poll);//銷毀內存池
注意:mempool會分配一些內存塊,空閑且不會被用到,造成內存的大量浪費。所以一般情況不要用內存池。
㈣ linux 下怎麼優化mysql佔用內存
Linux 進程通過 C 標准庫中的內存分配函數 malloc 向系統申請內存,但是到真正與內核交互之間,其實還隔了一層,即內存分配管理器(memory allocator)。常見的內存分配器包括:ptmalloc(Glibc)、tcmalloc(Google)、jemalloc(FreeBSD)。MySQL 默認使用的是 glibc 的 ptmalloc 作為內存分配器。
內存分配器採用的是內存池的管理方式,處在用戶程序層和內核層之間,它響應用戶的分配請求,向操作系統申請內存,然後將其返回給用戶程序。
為了保持高效的分配,分配器通常會預先向操作系統申請一塊內存,當用戶程序申請和釋放內存的時候,分配器會將這些內存管理起來,並通過一些演算法策略來判斷是否將其返回給操作系統。這樣做的最大好處就是可以避免用戶程序頻繁的調用系統來進行內存分配,使用戶程序在內存使用上更加高效快捷。
關於 ptmalloc 的內存分配原理,個人也不是非常了解,這里就不班門弄斧了,有興趣的同學可以去看下華庭的《glibc 內存管理 ptmalloc 源代碼分析》【文末鏈接】。
關於如何選擇這三種內存分配器,網上資料大多都是推薦摒棄 glibc 原生的 ptmalloc,而改用 jemalloc 或者 tcmalloc 作為默認分配器。因為 ptmalloc 的主要問題其實是內存浪費、內存碎片、以及加鎖導致的性能問題,而 jemalloc 與 tcmalloc 對於內存碎片、多線程處理優化的更好。
目前 jemalloc 應用於 Firefox、FaceBook 等,並且是 MariaDB、Redis、Tengine 默認推薦的內存分配器,而 tcmalloc 則應用於 WebKit、Chrome 等。
㈤ dpdk 技術 可否 應用於linux
DPDK主要使用了UIO、HUGEPAGE和CPU Affinity機制三個技術點來提高高速網路數據的處理性能。
UIO是實現用戶空間下驅動程序的支撐機制,DPDK使用UIO機制使網卡驅動程序(主要是intel自身的千兆igb與萬兆ixgbe驅動程序)運行在用戶態,並採用輪詢和零拷貝方式從網卡收取報文,提高收發報文的性能。
HUGEPAGE的主要好處是通過利用大內存頁提高內存的使用效率,DPDK在HUGEPAGE機制上構建內存管理系統,提高應用程序處理報文的性能。
CPU Affinity機制主要是讓各個CPU各自干自己的事情,DPDK使用CPU Affinity機制將控制面線程以及各個數據面線程綁定到不同的CPU核,節省反復調度的性能消耗。其工作模式類似於一個CPU核綁定一個死循環線程,專心處理各自的業務。比如兩個網卡eth0和eth1都收包,可以讓cpu0專心處理eth0,cpu1專心處理eth1,沒必要cpu0一下處理eth0,一下又處理eth1,這樣就提高了多核CPU的使用效率。
所以,這樣看來,DPDK並不高深,用到的東西也都是Linux本身提供的特性,還有額外的內存池、環形緩存等,雖然封裝得很好,但都是比較常用經常接觸的技術。
㈥ linux 下怎麼優化mysql佔用內存
Linux 進程通過 C 標准庫中的內存分配函數 malloc 向系統申請內存,但是到真正與內核交互之間,其實還隔了一層,即內存分配管理器(memory allocator)。常見的內存分配器包括:ptmalloc(Glibc)、tcmalloc(Google)、jemalloc(FreeBSD)。MySQL 默認使用的是 glibc 的 ptmalloc 作為內存分配器。
目前 jemalloc 應用於 Firefox、FaceBook 等,並且是 MariaDB、Redis、Tengine 默認推薦的內存分配器,而 tcmalloc 則應用於 WebKit、Chrome 等。
㈦ 如何設置myeclipise 的最優內存
處理器通過地址訪問內存單元,程序中用到的基址加偏移地址是線性地址,需要通過MMU將虛擬地址映射成物理地址。這給分配和釋放內存帶來方便:1)物理地址不連續的空間可以映射為邏輯上連續的虛擬地址。2)進程可以獲得比實際內存大的"空間",虛擬內存使得進程在這種情況下仍可正常運行。linux內核為驅動程序提供了一致的內存管理介面,因此不用考慮不同體系結構如何管理內存的。在linux內核中分配內存用kmalloc和kfree。kmalloc分配時可以被阻塞,且不對所獲得的區域清零。它分配的區域在物理內存中也是連續的。原型:#includevoid*kmalloc(size_tsize,intflags);//參數為分配大小及分配標志flags參數:GFP_KERNEL:內核內存通用分配方法,表示內存分配是由運行在內核空間的進程執行的。可休眠,所以使用GFP_KERNEL分配內存的函數必須是可重入的。GFP_ATOMIC:用於在中斷處理常式或者運行在進程上下文之外的代碼中分配內存,不可休眠。內核通常會為原子性的分配預留一些空閑頁面。所有標志定義在中。size參數:內核是基於頁技術分配內存,以最佳的利用系統的RAM。linux處理內存分配的方法是:創建一系列的內存對象池,每個池的內存大小事固定的,處理分配請求時,就直接在包含足夠大的內存塊中傳遞一個整款給請求者。內核只能分配一些預定義的固定大小的位元組數組。kmalloc能處理的的最小內存塊是32或者64,不大於128KB。內存區段:linux內核把內存分為3個區段:可用於DMA的內存,常規內存以及高端內存。kmalloc不能分配高端內存。內存區段在mm/page_alloc.c中實現。區段的初始化在對應的arch樹下的mm/init.c中。後備高速緩存(lookasidecache)內核中普通對象進行初始化所需的時間超過了對其進行分配和釋放所需的時間,因此不應該將內存釋放回一個全局的內存池,而是將內存保持為針對特定目而初始化的狀態。例如,如果內存被分配給了一個互斥鎖,那麼只需在為互斥鎖首次分配內存時執行一次互斥鎖初始化函數(mutex_init)即可。後續的內存分配不需要執行這個初始化函數,因為從上次釋放和調用析構之後,它已經處於所需的狀態中了。linux2.6中USB和SCSI驅動程序使用了這種高速緩存,是為一些反復使用的塊增加某些特殊的內存池。後背高速緩存管理也叫slab分配器,相關函數和類型在中申明。slab分配器實現高速緩存具有kmem_cache_t類型。kmem_cache_t*kmem_cache_create(constchar*name,size_tsize,size_talign,unsignedlongflags;void(*constructor)(void*,kmem_cache_t*,unsignedlong),void(*destructor)(void*,kmem_cache_t*,unsignedlong));用於創建一個新的高速緩存對象。constructor用於初始化新分配的對象,destructor用於清除對象。一旦某個對象的高速緩存被創建以後,就可以調用kmem_cache_alloc從中分配內存對象。void*kmem_cache_alloc(kmem_cache_t*cache,intflags);釋放內存對象使用kmem_cache_freevoidkmem_cache_free(kmem_cache_t*cache,constvoid*obj);在內存空間都被釋放後,模塊被卸載前,驅動程序應當釋放他的高速緩存。intkmem_cache_destory(kmem_cache_t*cache);要檢查其返回狀態,如果失敗,表明莫塊中發生了內存泄露。基於slab的高速緩存scullckmem_cache_t*scullc_cache;scullc_cache=kmem_cache_creat("scullc",scullc_quantum,0,SLAB_HWCACHE_ALIGN,NULL,NULL);if(!scullc_cache){scullc_cleanup();return-ENOMEM;}if(!dpte->data[s_pos]){dptr->data[s_pos]=kmem_cache_alloc(scullc_cache,GFP_KERNEL);if(!dptr->data[s_pos])gotonomem;memset(dptr->data[s_pos],0,scullc_quantum);}for(i=0;idata[i])kmem_cache_free(scullc_cache,dptr->data[i]);}if(scullc_cache)kmem_cache_destory(scullc_cache);內存池:內核中有些地方的內存分配是不允許失敗的,為確保能分配成功,內核建立一種稱為內存池的抽象,他試圖始終保持空閑狀態,以便緊急情況使用。mempool_t*mempool_creat(intmin_nr,mempool_alloc_t*alloc_fn,//對象分分配mempool_alloc_slabmempool_free_t*free_fn,//釋放mempool_free_slabvoid*pool_data);可以用如下代碼來構造內存池cache=kmem_cache_creat();//創建一個高速緩存pool=mempool_creat(MY_POOL_MINIMUM,mempool_alloc_slab,mempool_free_slab,cache);//建立內存池對象void*mempool_alloc(mempool_t*poll,intgfp_mask);//分配對象void*mempool_free(void*element,mempool_t*poll);//釋放對象voidmempool_destroy(mempool_t*poll);//銷毀內存池注意:mempool會分配一些內存塊,空閑且不會被用到,造成內存的大量浪費。所以一般情況不要用內存池。
㈧ 如何在linux下實現event事件機制
內部使用select、epoll、kqueue等系統調用管理事件機制只提供了簡單的網路API的封裝,線程池,內存池,遞歸輔助功能函數、日誌、libevent框架、對系 統I/O如何在linux下實現event事件機制
㈨ 怎樣設計一個內存池,減少內存碎片
一般工程里不推薦你寫,因為你費力寫一個出來99%可能性沒有內置的好,且內存出bug難調試
一定要設計的話,你也可以當個玩具寫寫玩玩:
1. 實現教科書上的內存分配器:
做一個鏈表指向空閑內存,分配就是取出一塊來,改寫鏈表,返回,釋放就是放回到鏈表裡面,並做好歸並。注意做好標記和保護,避免二次釋放,還可以花點力氣在如何查找最適合大小的內存快的搜索上,減少內存碎片,有空你了還可以把鏈表換成夥伴演算法,寫著玩嘛。
2. 實現固定內存分配器:
即實現一個 FreeList,每個 FreeList 用於分配固定大小的內存塊,比如用於分配 32位元組對象的固定內存分配器,之類的。每個固定內存分配器裡面有兩個鏈表,OpenList 用於存儲未分配的空閑對象,CloseList用於存儲已分配的內存對象,那麼所謂的分配就是從 OpenList 中取出一個對象放到 CloseList 里並且返回給用戶,釋放又是從 CloseList 移回到 OpenList。分配時如果不夠,那麼就需要增長 OpenList:申請一個大一點的內存塊,切割成比如 64 個相同大小的對象添加到 OpenList中。這個固定內存分配器回收的時候,統一把先前向系統申請的內存塊全部還給系統。
3. 實現 FreeList 池:
在你實現了 FreeList的基礎上,按照不同對象大小(8位元組,16位元組,32,64,128,256,512,1K。。。64K),構造十多個固定內存分配器,分配內存時根據內存大小查表,決定到底由哪個分配器負責,分配後要在頭部的 header 處(ptr[-sizeof(char*)]處)寫上 cookie,表示又哪個分配器分配的,這樣釋放時候你才能正確歸還。如果大於64K,則直接用系統的 malloc作為分配,如此以浪費內存為代價你得到了一個分配時間近似O(1)的內存分配器,差不多實現了一個 memcached 的 slab 內存管理器了,但是先別得意。此 slab 非彼 slab(sunos/solaris/linux kernel 的 slab)。這說白了還是一個弱智的 freelist 無法歸還內存給操作系統,某個 FreeList 如果高峰期佔用了大量內存即使後面不用,也無法支援到其他內存不夠的 FreeList,所以我們做的這個和 memcached 類似的分配器其實是比較殘缺的,你還需要往下繼續優化。
4. 實現正統的 slab (非memcached的偽 slab)代替 FreeList:
這時候你需要閱讀一下論文了,現代內存分配技術的基礎,如何管理 slab 上的對象,如何進行地址管理,如何管理不同 slab 的生命周期,如何將內存回收給系統。然後開始實現一個類似的東西,文章上傳統的 slab 的各種基礎概念雖然今天沒有改變,但是所用到的數據結構和控制方法其實已經有很多更好的方法了,你可以邊實現邊思考下,實在不行還可以參考 kernel 源碼嘛。但是有很多事情應用程序做不了,有很多實現你是不能照搬的,比如頁面提供器,可以提供連續線性地址的頁面,再比如說 kernel 本身記錄著每個頁面對應的 slab,你查找 slab 時,系統其實是根據線性地址移位得到頁面編號,然後查表得到的,而你應用程序不可能這么干,你還得做一些額外的體系來解決這些問題,還需要寫一些額外的 cookie 來做標記。做好內存收縮工作,內存不夠時先收縮所有分配器的 slab,再嘗試重新分配。再做好內存回收工作,多餘的內存,一段時間不使用可以還給操作系統。
5. 實現混合分配策略:
你實現了上面很多常見的演算法後,該具體閱讀各種內存分配器的代碼了,這些都是經過實踐檢驗的,比如 libc 的內存分配器,或者參考有自帶內存管理的各種開源項目,比如 python 源碼,做點實驗對比他們的優劣,然後根據分配對象的大小採用不同的分配策略,區別對待各種情況。試驗的差不多了就得引入多線程支持了,將你的鎖改小。注意很多系統層的線程安全策略你是沒法弄的,比如操作系統可以關中斷,短時間內禁止本cpu發生任務切換,這點應用程序就很麻煩了,還得用更小的鎖來代替。當鎖已經小到不能再小,也可以選擇引入 STM 來代替各種鏈表的鎖。
6. 實現 Per-CPU Cache:
現代內存分配器,在多核下的一個重要優化就是給多核增加 cache,為了進一步避免多線程鎖競爭,需要引入 Per-CPU Cache 了。分配內存先找到對應線程所在的cpu,從該cpu上對應的 cache 里分配,cache 不夠了就一次性從你底層的內存分配器里多分配幾個對象進來填充 cache,釋放時也是先放回 cache,cache裡面如果對象太多,就做一次收縮,把內存換個底層分配器,讓其他 cpu 的cache有機會利用。這樣針對很多短生命周期的頻繁的分配、釋放,其實都是在 cache 里完成的,沒有鎖競爭,同時cache分配邏輯簡單,速度更快。操作系統裡面的代碼經常是直接讀取當前的cpu是哪個,而應用層實現你可以用 thread local storage 來代替,目前這些東西在 crt的 malloc 里還暫時支持不到位(不排除未來版本會增加),可以更多參考 tc/jemalloc。
7. 實現地址著色:
現代內存分配器必須多考慮匯流排壓力,在很多機型上,如果內存訪問集中在某條 cache line相同的偏移上,會給匯流排帶來額外的負擔和壓力。比如你經常要分配一個 FILE 對象,而每個 FILE對象使用時會比較集中的訪問 int FILE::flag; 這個成員變數,如果你的頁面提供器提供的頁面地址是按照 4K對齊的,那麼很可能多個 FILE對象的 flag 成員所處的 cache line 偏移地址是相同的,大量訪問這些相同的偏移地址會給匯流排帶來很大負擔,這時候你需要給每個對象額外增加一些偏移,讓他們能夠均勻的分布在線性地址對應的cache line 偏移上,消減匯流排沖突的開銷。
8. 優化緩存競爭:
多核時代,很多單核時代的代碼都需要針對性的優化改寫,最基本的一條就是 cache 競爭,這是比前面鎖競爭更惡劣的情況:如果兩個cpu同時訪問相同的 cache-line 或者物理頁面,那麼 cpu 之間為了保證內存一致性會做很多的通信工作,比如那個cpu0需要用到這段內存,發現cpu1也在用,那麼需要通知cpu1,將cpu1 L1-L2緩存裡面的數據寫回該物理內存,並且釋放控制權,這時cpu0取得了控制權才能繼續操作,期間cpu0-cpu1之間的通信協議是比較復雜的,代價也是比較大的,cache競爭比鎖競爭惡劣不少。為了避免 cache 競爭,需要比先前Per-CPU cache 更徹底的 Per-CPU Page 機制來解決,直接讓不同的cpu使用不同的頁面進行二次分配,徹底避免 cache 競爭。具體應用層的做法也是利用線性地址來判斷所屬頁面(因為物理頁面映射到進程地址也是4k對齊的),同時繼續使用 thread local storage 或者用系統提供的 api 讀取當前屬於哪個 cpu 來實現。為了避免核太多每個核占據大量的頁面帶來的不必要的浪費,你可以參考下 Linux 最新的 slub 內存分配演算法,但是 slub 也有未盡之處,好幾個 linux 發行版在實踐中發現 slub 還是存在一些問題的(非bug,而是機制),所以大部分發行版默認都是關閉 slub 的,雖然,你還是可以借鑒測試一下。
9. 調試和折騰:
繼續參考各種現代內存分配器,取長補短,然後給你的分配器添加一些便於調試的機制,方便診斷各種問題。在你借鑒了很多開源項目,自己也做了一些所謂的優化,折騰了那麼久以後,你或許以為你的分配器可以同各種開源分配器一戰了,測試效果好像也挺好的,先別急,繼續觀察內存利用率,向操作系統申請/歸還內存的頻率等一系列容易被人忽視的指標是否相同。同時更換你的測試用例,看看更多的情況下,是否結果還和先前一樣?這些都差不多的時候,你發現沒有個一兩年的大規模持續使用,你很難發現一些潛在的隱患和bug,可能你覺得沒問題的代碼,跑了兩年後都會繼續報bug,這很正常,多點耐心,興許第三年以後就比較穩定了呢?
㈩ swig3.0.8 linux怎麼安裝
你找個工具將這個介面API,自動轉換成python版本的就可以了。好象是SWIG和Boost。 這兩個以前看過,偶爾還試過幾次。之所以建議你用自動工具,就是因為python對於C 擴展麻煩些,對於C語言介面可以使用ctypes和cython簡單解決。不過C 如果用這些方法有時候被很麻煩。
boost在linux里會經常用到,在windows下也可以使用。SWIG在一般的教程里都會提及到。SIP也是最近出名的。
下面是一些參考資料,搜索來的。
=== 使用工具進行擴展 ===
雖然擴展過程並不復雜,但也可以使用許多已知的工具簡化擴展過程。
(1) SWIG
由David Beazley創建,是一個自動的擴展構造工具。它讀入注釋的C/C 頭文件,為python、tcl、perl等多種腳本語言產生wrap代碼。SWIG可以包裝大量C 特性到Python的擴展模塊中。
評價:swig簡單,可以支持多種腳本文件,但支持的c 特性不完備。
(2) SIP
由Phil Thompson創建,是一個C 模塊構造器,專門為C 的類創造wrapper。它曾經被用於創建PyQt和PyKDE擴展模塊,因此比較出名。
評價:支持C 特徵很齊全,但比較復雜。
(3) bgen
該工具被包含在標准Python發布包中的模塊構建工具集里,由Jack Jansen維護。它用於產生在Macintosh版本可用的Python擴展模塊。
(4) pyfort
由Paul bois創建,用來產生Fortran語言生成的擴展模塊。
(5) cxx
也由Paul Dubois創建,甫俯顛謊郯荷奠捅訂拉是一個庫,為Python的C 擴展提供了友好的API。Cxx允許將許多python對象(如list和tuple)使用到STL的運算中。庫也提供了C 異常處理到python異常處理的轉化。
(6) WrapPy
由Greg Couch創建,通過讀入C 頭文件來產生擴展模塊。
(7) Boost Python Library
由David Abrahams創建。該庫提供了更多與眾不同的C wrap到python擴展中,而只需要對要擴展的C 類寫很少的附加信息。
評價:Boost為C 提供了許多實用的庫,如Regex(正則表達式庫)、Graph(圖組件和演算法)、concept check(檢查泛型編程中的concept)、Thread(可移植的C 多線程庫)、Python(把C 類和函數映射到Python之中)、Pool(內存池管理)等等。
Boost總體來說是實用價值很高,質量很高的庫。並且強調對跨平台的支持。但是Boost中也有很多是實驗性質的東西,在實際的開發中實用需要謹慎。
boost.python支持的c 特性較多,但是比較復雜。