仓酷云

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 826|回复: 16
打印 上一主题 下一主题

[其他Linux] 给大家带来linux内存办理之非一连物理地点分派(vmalloc)

[复制链接]
愤怒的大鸟 该用户已被删除
跳转到指定楼层
楼主
发表于 2015-1-16 12:01:49 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
linux系统的文件布置,etc/,opt/目录的内容等;
后面我们已剖析了linux怎样使用同伴体系,slab分派器分派内存,用这些办法失掉的内存在物理地点上都是一连的,但是,有些时分,每次哀求内存时,体系都分派物理地点一连的内存块是分歧适的,能够使用小块内存“毗连”成年夜块可以使用的内存.这在操纵体系计划中也被称为“内存拼接”,明显,内存拼接在必要较年夜内存,而内存会见比拟之下不是很频仍的情形下是对照无效的.
  在linux内核顶用来办理内存拼接的接口是vmalloc/vfree.用vmalloc分派失掉的内存在线性地点是光滑的,可是物理地点上长短一连的.
  一:筹办常识:
  Linux用vm_struct布局来暗示vmalloc利用的线性地点.vmalloc所利用的线性地点区间为:VMALLOC_STARTVMALLOC_END.借用<<Understanding.the.Linux.Kernel.3rd>>中的一副插图,以下示:
  从上图中我们能够看到每个vmalloc_area用4KB离隔,如许做是为了很简单就可以捕获到越界会见,由于两头是一个“朴陋”.
  二:相干的数据布局
  上面来剖析一下vmallocarea的数据布局:
  structvm_struct{
  void*addr;//假造地点
  unsignedlongsize;//vm的巨细
  unsignedlongflags;//vm的标记
  structpage**pages;//vm所映照的page
  unsignedintnr_pages;//page个数
  unsignedlongphys_addr;//对应的肇端物理地点
  structvm_struct*next;//下一个vm.用来构成链表
  }
  全局变量vmlist用来办理vm组成的链表
  全局变量vmlist用于会见vmlist所利用的旌旗灯号量
  关于vm_struct有两个经常使用的操纵:get_vm_area/remove_vm_area
  get_vm_area:用来分派一个符合巨细的vm布局,分派乐成以后,将其链进到vmlist中,代码在mm/vmalloc.c中.以下示:
  //size为vm的巨细
  structvm_struct*get_vm_area(unsignedlongsize,unsignedlongflags)
  {
  //在VMALLOC_START与VMALLOC_END找到一段符合的空间
  return__get_vm_area(size,flags,VMALLOC_START,VMALLOC_END);
  }
  //参数申明:
  //start:肇端地点end:停止地点size空间巨细
  structvm_struct*__get_vm_area(unsignedlongsize,unsignedlongflags,
  unsignedlongstart,unsignedlongend)
  {
  structvm_struct**p,*tmp,*area;
  unsignedlongalign=1;
  unsignedlongaddr;
  //假如指定了VM_IOREMAP.则调剂对齐因子
  if(flags&VM_IOREMAP){
  intbit=fls(size);
  if(bit>IOREMAP_MAX_ORDER)
  bit=IOREMAP_MAX_ORDER;
  elseif(bit<PAGE_SHIFT)
  bit=PAGE_SHIFT;
  align=1ul<<bit;
  }
  //将肇端地点依照对齐因子对齐
  addr=ALIGN(start,align);
  //分派一个vm_struct布局空间
  area=kmalloc(sizeof(*area),GFP_KERNEL);
  if(unlikely(!area))
  returnNULL;
  //PAGE_SIZE:在i32中为4KB,即下面所说的距离朴陋
  size+=PAGE_SIZE;
  if(unlikely(!size)){
  kfree(area);
  returnNULL;
  }
  write_lock(&vmlist_lock);
  //遍历vmlist:找到符合巨细的末利用空间
  for(p=&vmlist;(tmp=*p)!=NULL;p=&tmp-&
12345下一页


在linux中学习命令的最好办法是学习Shell脚本编程,Shell脚本比起其他语言来学习简单,但是功能却十分强大.通过学习Shell编程,能让你掌握大量的linux命令。
愤怒的大鸟 该用户已被删除
沙发
 楼主| 发表于 2015-1-16 13:05:57 | 只看该作者

给大家带来linux内存办理之非一连物理地点分派(vmalloc)

有些人号称用过十几种甚至几十种linux,向人谈论起来头头是到,好像懂的很多。
gt;next){</P>  //若肇端地点落在某一个vm区间,则调剂肇端地点为vm区间的开端
  if((unsignedlong)tmp->addr<addr){
  if((unsignedlong)tmp->addr+tmp->size>=addr)
  addr=ALIGN(tmp->size+
  (unsignedlong)tmp->addr,align);
  continue;
  }
  //size+addr<addr?除非size==0
  if((size+addr)<addr)
  gotoout;
  //两头的清闲能够包容下size巨细的vm.申明已找到了如许的一个vm
  if(size+addr<=(unsignedlong)tmp->addr)
  gotofound;
  //调剂肇端地点为vm的停止地点
  addr=ALIGN(tmp->size+(unsignedlong)tmp->addr,align);
  //假如超越了局限
  if(addr>end-size)
  gotoout;
  }
  found:
  //找到了符合巨细的空间,将area->addr赋值为addr,然后链进vmlist中
  area->next=*p;
  *p=area;
  area->flags=flags;
  area->addr=(void*)addr;
  area->size=size;
  area->pages=NULL;
  area->nr_pages=0;
  area->phys_addr=0;
  write_unlock(&vmlist_lock);
  returnarea;
  out:
  //没有找到符合巨细的空间,堕落前往
  write_unlock(&vmlist_lock);
  kfree(area);
  if(printk_ratelimit())
  printk(KERN_WARNING"allocationfailed:outofvmallocspace-usevmalloc=<size>toincreasesize.\n");
  returnNULL;
  }
  这段代码不是很庞大,在此不具体剖析了.
  remove_vm_area用来将响应的vm从vmlist中止开,使其暗示的空间能够被使用
  //addr:对应vm的超始地点
  structvm_struct*remove_vm_area(void*addr)
  {
  structvm_struct**p,*tmp;
  write_lock(&vmlist_lock);
  //遍历vmlist.找到超始地点为addr的vm
  for(p=&vmlist;(tmp=*p)!=NULL;p=&tmp->next){
  if(tmp->addr==addr)
  gotofound;
  }
  write_unlock(&vmlist_lock);
  returnNULL;
  found:
  //断开tmp所对应的映照干系
  unmap_vm_area(tmp);
  //找到了这个vm,将其从vmlist上断开
  *p=tmp->next;
  write_unlock(&vmlist_lock);
  returntmp;
  }
  unmap_vm_area用来断开vm地点线性地点所对应的映照干系.它的代码以下:
  voidunmap_vm_area(structvm_struct*area)
  {
  //vm所对应的肇端线性地点
  unsignedlongaddress=(unsignedlong)area->addr;
  //vm所对应的停止线性地点
  unsignedlongend=(address+area->size);
  pgd_t*dir;
  //肇端地点地点的内核页目次项
  dir=pgd_offset_k(address);
  flush_cache_vunmap(address,end);
  do{
  //断开地点所对应的pmd映照
  unmap_area_pmd(dir,address,end-address);
  //运转到这里的时分,已断开了一个页目次所暗示的线性地点,而每一个页目次暗示的线性地点//巨细为PGDIR_SIZE
  address=(address+PGDIR_SIZE)&PGDIR_MASK;
  dir++;
  }while(address&&(address<end));
  //当抵达开端时停止轮回
  flush_tlb_kernel_range((unsignedlong)area->addr,end);
  }
  //断开线性地点区间地点的pmd的映照
  st
上一页12345下一页


使用gcc或g++进行编译,使用gdb进行调试;
愤怒的大鸟 该用户已被删除
板凳
 楼主| 发表于 2015-1-16 13:10:17 | 只看该作者

给大家带来linux内存办理之非一连物理地点分派(vmalloc)

vim除非你打算真正的学好linux,或者说打算长久时间学习他,而且肯花大量时间vim,否则,最好别碰
aticvoidunmap_area_pmd(pgd_t*dir,unsignedlongaddress,</P>  unsignedlongsize)
  {
  unsignedlongend;
  pmd_t*pmd;
  if(pgd_none(*dir))
  return;
  if(pgd_bad(*dir)){
  pgd_ERROR(*dir);
  pgd_clear(dir);
  return;
  }
  pmd=pmd_offset(dir,address);
  address&=~PGDIR_MASK;
  end=address+size;
  if(end>PGDIR_SIZE)
  end=PGDIR_SIZE;
  do{
  //断开线性地点地点的pte的映照干系
  unmap_area_pte(pmd,address,end-address);
  address=(address+PMD_SIZE)&PMD_MASK;
  pmd++;
  }while(address<end);
  }
  staticvoidunmap_area_pte(pmd_t*pmd,unsignedlongaddress,
  unsignedlongsize)
  {
  unsignedlongend;
  pte_t*pte;
  if(pmd_none(*pmd))
  return;
  if(pmd_bad(*pmd)){
  pmd_ERROR(*pmd);
  pmd_clear(pmd);
  return;
  }
  pte=pte_offset_kernel(pmd,address);
  address&=~PMD_MASK;
  end=address+size;
  if(end>PMD_SIZE)
  end=PMD_SIZE;
  do{
  pte_tpage;
  //扫除pte的对应映照干系
  page=ptep_get_and_clear(pte);
  address+=PAGE_SIZE;
  pte++;
  if(pte_none(page))
  continue;
  if(pte_present(page))
  continue;
  printk(KERN_CRIT"Whee..Swappedoutpageinkernelpagetable\n");
  }while(address<end);
  }
  经由这几个历程以后,实践上,它只是找到线性地点所对应的pte,然后断开pte的映照.值得注重的是:为了效力起见,这里只是断开了pte的映照,即只是将pte置为none,暗示pte末映照内存.并末断开pmd和pgd的映照
三:vmalloc的完成:
  void*vmalloc(unsignedlongsize)
  {
  return__vmalloc(size,GFP_KERNEL|__GFP_HIGHMEM,PAGE_KERNEL);
  }
  实践上挪用__vmalloc:
  void*__vmalloc(unsignedlongsize,intgfp_mask,pgprot_tprot)
  {
  structvm_struct*area;
  structpage**pages;
  unsignedintnr_pages,array_size,i;
  //使哀求的巨细与页框对齐
  size=PAGE_ALIGN(size);
  //无效性反省
  if(!size||(size>>PAGE_SHIFT)>num_physpages)
  returnNULL;
  //获得一个无效的VM,这个函数我们在后面已具体的剖析过了
  area=get_vm_area(size,VM_ALLOC);
  if(!area)
  returnNULL;
  //所要映照的页面总数
  nr_pages=size>>PAGE_SHIFT;
  //页面形貌符所占的空间
  array_size=(nr_pages*sizeof(structpage*));
  area->nr_pages=nr_pages;
  area->pages=pages=kmalloc(array_size,(gfp_mask&~__GFP_HIGHMEM));
  //假如空间分派失利
  if(!area->pages){
  remove_vm_area(area->addr);
  kfree(area);
  returnNULL;
  }
  memset(area->pages,0,array_size);
  //为每个页面分派空间
  for(i=0;i<area->nr_pages;i++){
  area->pages=al
上一页12345下一页


写学习日记,这是学习历程的见证,同时我坚持认为是增强学习信念的法宝。以上是我学习Linux的心得体会,希望对大家的学习有所帮助,由于水平有限,本文难免有所欠缺,望请指正。
愤怒的大鸟 该用户已被删除
地板
 楼主| 发表于 2015-1-16 13:11:49 | 只看该作者

给大家带来linux内存办理之非一连物理地点分派(vmalloc)

系统管理相关命令:df、top、free、quota、at、lp、adduser、groupaddkill、crontab、tar、unzip、gunzip、last
loc_page(gfp_mask);</P>  if(unlikely(!area->pages)){
  /*Successfullyallocatedipages,freethemin__vunmap()*/
  area->nr_pages=i;
  gotofail;
  }
  }
  //为所分派的页面创建映照干系
  if(map_vm_area(area,prot,&pages))
  gotofail;
  returnarea->addr;
  fail:
  vfree(area->addr);
  returnNULL;
  }
  map_vm_area为所分派的内存创建映照干系,它的程序流程与unmap_vm_area差未几,都是从pgd找到pte,假如一样的映照干系不存在,则新建之.(如:pgd对应的pmd不存在,则新建pmd项,使pgd指向建好的pmd.同理,假如pmd所映照的pte项不存在,则新建pte,然后创建映照),然后将pte映照到响应的页表.代码以下:
  intmap_vm_area(structvm_struct*area,pgprot_tprot,structpage***pages)
  {
  unsignedlongaddress=(unsignedlong)area->addr;
  unsignedlongend=address+(area->size-PAGE_SIZE);
  pgd_t*dir;
  interr=0;
  //vm肇端地点地点的页目次
  dir=pgd_offset_k(address);
  spin_lock(&init_mm.page_table_lock);
  do{
  pmd_t*pmd=pmd_alloc(&init_mm,dir,address);
  if(!pmd){
  err=-ENOMEM;
  break;
  }
  //轮到pmd了^_^
  if(map_area_pmd(pmd,address,end-address,prot,pages)){
  err=-ENOMEM;
  break;
  }
  address=(address+PGDIR_SIZE)&PGDIR_MASK;
  dir++;
  }while(address&&(address<end));
  spin_unlock(&init_mm.page_table_lock);
  flush_cache_vmap((unsignedlong)area->addr,end);
  returnerr;
  }
  staticintmap_area_pmd(pmd_t*pmd,unsignedlongaddress,
  unsignedlongsize,pgprot_tprot,
  structpage***pages)
  {
  unsignedlongbase,end;
  base=address&PGDIR_MASK;
  address&=~PGDIR_MASK;
  end=address+size;
  if(end>PGDIR_SIZE)
  end=PGDIR_SIZE;
  do{
  pte_t*pte=pte_alloc_kernel(&init_mm,pmd,base+address);
  if(!pte)
  return-ENOMEM;
  //轮到pte了^_^
  if(map_area_pte(pte,address,end-address,prot,pages))
  return-ENOMEM;
  address=(address+PMD_SIZE)&PMD_MASK;
  pmd++;
  }while(address<end);
  return0;
  }
  //为页表页创建映照干系
  staticintmap_area_pte(pte_t*pte,unsignedlongaddress,
  unsignedlongsize,pgprot_tprot,
  structpage***pages)
  {
  unsignedlongend;
  address&=~PMD_MASK;
  end=address+size;
  if(end>PMD_SIZE)
  end=PMD_SIZE;
  do{
  structpage*page=**pages;
  WARN_ON(!pte_none(*pte));
  if(!page)
  return-ENOMEM;
  //详细的映照在这里了^_^
  set_pte(pte,mk_pte(page,prot));
  address+=PAGE_SIZE;
  pte++;
  (*pages)++;
  }while(add
上一页12345下一页


看不懂man文档的人.在linux中,命令可分为系统基本命令和应用程序命令.系统基本命令是所有的unix类系统都支持的命令,走到哪都不变,只要是unix类系统上就肯定有.
愤怒的大鸟 该用户已被删除
5#
 楼主| 发表于 2015-1-16 13:30:27 | 只看该作者

给大家带来linux内存办理之非一连物理地点分派(vmalloc)

不同版本的Linux命令数量不一样,这里笔者把它们中比较重要的和使用频率最多的命令。
ress<end);</P>  return0;
  }
  只需了解了断开映照的历程,这段代码是很好了解的.
  总而言之:linux在创建映照的时分,从pgd到pte响应的创建映照干系,最初将pte映照到分派失掉的物理内存.而在断开映照的时分,linux内核从pgd找到pte,然后将pte置为none,暗示pte末创建映照干系.
  四:vfree的完成:
  代码以下:
  voidvfree(void*addr)
  {
  BUG_ON(in_interrupt());
  __vunmap(addr,1);
  }
  跟踪至__vunmap:
  void__vunmap(void*addr,intdeallocate_pages)
  {
  structvm_struct*area;
  //参数无效性反省
  if(!addr)
  return;
  //判别addr是不是是按页框对齐的
  if((PAGE_SIZE-1)&(unsignedlong)addr){
  printk(KERN_ERR"Tryingtovfree()badaddress(%p)\n",addr);
  WARN_ON(1);
  return;
  }
  //remove_vm_area:这个函数我们在之前已剖析过了^_^
  area=remove_vm_area(addr);
  if(unlikely(!area)){
  //没有找到肇端地点为addr的vm.则有效,加入
  printk(KERN_ERR"Tryingtovfree()nonexistentvmarea(%p)\n",
  addr);
  WARN_ON(1);
  return;
  }
  if(deallocate_pages){
  inti;
  for(i=0;i<area->nr_pages;i++){
  if(unlikely(!area->pages))
  BUG();
  //开释哀求取得的页面
  __free_page(area->pages);
  }
  //开释分派的page形貌符
  kfree(area->pages);
  }
  //开释内核的vm形貌符
  kfree(area);
  return;
  }
  五:总结
  经由下面的剖析,我们能够看到,vmalloc分派内存的历程是非常低效的,不但要从同伴体系中取内存并且要创建映照干系,明显,用vmalloc分派较小的内存是分歧算的。别的。有个成绩值得思索一下:为何用__get_free_page不必要创建映照干系,而vmalloc就必要呢?
  实在,不论利用何种体例。线性地点到物理地点的转换终极都要经由硬件的页式办理往完成。所分歧的是__get_free_page前往的线性地点是属于(PAGE_OFFSET,HIGH_MEMORY)之间的,这段线性地点在内核初始化的时分就完成了映照。而vmalloc利用的线性地点是属于(VMALLOC_STARTVMALLOC_END)之间的,也就是说属于一个一时映照区,以是必需为其创建映照干系
</p>
上一页12345


如果你学不好的话,你在linux中开发的机会就很少,或者说几乎没有,它的优势就消失了,然后随着时间的流逝,你就会全部忘记她;
飘飘悠悠 该用户已被删除
6#
发表于 2015-1-18 16:51:07 | 只看该作者
发问的时候一定要注意到某些礼节。因为Linux社区是一个松散的组织、也不承担回复每个帖子的义务。它不是技术支持。
第二个灵魂 该用户已被删除
7#
发表于 2015-1-27 13:15:18 | 只看该作者
如果你想深入学习Linux,看不懂因为文档实在是太难了。写的最好的、最全面的文档都是英语写的,最先发布的技术信息也都是用英语写的。
海妖 该用户已被删除
8#
发表于 2015-2-5 13:51:20 | 只看该作者
通过自学老师给的资料和向同学请教,掌握了一些基本的操作,比如挂载优盘,编译程序,在Linux环境下运行,转换目录等等。学了这些基础才能进行下面的模拟OS程序。?
山那边是海 该用户已被删除
9#
发表于 2015-2-12 02:57:11 | 只看该作者
Linux是参照Unix思想设计的,理解掌握Linux必须按照Unix思维来进行。思想性的转变比暂时性的技术提高更有用,因为他能帮助你加快学习速度。
小女巫 该用户已被删除
10#
发表于 2015-3-2 22:25:17 | 只看该作者
熟悉系统的基本操作,Linux的图形界面直观,操作简便,多加上机练习就可熟悉操作,在Linux下学习办公软件等常用软件。
活着的死人 该用户已被删除
11#
发表于 2015-3-11 08:36:00 | 只看该作者
如果上面的措施没有解决问题,此时你就需要Linux社区的帮助了。 Linux的使用者一般都是专业人士,他们有着很好的电脑背景且愿意协助他人。
兰色精灵 该用户已被删除
12#
发表于 2015-3-18 07:38:24 | 只看该作者
我学习Linux的心得体会 ,希望对大家的学习有所帮助,由于水平有限,本文难免有所欠缺,望请指正。
只想知道 该用户已被删除
13#
发表于 2015-3-25 19:16:01 | 只看该作者
linux鸟哥的私房菜,第三版,基础篇,网上有pdf下的,看它的目录和每章的介绍就行了,这个绝对原创!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|仓酷云 鄂ICP备14007578号-2

GMT+8, 2024-5-15 18:54

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表