⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 6.html

📁 介绍linux下文件和设备编程
💻 HTML
📖 第 1 页 / 共 4 页
字号:
&middot;arg_start、arg_end:调用参数区的起始地址和结束地址。<br>
&middot;env_start、env_end:进程环境区的起始地址和结束地址。<br>
&middot;rss:进程内容驻留在物理内存的页面总数。<p>
<br>
2. 虚存段(vma)数据结构:vm_area_atruct<br>
&nbsp;&nbsp;&nbsp; 虚存段vma由数据结构vm_area_atruct(include/linux/mm.h)描述:<p>
struct vm_area_struct {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct mm_struct * vm_mm;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* VM area parameters */<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned long vm_start;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned long vm_end;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pgprot_t vm_page_prot;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned short vm_flags;<br>
/* AVL tree of VM areas per task, sorted by address */<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; short vm_avl_height;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct vm_area_struct * vm_avl_left;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct vm_area_struct * vm_avl_right;<br>
/* linked list of VM areas per task, sorted by address */<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct vm_area_struct * vm_next;<br>
/* for areas with inode, the circular list inode-&gt;i_mmap */<br>
/* for shm areas, the circular list of attaches */<br>
/* otherwise unused */<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct vm_area_struct * vm_next_share;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct vm_area_struct * vm_prev_share;<br>
/* more */<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct vm_operations_struct * vm_ops;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned long vm_offset;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct inode * vm_inode;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned long vm_pte;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* shared mem */<br>
};<p>
vm_start;//所对应内存区域的开始地址<br>
vm_end; //所对应内存区域的结束地址<br>
vm_flags; //进程对所对应内存区域的访问权限<br>
vm_avl_height;//avl树的高度<br>
vm_avl_left; //avl树的左儿子<br>
vm_avl_right; //avl树的右儿子<br>
vm_next;// 进程所使用的按地址排序的vm_area链表指针<br>
vm_ops;//一组对内存的操作<br>
&nbsp;&nbsp;&nbsp; 这些对内存的操作是当对虚存进行操作的时候Linux系统必须使用的一组方法。比如说,当进程准备访问某一虚存区域但是发现此区域在物理内存不存在时(缺页中断),就激发某种对内存的操作执行正确的行为。这种操作是空页(nopage)操作。当Linux系统按需调度可执行的页面映象进入内存时就使用这种空页(nopage)操作。<br>
&nbsp;&nbsp;&nbsp; 当一个可执行的页面映象映射到进程的虚存地址时,一组vm_area_struct结构的数据结构(vma)就会生成。每一个vm_area_struct的数据结构(vma)代表可执行的页面映象的一部分:可执行代码,初始化数据(变量),非初始化数据等等。Linux系统可以支持大量的标准虚存操作,当vm_area_struct数据结构(vma)一被创建,它就对应于一组正确的虚存操作。<br>
&nbsp;&nbsp;&nbsp; 属于同一进程的vma段通过vm_next指针连接,组成链表。如图2-3所示,struct mm_struct结构的成员struct vm_area_struct * mmap&nbsp; 表示进程的vma链表的表头。<br>
&nbsp;&nbsp;&nbsp; 为了提高对vma段 查询、插入、删除操作的速度,LINUX同时维护了一个AVL(Adelson-Velskii and Landis)树。在树中,所有的vm_area_struct虚存段均有左指针vm_avl_left指向相邻的低地址虚存段,右指针vm_avl_right指向相邻的高地址虚存段,如图2-5。struct mm_struct结构的成员struct vm_area_struct * mmap_avl表示进程的AVL树的根,vm_avl_height表示AVL树的高度。<br>
&nbsp;&nbsp;&nbsp; 对平衡树mmap_avl的任何操作必须满足平衡树的一些规则:<br>
Consistency and balancing rulesJ(一致性和平衡规则):<p>
tree-&gt;vm_avl_height==1+max(heightof(tree-&gt;vm_avl_left),heightof(<br>
tree-&gt;vm_avl_right))<br>
abs( heightof(tree-&gt;vm_avl_left) - heightof(tree-&gt;vm_avl_right) ) &lt;= 1<br>
foreach node in tree-&gt;vm_avl_left: node-&gt;vm_avl_key &lt;= tree-&gt;vm_avl_key,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach node in tree-&gt;vm_avl_right: node-&gt;vm_avl_key &gt;= tree-&gt;vm_avl_key.<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注:其中node-&gt;vm_avl_key= node-&gt;vm_end<p>
对vma可以进行加锁、加保护、共享和动态扩展等操作。<p>
<br>



<center><A HREF="#Content">[目录]</A></center>
<hr><br><A NAME="I457" ID="I457"></A><center><b><font size=+2>重要常量</font></b></center><br>
&nbsp;&nbsp;&nbsp; mlock系统调用所用到的重要常量有:PAGE_MASK、PAGE_SIZE、PAGE_SHIFT、RLIMIT_MEMLOCK、VM_LOCKED、 PF_SUPERPRIV等。它们的值分别如下:<p>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PAGE_SHIFT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 12&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // PAGE_SHIFT determines the page size<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PAGE_SIZE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x1000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //1UL&lt;&lt;PAGE_SHIFT<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PAGE_MASK&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ~(PAGE_SIZE-1)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //a very useful constant variable<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RLIMIT_MEMLOCK&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //max locked-in-memory address space<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; VM_LOCKED&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x2000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //8*1024=8192, vm_flags的标志之一。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PF_SUPERPRIV&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000100&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //512<p>



<center><A HREF="#Content">[目录]</A></center>
<hr><br><A NAME="I458" ID="I458"></A><center><b><font size=+2>代码函数功能分析</font></b></center><br>
mlock系统调用代码函数功能分析<p>
&nbsp;&nbsp;&nbsp; 下面对各个函数的功能作详细的分析((1)和(2)在前面简介mlock时已介绍过,并在后面有详细的程序流程):<p>
suser():如果用户有效(即current-&gt;euid == 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ),则设置进程标志为root优先权(current-&gt;flags |= PF_SUPERPRIV),并返回1;否则返回0。<p>
find_vma(struct mm_struct * mm, unsigned long addr):输入参数为当前进程的mm、需要加锁的开始内存地址addr。find_vma的功能是在mm的mmap_avl树中寻找第一个满足mm-&gt;mmap_avl-&gt;vm_start&lt;=addr&lt; mm-&gt;mmap_avl-&gt;vm_end的vma,如果成功则返回此vma;否则返回空null。<p>
mlock_fixup(struct vm_area_struct * vma, unsigned long start, unsigned long end, unsigned int newflags):输入参数为vm_mmap链中的某个vma、需要加锁内存区域起始地址和结束地址、需要修改的标志(0:加锁,1:释放锁)。<p>
merge_segments(struct mm_struct * mm, unsigned long start_addr, unsigned long end_addr):输入参数为当前进程的mm、需要加锁的开始内存地址start_addr和结束地址end_addr。merge_segments的功能的是尽最大可能归并相邻(即内存地址偏移量连续)并有相同属性(包括vm_inode,vm_pte,vm_ops,vm_flags)的内存段,在这过程中冗余的vm_area_structs被释放,这就要求vm_mmap链按地址大小排序(我们不需要遍历整个表,而只需要遍历那些交叉或者相隔一定连续区域的邻接vm_area_structs)。当然在缺省的情况下,merge_segments是对vm_mmap_avl树进行循环处理,有多少可以合并的段就合并多少。<p>
mlock_fixup_all(struct vm_area_struct * vma, int newflags):输入参数为vm_mmap链中的某个vma、需要修改的标志(0:加锁,1:释放锁)。mlock_fixup_all的功能是根据输入参数newflags修改此vma的vm_flags。<p>
mlock_fixup_start(struct vm_area_struct * vma,unsigned long end, int newflags):输入参数为vm_mmap链中的某个vma、需要加锁内存区域结束地址、需要修改的标志(0:加锁,1:释放锁)。mlock_fixup_start的功能是根据输入参数end,在内存中分配一个新的new_vma,把原来的vma分成两个部分: new_vma和vma,其中new_vma的vm_flags被设置成输入参数newflags;并且按地址(new_vma-&gt;start和new_vma-&gt;end)大小序列把新生成的new-&gt;vma插入到当前进程mm的mmap链或mmap_avl树中(缺省情况下是插入到mmap_avl树中)。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注:vma-&gt;vm_offset+= vma-&gt;vm_start-new_vma-&gt;vm_start;<br>
mlock_fixup_end(struct vm_area_struct * vma,unsigned long start, int newflags):输入参数为vm_mmap链中的某个vma、需要加锁内存区域起始地址、需要修改的标志(0:加锁,1:释放锁)。mlock_fixup_end的功能是根据输入参数start,在内存中分配一个新的new_vma,把原来的vma分成两个部分:vma和new_vma,其中new_vma的vm_flags被设置成输入参数newflags;并且按地址大小序列把new-&gt;vma插入到当前进程mm的mmap链或mmap_avl树中。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注:new_vma-&gt;vm_offset= vma-&gt;vm_offset+(new_vma-&gt;vm_start-vma-&gt;vm_start);<br>
mlock_fixup_middle(struct vm_area_struct * vma,unsigned long start, unsigned long end, int newflags):输入参数为vm_mmap链中的某个vma、需要加锁内存区域起始地址和结束地址、需要修改的标志(0:加锁,1:释放锁)。mlock_fixup_middle的功能是根据输入参数start、end,在内存中分配两个新vma,把原来的vma分成三个部分:left_vma、vma和right_vma,其中vma的vm_flags被设置成输入参数newflags;并且按地址大小序列把left-&gt;vma和right-&gt;vma插入到当前进程mm的mmap链或mmap_avl树中。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注:vma-&gt;vm_offset += vma-&gt;vm_start-left_vma-&gt;vm_start;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; right_vma-&gt;vm_offset += right_vma-&gt;vm_start-left_vma-&gt;vm_start;<p>
kmalloc():常用的一个内核函数<p>
insert_vm_struct(struct mm_struct *mm, struct vm_area_struct *vmp):输入参数为当前进程的mm、需要插入的vmp。insert_vm_struct的功能是按地址大小序列把vmp插入到当前进程mm的mmap链或mmap_avl树中,并且把vmp插入到vmp-&gt;inode的i_mmap环(循环共享链)中。<p>
avl_insert_neighbours(struct vm_area_struct * new_node,** ptree,** to_the_left,** to_the_right):输入参数为当前需要插入的新vma结点new_node、目标mmap_avl树ptree、新结点插入ptree后它左边的结点以及它右边的结点(左右边结点按mmap_avl中各vma-&gt;vma_end大小排序)。avl_insert_neighbours的功能是插入新vma结点new_node到目标mmap_avl树ptree中,并且调用avl_rebalance以保持ptree的平衡树特性,最后返回new_node左边的结点以及它右边的结点。<p>
avl_rebalance(struct vm_area_struct *** nodeplaces_ptr, int count):输入参数为指向vm_area_struct指针结构的指针数据nodeplaces_ptr[](每个元素表示需要平衡的mmap_avl子树)、数据元素个数count。avl_rebalance的功能是从nodeplaces_ptr[--count]开始直到nodeplaces_ptr[0]循环平衡各个mmap_avl子树,最终使整个mmap_avl树平衡。<p>
down(struct semaphore * sem):输入参数为同步(进入临界区)信号量sem。down的功能根据当前信号量的设置情况加锁(阻止别的进程进入临界区)并继续执行或进入等待状态(等待别的进程执行完成退出临界区并释放锁)。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; down定义在/include/linux/sched.h中:<br>
extern inline void down(struct semaphore * sem)<br>
{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (sem-&gt;count &lt;= 0)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __down(sem);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sem-&gt;count--;<br>
}<p>
up(struct semaphore * sem)输入参数为同步(进入临界区)信号量sem。up的功能根据当前信号量的设置情况(当信号量的值为负数:表示有某个进程在等待使用此临界区 )释放锁。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; up定义在/include/linux/sched.h中:<br>
extern inline void up(struct semaphore * sem)<br>
{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sem-&gt;count++;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wake_up(&amp;sem-&gt;wait);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<p>
kfree_s(a,b):kfree_s定义在/include/linux/malloc.h中:#define kfree_s(a,b) kfree(a)。而kfree()将在后面3.3中详细讨论。<p>
avl_neighbours(struct vm_area_struct * node,* tree,** to_the_left,** to_the_right):输入参数为作为查找条件的vma结点node、目标mmap_avl树tree、node左边的结点以及它右边的结点(左右边结点按mmap_avl中各vma-&gt;vma_end大小排序)。avl_ neighbours的功能是根据查找条件node在目标mmap_avl树ptree中找到node左边的结点以及它右边的结点,并返回。<p>
avl_remove(struct vm_area_struct * node_to_delete, ** ptree):输入参数为需要删除的结点node_to_delete和目标mmap_avl树ptree。avl_remove的功能是在目标mmap_avl树ptree中找到结点node_to_delete并把它从平衡树中删除,并且调用avl_rebalance以保持ptree的平衡树特性。<p>
remove_shared_vm_struct(struct vm_area_struct *mpnt):输入参数为需要从inode-&gt;immap环中删除的vma结点mpnt。remove_shared_vm_struct的功能是从拥有vma结点mpnt 的inode-&gt;immap环中删除的该结点。<p>
<br>



<center><A HREF="#Content">[目录]</A></center>
<hr><br><A NAME="I459" ID="I459"></A><center><b><font size=+2>添加新调用</font></b></center><br>



<center><A HREF="#Content">[目录]</A></center>
<hr><br><A NAME="I460" ID="I460"></A><center><b><font size=+2>例子一</font></b></center><br>
深入LINUX内核:为你的LINUX增加一条系统调用<br>
  充分利用LINUX开放源码的特性,我们可以轻易地对它进行修改,使我们能够随心所欲驾驭LINUX,完成一个真正属于自己的操作系统,这种感觉使无与伦比的,下面通过为LINUX增加一个系统调用来展示LINUX作为一个开放源码操作系统的强大魅力。<br>
  首先,让我们简单地分析一下LINUX中与系统调用的相关的部分:<br>
  LINUX的系统调用的总控程序是system_call,它是LINUX系统中所有系统调用的总入口,这个system_call是作为一个中断服务程序挂在中断0x80上,系统初始化时通过void init trap_init(void)调用一个宏set_system_ gate(SYSCALL_VERCTOR,&amp;system_call)来对IDT表进行初始化,在0x80对应的中断描述符处填入system_call函数的地址,其中宏SYSCALL_VERCTOR就是0x80。<br>
  当发生一条系统调用时,由中断总控程序保存处理机状态,检查调用参数的合法性,然后根据系统调用向量在sys_call_table中找到相应的系统服务例程的地址,然后执行该服务例程,完成后恢复中断总控程序所保存的处理机状态,返回用户程序。<br>
  系统服务例程一般定义于kernel/sys.c中,系统调用向量定义在include/asm-386/unistd.h中,而sys_call _table表则定义在arch/i386/kernel/entry.S文件里。<br>
  现在我们知道增加一条系统调用我们首先要添加服务例程实现代码,然后在进行对应向量的申明,最后当然还要在sys_call_table表中增加一项以指明服务例程的入口地址。<br>
  OK,有了以上简单的分析,现在我们可以开始进行源码的修改,假设我们需要添加一条系统调用计算两个整数的平方和,系统调用名为add2,我们需要修改三个文件:kernel/sys.c , arch/i386/kernel/entry.S 和 include/asm-386/unistd.h。<br>
  1、修改kernel/sys.c ,增加服务例程代码:<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 ,对我们刚才增加的系统调用申明向量,以使用户或系统进程能够找到这条系统调用,修改后文件如下所示:<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 , 将服务函数入口地址加入 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>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -