?? 6.html
字號(hào):
·arg_start、arg_end:調(diào)用參數(shù)區(qū)的起始地址和結(jié)束地址。<br>·env_start、env_end:進(jìn)程環(huán)境區(qū)的起始地址和結(jié)束地址。<br>·rss:進(jìn)程內(nèi)容駐留在物理內(nèi)存的頁(yè)面總數(shù)。<p><br>2. 虛存段(vma)數(shù)據(jù)結(jié)構(gòu):vm_area_atruct<br> 虛存段vma由數(shù)據(jù)結(jié)構(gòu)vm_area_atruct(include/linux/mm.h)描述:<p>struct vm_area_struct {<br> struct mm_struct * vm_mm; /* VM area parameters */<br> unsigned long vm_start;<br> unsigned long vm_end;<br> pgprot_t vm_page_prot;<br> unsigned short vm_flags;<br>/* AVL tree of VM areas per task, sorted by address */<br> short vm_avl_height;<br> struct vm_area_struct * vm_avl_left;<br> struct vm_area_struct * vm_avl_right;<br>/* linked list of VM areas per task, sorted by address */<br> struct vm_area_struct * vm_next;<br>/* for areas with inode, the circular list inode->i_mmap */<br>/* for shm areas, the circular list of attaches */<br>/* otherwise unused */<br> struct vm_area_struct * vm_next_share;<br> struct vm_area_struct * vm_prev_share;<br>/* more */<br> struct vm_operations_struct * vm_ops;<br> unsigned long vm_offset;<br> struct inode * vm_inode;<br> unsigned long vm_pte; /* shared mem */<br>};<p>vm_start;//所對(duì)應(yīng)內(nèi)存區(qū)域的開(kāi)始地址<br>vm_end; //所對(duì)應(yīng)內(nèi)存區(qū)域的結(jié)束地址<br>vm_flags; //進(jìn)程對(duì)所對(duì)應(yīng)內(nèi)存區(qū)域的訪(fǎng)問(wèn)權(quán)限<br>vm_avl_height;//avl樹(shù)的高度<br>vm_avl_left; //avl樹(shù)的左兒子<br>vm_avl_right; //avl樹(shù)的右兒子<br>vm_next;// 進(jìn)程所使用的按地址排序的vm_area鏈表指針<br>vm_ops;//一組對(duì)內(nèi)存的操作<br> 這些對(duì)內(nèi)存的操作是當(dāng)對(duì)虛存進(jìn)行操作的時(shí)候Linux系統(tǒng)必須使用的一組方法。比如說(shuō),當(dāng)進(jìn)程準(zhǔn)備訪(fǎng)問(wèn)某一虛存區(qū)域但是發(fā)現(xiàn)此區(qū)域在物理內(nèi)存不存在時(shí)(缺頁(yè)中斷),就激發(fā)某種對(duì)內(nèi)存的操作執(zhí)行正確的行為。這種操作是空頁(yè)(nopage)操作。當(dāng)Linux系統(tǒng)按需調(diào)度可執(zhí)行的頁(yè)面映象進(jìn)入內(nèi)存時(shí)就使用這種空頁(yè)(nopage)操作。<br> 當(dāng)一個(gè)可執(zhí)行的頁(yè)面映象映射到進(jìn)程的虛存地址時(shí),一組vm_area_struct結(jié)構(gòu)的數(shù)據(jù)結(jié)構(gòu)(vma)就會(huì)生成。每一個(gè)vm_area_struct的數(shù)據(jù)結(jié)構(gòu)(vma)代表可執(zhí)行的頁(yè)面映象的一部分:可執(zhí)行代碼,初始化數(shù)據(jù)(變量),非初始化數(shù)據(jù)等等。Linux系統(tǒng)可以支持大量的標(biāo)準(zhǔn)虛存操作,當(dāng)vm_area_struct數(shù)據(jù)結(jié)構(gòu)(vma)一被創(chuàng)建,它就對(duì)應(yīng)于一組正確的虛存操作。<br> 屬于同一進(jìn)程的vma段通過(guò)vm_next指針連接,組成鏈表。如圖2-3所示,struct mm_struct結(jié)構(gòu)的成員struct vm_area_struct * mmap 表示進(jìn)程的vma鏈表的表頭。<br> 為了提高對(duì)vma段 查詢(xún)、插入、刪除操作的速度,LINUX同時(shí)維護(hù)了一個(gè)AVL(Adelson-Velskii and Landis)樹(shù)。在樹(shù)中,所有的vm_area_struct虛存段均有左指針vm_avl_left指向相鄰的低地址虛存段,右指針vm_avl_right指向相鄰的高地址虛存段,如圖2-5。struct mm_struct結(jié)構(gòu)的成員struct vm_area_struct * mmap_avl表示進(jìn)程的AVL樹(shù)的根,vm_avl_height表示AVL樹(shù)的高度。<br> 對(duì)平衡樹(shù)mmap_avl的任何操作必須滿(mǎn)足平衡樹(shù)的一些規(guī)則:<br>Consistency and balancing rulesJ(一致性和平衡規(guī)則):<p>tree->vm_avl_height==1+max(heightof(tree->vm_avl_left),heightof(<br>tree->vm_avl_right))<br>abs( heightof(tree->vm_avl_left) - heightof(tree->vm_avl_right) ) <= 1<br>foreach node in tree->vm_avl_left: node->vm_avl_key <= tree->vm_avl_key, foreach node in tree->vm_avl_right: node->vm_avl_key >= tree->vm_avl_key.<br> 注:其中node->vm_avl_key= node->vm_end<p>對(duì)vma可以進(jìn)行加鎖、加保護(hù)、共享和動(dòng)態(tài)擴(kuò)展等操作。<p><br><center><A HREF="#Content">[目錄](méi)</A></center><hr><br><A NAME="I457" ID="I457"></A><center><b><font size=+2>重要常量</font></b></center><br> mlock系統(tǒng)調(diào)用所用到的重要常量有:PAGE_MASK、PAGE_SIZE、PAGE_SHIFT、RLIMIT_MEMLOCK、VM_LOCKED、 PF_SUPERPRIV等。它們的值分別如下:<p> PAGE_SHIFT 12 // PAGE_SHIFT determines the page size<br> PAGE_SIZE 0x1000 //1UL<<PAGE_SHIFT<br> PAGE_MASK ~(PAGE_SIZE-1) //a very useful constant variable<br> RLIMIT_MEMLOCK 8 //max locked-in-memory address space<br> VM_LOCKED 0x2000 //8*1024=8192, vm_flags的標(biāo)志之一。<br> PF_SUPERPRIV 0x00000100 //512<p><center><A HREF="#Content">[目錄](méi)</A></center><hr><br><A NAME="I458" ID="I458"></A><center><b><font size=+2>代碼函數(shù)功能分析</font></b></center><br>mlock系統(tǒng)調(diào)用代碼函數(shù)功能分析<p> 下面對(duì)各個(gè)函數(shù)的功能作詳細(xì)的分析((1)和(2)在前面簡(jiǎn)介mlock時(shí)已介紹過(guò),并在后面有詳細(xì)的程序流程):<p>suser():如果用戶(hù)有效(即current->euid == 0 ),則設(shè)置進(jìn)程標(biāo)志為root優(yōu)先權(quán)(current->flags |= PF_SUPERPRIV),并返回1;否則返回0。<p>find_vma(struct mm_struct * mm, unsigned long addr):輸入?yún)?shù)為當(dāng)前進(jìn)程的mm、需要加鎖的開(kāi)始內(nèi)存地址addr。find_vma的功能是在mm的mmap_avl樹(shù)中尋找第一個(gè)滿(mǎn)足mm->mmap_avl->vm_start<=addr< mm->mmap_avl->vm_end的vma,如果成功則返回此vma;否則返回空null。<p>mlock_fixup(struct vm_area_struct * vma, unsigned long start, unsigned long end, unsigned int newflags):輸入?yún)?shù)為vm_mmap鏈中的某個(gè)vma、需要加鎖內(nèi)存區(qū)域起始地址和結(jié)束地址、需要修改的標(biāo)志(0:加鎖,1:釋放鎖)。<p>merge_segments(struct mm_struct * mm, unsigned long start_addr, unsigned long end_addr):輸入?yún)?shù)為當(dāng)前進(jìn)程的mm、需要加鎖的開(kāi)始內(nèi)存地址start_addr和結(jié)束地址end_addr。merge_segments的功能的是盡最大可能歸并相鄰(即內(nèi)存地址偏移量連續(xù))并有相同屬性(包括vm_inode,vm_pte,vm_ops,vm_flags)的內(nèi)存段,在這過(guò)程中冗余的vm_area_structs被釋放,這就要求vm_mmap鏈按地址大小排序(我們不需要遍歷整個(gè)表,而只需要遍歷那些交叉或者相隔一定連續(xù)區(qū)域的鄰接vm_area_structs)。當(dāng)然在缺省的情況下,merge_segments是對(duì)vm_mmap_avl樹(shù)進(jìn)行循環(huán)處理,有多少可以合并的段就合并多少。<p>mlock_fixup_all(struct vm_area_struct * vma, int newflags):輸入?yún)?shù)為vm_mmap鏈中的某個(gè)vma、需要修改的標(biāo)志(0:加鎖,1:釋放鎖)。mlock_fixup_all的功能是根據(jù)輸入?yún)?shù)newflags修改此vma的vm_flags。<p>mlock_fixup_start(struct vm_area_struct * vma,unsigned long end, int newflags):輸入?yún)?shù)為vm_mmap鏈中的某個(gè)vma、需要加鎖內(nèi)存區(qū)域結(jié)束地址、需要修改的標(biāo)志(0:加鎖,1:釋放鎖)。mlock_fixup_start的功能是根據(jù)輸入?yún)?shù)end,在內(nèi)存中分配一個(gè)新的new_vma,把原來(lái)的vma分成兩個(gè)部分: new_vma和vma,其中new_vma的vm_flags被設(shè)置成輸入?yún)?shù)newflags;并且按地址(new_vma->start和new_vma->end)大小序列把新生成的new->vma插入到當(dāng)前進(jìn)程mm的mmap鏈或mmap_avl樹(shù)中(缺省情況下是插入到mmap_avl樹(shù)中)。<br> 注:vma->vm_offset+= vma->vm_start-new_vma->vm_start;<br>mlock_fixup_end(struct vm_area_struct * vma,unsigned long start, int newflags):輸入?yún)?shù)為vm_mmap鏈中的某個(gè)vma、需要加鎖內(nèi)存區(qū)域起始地址、需要修改的標(biāo)志(0:加鎖,1:釋放鎖)。mlock_fixup_end的功能是根據(jù)輸入?yún)?shù)start,在內(nèi)存中分配一個(gè)新的new_vma,把原來(lái)的vma分成兩個(gè)部分:vma和new_vma,其中new_vma的vm_flags被設(shè)置成輸入?yún)?shù)newflags;并且按地址大小序列把new->vma插入到當(dāng)前進(jìn)程mm的mmap鏈或mmap_avl樹(shù)中。<br> 注:new_vma->vm_offset= vma->vm_offset+(new_vma->vm_start-vma->vm_start);<br>mlock_fixup_middle(struct vm_area_struct * vma,unsigned long start, unsigned long end, int newflags):輸入?yún)?shù)為vm_mmap鏈中的某個(gè)vma、需要加鎖內(nèi)存區(qū)域起始地址和結(jié)束地址、需要修改的標(biāo)志(0:加鎖,1:釋放鎖)。mlock_fixup_middle的功能是根據(jù)輸入?yún)?shù)start、end,在內(nèi)存中分配兩個(gè)新vma,把原來(lái)的vma分成三個(gè)部分:left_vma、vma和right_vma,其中vma的vm_flags被設(shè)置成輸入?yún)?shù)newflags;并且按地址大小序列把left->vma和right->vma插入到當(dāng)前進(jìn)程mm的mmap鏈或mmap_avl樹(shù)中。<br> 注:vma->vm_offset += vma->vm_start-left_vma->vm_start;<br> right_vma->vm_offset += right_vma->vm_start-left_vma->vm_start;<p>kmalloc():常用的一個(gè)內(nèi)核函數(shù)<p>insert_vm_struct(struct mm_struct *mm, struct vm_area_struct *vmp):輸入?yún)?shù)為當(dāng)前進(jìn)程的mm、需要插入的vmp。insert_vm_struct的功能是按地址大小序列把vmp插入到當(dāng)前進(jìn)程mm的mmap鏈或mmap_avl樹(shù)中,并且把vmp插入到vmp->inode的i_mmap環(huán)(循環(huán)共享鏈)中。<p>avl_insert_neighbours(struct vm_area_struct * new_node,** ptree,** to_the_left,** to_the_right):輸入?yún)?shù)為當(dāng)前需要插入的新vma結(jié)點(diǎn)new_node、目標(biāo)mmap_avl樹(shù)ptree、新結(jié)點(diǎn)插入ptree后它左邊的結(jié)點(diǎn)以及它右邊的結(jié)點(diǎn)(左右邊結(jié)點(diǎn)按mmap_avl中各vma->vma_end大小排序)。avl_insert_neighbours的功能是插入新vma結(jié)點(diǎn)new_node到目標(biāo)mmap_avl樹(shù)ptree中,并且調(diào)用avl_rebalance以保持ptree的平衡樹(shù)特性,最后返回new_node左邊的結(jié)點(diǎn)以及它右邊的結(jié)點(diǎn)。<p>avl_rebalance(struct vm_area_struct *** nodeplaces_ptr, int count):輸入?yún)?shù)為指向vm_area_struct指針結(jié)構(gòu)的指針數(shù)據(jù)nodeplaces_ptr[](每個(gè)元素表示需要平衡的mmap_avl子樹(shù))、數(shù)據(jù)元素個(gè)數(shù)count。avl_rebalance的功能是從nodeplaces_ptr[--count]開(kāi)始直到nodeplaces_ptr[0]循環(huán)平衡各個(gè)mmap_avl子樹(shù),最終使整個(gè)mmap_avl樹(shù)平衡。<p>down(struct semaphore * sem):輸入?yún)?shù)為同步(進(jìn)入臨界區(qū))信號(hào)量sem。down的功能根據(jù)當(dāng)前信號(hào)量的設(shè)置情況加鎖(阻止別的進(jìn)程進(jìn)入臨界區(qū))并繼續(xù)執(zhí)行或進(jìn)入等待狀態(tài)(等待別的進(jìn)程執(zhí)行完成退出臨界區(qū)并釋放鎖)。<br> down定義在/include/linux/sched.h中:<br>extern inline void down(struct semaphore * sem)<br>{<br> if (sem->count <= 0)<br> __down(sem);<br> sem->count--;<br>}<p>up(struct semaphore * sem)輸入?yún)?shù)為同步(進(jìn)入臨界區(qū))信號(hào)量sem。up的功能根據(jù)當(dāng)前信號(hào)量的設(shè)置情況(當(dāng)信號(hào)量的值為負(fù)數(shù):表示有某個(gè)進(jìn)程在等待使用此臨界區(qū) )釋放鎖。<br> up定義在/include/linux/sched.h中:<br>extern inline void up(struct semaphore * sem)<br>{<br> sem->count++;<br> wake_up(&sem->wait);<br> }<p>kfree_s(a,b):kfree_s定義在/include/linux/malloc.h中:#define kfree_s(a,b) kfree(a)。而kfree()將在后面3.3中詳細(xì)討論。<p>avl_neighbours(struct vm_area_struct * node,* tree,** to_the_left,** to_the_right):輸入?yún)?shù)為作為查找條件的vma結(jié)點(diǎn)node、目標(biāo)mmap_avl樹(shù)tree、node左邊的結(jié)點(diǎn)以及它右邊的結(jié)點(diǎn)(左右邊結(jié)點(diǎn)按mmap_avl中各vma->vma_end大小排序)。avl_ neighbours的功能是根據(jù)查找條件node在目標(biāo)mmap_avl樹(shù)ptree中找到node左邊的結(jié)點(diǎn)以及它右邊的結(jié)點(diǎn),并返回。<p>avl_remove(struct vm_area_struct * node_to_delete, ** ptree):輸入?yún)?shù)為需要?jiǎng)h除的結(jié)點(diǎn)node_to_delete和目標(biāo)mmap_avl樹(shù)ptree。avl_remove的功能是在目標(biāo)mmap_avl樹(shù)ptree中找到結(jié)點(diǎn)node_to_delete并把它從平衡樹(shù)中刪除,并且調(diào)用avl_rebalance以保持ptree的平衡樹(shù)特性。<p>remove_shared_vm_struct(struct vm_area_struct *mpnt):輸入?yún)?shù)為需要從inode->immap環(huán)中刪除的vma結(jié)點(diǎn)mpnt。remove_shared_vm_struct的功能是從擁有vma結(jié)點(diǎn)mpnt 的inode->immap環(huán)中刪除的該結(jié)點(diǎn)。<p><br><center><A HREF="#Content">[目錄](méi)</A></center><hr><br><A NAME="I459" ID="I459"></A><center><b><font size=+2>添加新調(diào)用</font></b></center><br><center><A HREF="#Content">[目錄](méi)</A></center><hr><br><A NAME="I460" ID="I460"></A><center><b><font size=+2>例子一</font></b></center><br>深入LINUX內(nèi)核:為你的LINUX增加一條系統(tǒng)調(diào)用<br> 充分利用LINUX開(kāi)放源碼的特性,我們可以輕易地對(duì)它進(jìn)行修改,使我們能夠隨心所欲駕馭LINUX,完成一個(gè)真正屬于自己的操作系統(tǒng),這種感覺(jué)使無(wú)與倫比的,下面通過(guò)為L(zhǎng)INUX增加一個(gè)系統(tǒng)調(diào)用來(lái)展示LINUX作為一個(gè)開(kāi)放源碼操作系統(tǒng)的強(qiáng)大魅力。<br> 首先,讓我們簡(jiǎn)單地分析一下LINUX中與系統(tǒng)調(diào)用的相關(guān)的部分:<br> LINUX的系統(tǒng)調(diào)用的總控程序是system_call,它是LINUX系統(tǒng)中所有系統(tǒng)調(diào)用的總?cè)肟冢@個(gè)system_call是作為一個(gè)中斷服務(wù)程序掛在中斷0x80上,系統(tǒng)初始化時(shí)通過(guò)void init trap_init(void)調(diào)用一個(gè)宏set_system_ gate(SYSCALL_VERCTOR,&system_call)來(lái)對(duì)IDT表進(jìn)行初始化,在0x80對(duì)應(yīng)的中斷描述符處填入system_call函數(shù)的地址,其中宏SYSCALL_VERCTOR就是0x80。<br> 當(dāng)發(fā)生一條系統(tǒng)調(diào)用時(shí),由中斷總控程序保存處理機(jī)狀態(tài),檢查調(diào)用參數(shù)的合法性,然后根據(jù)系統(tǒng)調(diào)用向量在sys_call_table中找到相應(yīng)的系統(tǒng)服務(wù)例程的地址,然后執(zhí)行該服務(wù)例程,完成后恢復(fù)中斷總控程序所保存的處理機(jī)狀態(tài),返回用戶(hù)程序。<br> 系統(tǒng)服務(wù)例程一般定義于kernel/sys.c中,系統(tǒng)調(diào)用向量定義在include/asm-386/unistd.h中,而sys_call _table表則定義在arch/i386/kernel/entry.S文件里。<br> 現(xiàn)在我們知道增加一條系統(tǒng)調(diào)用我們首先要添加服務(wù)例程實(shí)現(xiàn)代碼,然后在進(jìn)行對(duì)應(yīng)向量的申明,最后當(dāng)然還要在sys_call_table表中增加一項(xiàng)以指明服務(wù)例程的入口地址。<br> OK,有了以上簡(jiǎn)單的分析,現(xiàn)在我們可以開(kāi)始進(jìn)行源碼的修改,假設(shè)我們需要添加一條系統(tǒng)調(diào)用計(jì)算兩個(gè)整數(shù)的平方和,系統(tǒng)調(diào)用名為add2,我們需要修改三個(gè)文件:kernel/sys.c , arch/i386/kernel/entry.S 和 include/asm-386/unistd.h。<br> 1、修改kernel/sys.c ,增加服務(wù)例程代碼:<br> asmlinkage int sys_add2(int a , int b)<br> {<br> int c=0;<br> c=a*a+b*b;<br> return c;<br> }<br> 2、修改include/asm-386/unistd.h ,對(duì)我們剛才增加的系統(tǒng)調(diào)用申明向量,以使用戶(hù)或系統(tǒng)進(jìn)程能夠找到這條系統(tǒng)調(diào)用,修改后文件如下所示:<br> .... .....<br> #define _NR_sendfile 187<br> #define _NR_getpmsg 188<br> #define _NR_putmsg 189<br> #define _NR_vfork 190<br> #define _NR_add2 191 /* 這是我們添加的部分,191即向量 */<br> 3、修改include/asm-386/unistd.h , 將服務(wù)函數(shù)入口地址加入 sys_call_table,首先找到這么一段:<br> .... .....<br> .long SYMBOL_NAME(sys_sendfile)<br> .long SYMBOL_NAME(sys_ni_syscall) /* streams 1 */<br> .long SYMBOL_NAME(sys_ni_syscall) /* streams 2 */<br>
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)