新手学linux之-----------memroy.c
生活随笔
收集整理的這篇文章主要介紹了
新手学linux之-----------memroy.c
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
2019獨角獸企業重金招聘Python工程師標準>>>
/**??linux/mm/memory.c**??(C)?1991??Linus?Torvalds*//**?demand-loading?started?01.12.91?-?seems?it?is?high?on?the?list?of*?things?wanted,?and?it?should?be?easy?to?implement.?-?Linus*//**?Ok,?demand-loading?was?easy,?shared?pages?a?little?bit?tricker.?Shared*?pages?started?02.12.91,?seems?to?work.?-?Linus.**?Tested?sharing?by?executing?about?30?/bin/sh:?under?the?old?kernel?it*?would?have?taken?more?than?the?6M?I?have?free,?but?it?worked?well?as*?far?as?I?could?see.**?Also?corrected?some?"invalidate()"s?-?I?wasn't?doing?enough?of?them.*/#include?<signal.h>#include?<asm/system.h>#include?<linux/sched.h> #include?<linux/head.h> #include?<linux/kernel.h>volatile?void?do_exit(long?code);//內存溢出函數 static?inline?volatile?void?oom(void) {printk("out?of?memory\n\r");do_exit(SIGSEGV); }#define?invalidate()?\ __asm__("movl?%%eax,%%cr3"::"a"?(0))/*?these?are?not?to?be?changed?without?changing?head.s?etc?*/ #define?LOW_MEM?0x100000 #define?PAGING_MEMORY?(15*1024*1024)????????//16M-1M?那個1M為內核使用 #define?PAGING_PAGES?(PAGING_MEMORY>>12) #define?MAP_NR(addr)?(((addr)-LOW_MEM)>>12) #define?USED?100#define?CODE_SPACE(addr)?((((addr)+4095)&~4095)?<?\ current->start_code?+?current->end_code)static?long?HIGH_MEMORY?=?0;#define?copy_page(from,to)?\ __asm__("cld?;?rep?;?movsl"::"S"?(from),"D"?(to),"c"?(1024):"cx","di","si")static?unsigned?char?mem_map?[?PAGING_PAGES?]?=?{0,};????//mem-map是內存映射字節圖/**?Get?physical?address?of?first?(actually?last?:-)?free?page,?and?mark?it*?used.?If?no?free?pages?left,?return?0.*/ //獲取一個空頁面?并返回?actually?last?:-)這句話的意思是從后往前找 unsigned?long?get_free_page(void) { register?unsigned?long?__res?asm("ax"); //scas?功能:?把AL(字節串)或AX(字串)的內容與由DI寄存器尋址的目的串中的數據相減,結果置??? //標志位,但不改變任一操作數本身.地址指針DI自動調整.? //repne?CX<>0?且ZF=0重復執行字符串指令? //格式:?SAL?OPRD1,COUNT? //功能:?其中OPRD1,COUNT與指令SHL相同.本指令與SHL的功能也完全相同,這是因為邏輯左??? //----??移指令與算術左移指令所要完成的操作是一樣的.?如果SAL將OPRD1的最高位移至????????? //CF,改變了原來的CF值,則溢出標志位OF=1,表示移位的前后的操作數不同具有倍增????????? //的關系.因而SAL可用于帶符號數的倍增運算,SHL只能用于無符號數的倍增運 //有效地址傳送指令?LEA? //格式:?LEA?OPRD1,OPRD2? //功能:?將源操作數給出的有效地址傳送到指定的的寄存器中. __asm__("std?;?repne?;?scasb\n\t"?"jne?1f\n\t"????????????????????//沒有滿足的???跳轉?執行結束"movb?$1,1(%%edi)\n\t"????????????//mov?[edi+1],1;?將對應頁面的內存印象比特位置一"sall?$12,%%ecx\n\t"????????????//PAGING_PAGES*4?=?對應頁面起始位置"addl?%2,%%ecx\n\t"????????????????//加上內存低端地址LOW_MEM(1M)?得到實際的物理地址"movl?%%ecx,%%edx\n\t"????????????"movl?$1024,%%ecx\n\t"????????????"leal?4092(%%edx),%%edi\n\t"????"rep?;?stosl\n\t"????????????????"movl?%%edx,%%eax\n"????????????"1:":"=a"?(__res):"0"?(0),"i"?(LOW_MEM),"c"?(PAGING_PAGES),"D"?(mem_map+PAGING_PAGES-1)????????/*?%edi被指向最后一頁內存?*/:"di","cx","dx"); return?__res; }/**?Free?a?page?of?memory?at?physical?address?'addr'.?Used?by*?'free_page_tables()'*///addr對應的是物理地址 void?free_page(unsigned?long?addr) {if?(addr?<?LOW_MEM)?return;if?(addr?>=?HIGH_MEMORY)panic("trying?to?free?nonexistent?page");addr?-=?LOW_MEM;addr?>>=?12;//如果對應的映射不為零?減一返回if?(mem_map[addr]--)?return;mem_map[addr]=0;panic("trying?to?free?free?page"); }/**?This?function?frees?a?continuos?block?of?page?tables,?as?needed*?by?'exit()'.?As?does?copy_page_tables(),?this?handles?only?4Mb?blocks.*///釋放頁面連續的內存塊//from?開始的線性地址?size釋放的長度 int?free_page_tables(unsigned?long?from,unsigned?long?size) {unsigned?long?*pg_table;unsigned?long?*?dir,?nr;//位于4MB之內??if?(from?&?0x3fffff)panic("free_page_tables?called?with?wrong?alignment");if?(!from)panic("Trying?to?free?up?swapper?memory?space");size?=?(size?+?0x3fffff)?>>?22;//得到頁目錄項dir?=?(unsigned?long?*)?((from>>20)?&?0xffc);?/*?_pg_dir?=?0?*/for?(?;?size-->0?;?dir++)?{//二級頁表不存在???跳出?繼續if?(!(1?&?*dir))continue;//取二級頁表?并釋放之pg_table?=?(unsigned?long?*)?(0xfffff000?&?*dir);for?(nr=0?;?nr<1024?;?nr++)?{if?(1?&?*pg_table)free_page(0xfffff000?&?*pg_table);*pg_table?=?0;pg_table++;}//釋放頁目錄表free_page(0xfffff000?&?*dir);*dir?=?0;}invalidate();return?0; }/**??Well,?here?is?one?of?the?most?complicated?functions?in?mm.?It*?copies?a?range?of?linerar?addresses?by?copying?only?the?pages.*?Let's?hope?this?is?bug-free,?'cause?this?one?I?don't?want?to?debug?:-)**?Note!?We?don't?copy?just?any?chunks?of?memory?-?addresses?have?to*?be?divisible?by?4Mb?(one?page-directory?entry),?as?this?makes?the*?function?easier.?It's?used?only?by?fork?anyway.**?NOTE?2!!?When?from==0?we?are?copying?kernel?space?for?the?first*?fork().?Then?we?DONT?want?to?copy?a?full?page-directory?entry,?as*?that?would?lead?to?some?serious?memory?waste?-?we?just?copy?the*?first?160?pages?-?640kB.?Even?that?is?more?than?we?need,?but?it*?doesn't?take?any?more?memory?-?we?don't?copy-on-write?in?the?low*?1?Mb-range,?so?the?pages?can?be?shared?with?the?kernel.?Thus?the*?special?case?for?nr=xxxx.*///參數?from?to?是線性地址?size是需要復制的內存長度 int?copy_page_tables(unsigned?long?from,unsigned?long?to,long?size) {unsigned?long?*?from_page_table;unsigned?long?*?to_page_table;unsigned?long?this_page;unsigned?long?*?from_dir,?*?to_dir;unsigned?long?nr;if?((from&0x3fffff)?||?(to&0x3fffff))panic("copy_page_tables?called?with?wrong?alignment");//頁表項地址from_dir?=?(unsigned?long?*)?((from>>20)?&?0xffc);?/*?_pg_dir?=?0?*/to_dir?=?(unsigned?long?*)?((to>>20)?&?0xffc);size?=?((unsigned)?(size+0x3fffff))?>>?22;//這里遍歷的是一級頁表for(?;?size-->0?;?from_dir++,to_dir++)?{//目的的頁表已經存在???停機if?(1?&?*to_dir)panic("copy_page_tables:?already?exist");//源頁表項不存在???無意義?跳出?繼續循環if?(!(1?&?*from_dir))continue;from_page_table?=?(unsigned?long?*)?(0xfffff000?&?*from_dir);if?(!(to_page_table?=?(unsigned?long?*)?get_free_page()))return?-1;????/*?Out?of?memory,?see?freeing?*/*to_dir?=?((unsigned?long)?to_page_table)?|?7;//from如果等于0的話說明對應的是內核段內存?內核只占0xA0長度?nr?=?(from==0)?0xA0:1024;//這里開始遍歷二級頁表for?(?;?nr--?>?0?;?from_page_table++,to_page_table++)?{this_page?=?*from_page_table;if?(!(1?&?this_page))continue;this_page?&=?~2;*to_page_table?=?this_page;if?(this_page?>?LOW_MEM)?{*from_page_table?=?this_page;this_page?-=?LOW_MEM;this_page?>>=?12;//將新加映射的頁表mem_map對應的加一mem_map[this_page]++;}}}//刷新緩沖區invalidate();return?0; }/**?This?function?puts?a?page?in?memory?at?the?wanted?address.*?It?returns?the?physical?address?of?the?page?gotten,?0?if*?out?of?memory?(either?when?trying?to?access?page-table?or*?page.)*///do_no_page中調用這個函數?page是奉陪的主存區的某一頁面的指針//address是線性地址//此函數容易懂?不必多說 unsigned?long?put_page(unsigned?long?page,unsigned?long?address) {unsigned?long?tmp,?*page_table;/*?NOTE?!!!?This?uses?the?fact?that?_pg_dir=0?*/if?(page?<?LOW_MEM?||?page?>=?HIGH_MEMORY)?printk("Trying?to?put?page?%p?at?%p\n",page,address);//相應位置是否已經置位??if?(mem_map[(page-LOW_MEM)>>12]?!=?1)printk("mem_map?disagrees?with?%p?at?%p\n",page,address);page_table?=?(unsigned?long?*)?((address>>20)?&?0xffc);//該頁面是否存在??if?(?(*page_table)&1)page_table?=?(unsigned?long?*)?(0xfffff000?&?*page_table);else?{//否則重新申請一頁新的內存空間if?(!(tmp=get_free_page()))return?0;*page_table?=?tmp|7;page_table?=?(unsigned?long?*)?tmp;}page_table[(address>>12)?&?0x3ff]?=?page?|?7; /*?no?need?for?invalidate?*/ //為什么不要刷新緩沖區 //由于任何缺頁緣故對頁表做修改時?并不需要刷新cpu的變頁緩沖return?page; }//取消寫保護頁面 //un_write?protect?page //輸入參數?頁表指針 void?un_wp_page(unsigned?long?*?table_entry) {unsigned?long?old_page,new_page;old_page?=?0xfffff000?&?*table_entry;//指定的頁表只存在一個映射位??if?(old_page?>=?LOW_MEM?&&?mem_map[MAP_NR(old_page)]==1)?{*table_entry?|=?2;//刷新緩沖區invalidate();return;}if?(!(new_page=get_free_page()))oom();if?(old_page?>=?LOW_MEM)mem_map[MAP_NR(old_page)]--;*table_entry?=?new_page?|?7;invalidate();copy_page(old_page,new_page); }????/**?This?routine?handles?present?pages,?when?users?try?to?write*?to?a?shared?page.?It?is?done?by?copying?the?page?to?a?new?address*?and?decrementing?the?shared-page?counter?for?the?old?page.**?If?it's?in?code?space?we?exit?with?a?segment?error.*/ //該函數被page.s調用?當發生寫保護異常時 //該函數做到的主要是???寫時復制? void?do_wp_page(unsigned?long?error_code,unsigned?long?address) { #if?0 /*?we?cannot?do?this?yet:?the?estdio?library?writes?to?code?space?*/ /*?stupid,?stupid.?I?really?want?the?libc.a?from?GNU?*/if?(CODE_SPACE(address))do_exit(SIGSEGV); #endif //要想搞懂這么個長串的內存地址相關的關系?關鍵是要搞懂參數到底傳的是什么地址類型 //是線性地址??還是物理地址?頁表項或頁表中的地址? //??????????????31---22???????21---12?????11---0 //線性地址:頁目錄項(10)???頁表項(10)??頁內偏移值(12) //address是線性地址??對照表就好理解多了 //((address>>12)&0x3ff)<<2=(address>>10)?&?0xffc)? //?這樣理解就可以輕松理解這個式子了un_wp_page((unsigned?long?*)(((address>>10)?&?0xffc)?+?(0xfffff000?&*((unsigned?long?*)?((address>>20)?&0xffc)))));}//寫頁面認證函數 void?write_verify(unsigned?long?address) {unsigned?long?page;//p位為0?if?(!(?(page?=?*((unsigned?long?*)?((address>>20)?&?0xffc))?)&1))??//此時page為頁目錄項基地址return;page?&=?0xfffff000;????????page?+=?((address>>10)?&?0xffc);??//頁表基地址加上頁表項?如果用*取值那么就為頁框基地址if?((3?&?*(unsigned?long?*)?page)?==?1)??/*?non-writeable,?present?*/un_wp_page((unsigned?long?*)?page);????????//un_wp_page中的參數為頁表項指針?為物理地址return;???? }//取得一頁空閑內存頁?并映射到指定額線性地址處 void?get_empty_page(unsigned?long?address) {unsigned?long?tmp; //如果能得到新的內存釋放頁并且能將線性地址映射到指定的頁面內存那么 //就不會進入到free_page中?并且拋出out?of?memroy的操作?if?(!(tmp=get_free_page())?||?!put_page(tmp,address))?{free_page(tmp);????????/*?0?is?ok?-?ignored?*/oom();} }/**?try_to_share()?checks?the?page?at?address?"address"?in?the?task?"p",*?to?see?if?it?exists,?and?if?it?is?clean.?If?so,?share?it?with?the?current*?task.**?NOTE!?This?assumes?we?have?checked?that?p?!=?current,?and?that?they*?share?the?same?executable.*///參數說明:address是進程中的邏輯地址?//嘗試將進程address中對應的頁面地址與p進行共享?//可以理解為由進程p?fork出來的進程? static?int?try_to_share(unsigned?long?address,?struct?task_struct?*?p) {unsigned?long?from;unsigned?long?to;unsigned?long?from_page;unsigned?long?to_page;unsigned?long?phys_addr;//這里from_page為實際的頁表項地址from_page?=?to_page?=?((address>>20)?&?0xffc); //即實際地址為虛擬地址加上真實偏移值((p->start_code)>>22)from_page?+=?((p->start_code>>20)?&?0xffc);to_page?+=?((current->start_code>>20)?&?0xffc); /*?is?there?a?page-directory?at?from??*/ //取得頁表基地址from?=?*(unsigned?long?*)?from_page;if?(!(from?&?1))return?0;from?&=?0xfffff000;from_page?=?from?+?((address>>10)?&?0xffc);phys_addr?=?*(unsigned?long?*)?from_page;????//頁表項內容 /*?is?the?page?clean?and?present??*/ //0x41分別對應第六位(D位)第0位(p位)if?((phys_addr?&?0x41)?!=?0x01)return?0;phys_addr?&=?0xfffff000;if?(phys_addr?>=?HIGH_MEMORY?||?phys_addr?<?LOW_MEM)return?0;to?=?*(unsigned?long?*)?to_page;//如果當前進程頁目錄項p=0?那么將獲取新的頁面if?(!(to?&?1))if?(to?=?get_free_page())*(unsigned?long?*)?to_page?=?to?|?7;elseoom();//獲取新頁面的后的相關設置to?&=?0xfffff000;to_page?=?to?+?((address>>10)?&?0xffc);if?(1?&?*(unsigned?long?*)?to_page)panic("try_to_share:?to_page?already?exists"); /*?share?them:?write-protect?*/*(unsigned?long?*)?from_page?&=?~2;//本程序的核心就是這一句//使當前的頁表項值等于p進程的頁表項的值*(unsigned?long?*)?to_page?=?*(unsigned?long?*)?from_page;invalidate();//對引用加一的設置phys_addr?-=?LOW_MEM;phys_addr?>>=?12;mem_map[phys_addr]++;return?1; }/**?share_page()?tries?to?find?a?process?that?could?share?a?page?with*?the?current?one.?Address?is?the?address?of?the?wanted?page?relative*?to?the?current?data?space.**?We?first?check?if?it?is?at?all?feasible?by?checking?executable->i_count.*?It?should?be?>1?if?there?are?other?tasks?sharing?this?inode.*/ //共享頁面處理函數 //參數說明?address是邏輯地址 //當頁面發生異常時?首先看看是否有p進程與當前進程有同一樣的執行文件 //若有則嘗試共享??這種linux的內存節省操作是值得學習的??寫的真好 static?int?share_page(unsigned?long?address) {struct?task_struct?**?p; //executable是一個結構體指針??對應的結構體如下 //fs.h //這是在內存中的i節點結構 /*struct?m_inode?{...unsigned?short?i_count;????????//此項對應的意思是?i節點被使用過的次數... };*/if?(!current->executable)return?0;//沒有被共享的內存返回0??//我感覺這個想的有點意思?值得學習一下??哈哈if?(current->executable->i_count?<?2)return?0;//LAST_TASK的定義#define?LAST_TASK?task[NR_TASKS-1]//NR_TASKS表示當前linux版本中最大的進程數?為64//開始遍歷當前的進程?for?(p?=?&LAST_TASK?;?p?>?&FIRST_TASK?;?--p)?{if?(!*p)continue;//當前進程就是本進程?無意義?跳出?繼續循環if?(current?==?*p)continue;if?((*p)->executable?!=?current->executable)continue;//嘗試共享頁面if?(try_to_share(address,*p))return?1;}return?0; } //該函數為缺頁調用函數??在page.s中被調用 //address是產生異常的線性地址 //此函數bug較多?不必深究(在linux0.11中) void?do_no_page(unsigned?long?error_code,unsigned?long?address) {int?nr[4];unsigned?long?tmp;unsigned?long?page;int?block,i;address?&=?0xfffff000;?tmp?=?address?-?current->start_code;????//獲取對應的邏輯地址?//是由執行文件i節點缺失引起的??if?(!current->executable?||?tmp?>=?current->end_data)?{get_empty_page(address);return;}//開始嘗試共享頁面if?(share_page(tmp))return;//否則去申請新的頁面if?(!(page?=?get_free_page()))oom(); /*?remember?that?1?block?is?used?for?header?*///四個邏輯塊讀入到剛剛申請的頁面中?并且設置他們的映射關系block?=?1?+?tmp/BLOCK_SIZE;?for?(i=0?;?i<4?;?block++,i++)nr[i]?=?bmap(current->executable,block);bread_page(page,current->executable->i_dev,nr);//在讀設備的邏輯塊的時候?可能會出現?在執行文件中讀取的頁面位置可能//不到一個頁面的信息?就會讀入一些無用的信息?因此需要將其置零//此段代碼在linux0.11中bug有點多?不必過分追究i?=?tmp?+?4096?-?current->end_data;tmp?=?page?+?4096;while?(i--?>?0)?{tmp--;?*(char?*)tmp?=?0;}//將申請的page頁面的地址轉換為addressif?(put_page(page,address))return;free_page(page);oom(); }//內存初始化函數?在main.c中調用 void?mem_init(long?start_mem,?long?end_mem) {int?i;//#define?PAGING_MEMORY?(15*1024*1024)//#define?PAGING_PAGES?(PAGING_MEMORY>>12)//linus的意思是將1M以內的內存作為內核內存區//將1M以上的15M作為普通的內存區??并且內存的頁面數為15MB/4KB=3840HIGH_MEMORY?=?end_mem;//先將1M-16M的地方設置為USEDfor?(i=0?;?i<PAGING_PAGES?;?i++)mem_map[i]?=?USED;????????//將內存的映射關系設置為USED=100i?=?MAP_NR(start_mem);????????//MAP_NR=>(start_mem-1MB)>>12?end_mem?-=?start_mem;end_mem?>>=?12;//4M-16M的地方設置內存為0?表示都沒有使用while?(end_mem-->0)mem_map[i++]=0; }//內存計算函數 void?calc_mem(void) {int?i,j,k,free=0;long?*?pg_tbl;for(i=0?;?i<PAGING_PAGES?;?i++)if?(!mem_map[i])?free++;printk("%d?pages?free?(of?%d)\n\r",free,PAGING_PAGES);//i=0,1作為內核的使用?在這里不計算for(i=2?;?i<1024?;?i++)?{if?(1&pg_dir[i])?{pg_tbl=(long?*)?(0xfffff000?&?pg_dir[i]);//二級頁表??for(j=k=0?;?j<1024?;?j++)if?(pg_tbl[j]&1)k++;printk("Pg-dir[%d]?uses?%d?pages\n",i,k);}} }轉載于:https://my.oschina.net/lngligelang/blog/262839
總結
以上是生活随笔為你收集整理的新手学linux之-----------memroy.c的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ExtJS MVC学习手记 1
- 下一篇: linux 其他常用命令