文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>linux 1.0 内核注解 linux/mm/mmap.c

linux 1.0 内核注解 linux/mm/mmap.c

时间:2009-03-17  来源:taozhijiangscu

/********************************************
 *Created By: 陶治江
 *Date:       2009-3-16
 ********************************************/
#include <linux/stat.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/shm.h>
#include <linux/errno.h>
#include <linux/mman.h>
#include <linux/string.h>
#include <linux/malloc.h>
#include <asm/segment.h>
#include <asm/system.h>
static int anon_map(struct inode *, struct file *,
      unsigned long, size_t, int,
      unsigned long);
/*
 * description of effects of mapping type and prot in current implementation.
 * this is due to the current handling of page faults in memory.c. the expected
 * behavior is in parens:
 *
 * map_type prot
 *  PROT_NONE PROT_READ PROT_WRITE PROT_EXEC
 * MAP_SHARED r: (no) yes r: (yes) yes r: (no) yes r: (no) no
 *  w: (no) yes w: (no) copy w: (yes) yes w: (no) no
 *  x: (no) no x: (no) no x: (no) no x: (yes) no
 *  
 * MAP_PRIVATE r: (no) yes r: (yes) yes r: (no) yes r: (no) no
 *  w: (no) copy w: (no) copy w: (copy) copy w: (no) no
 *  x: (no) no x: (no) no x: (no) no x: (yes) no
 *
 */
#define CODE_SPACE(addr) \
 (PAGE_ALIGN(addr) < current->start_code + current->end_code)
int do_mmap(struct file * file, unsigned long addr, unsigned long len,
 unsigned long prot, unsigned long flags, unsigned long off)
{
 int mask, error;
 if ((len = PAGE_ALIGN(len)) == 0) //PAGE_ALIGN,将指针对齐到下一个页面的起始地址
  return addr;
 //TASK_SIZE,用户进程空间:3G,这里是检查起始地址addr以及长度等信息是否合法
 if (addr > TASK_SIZE || len > TASK_SIZE || addr > TASK_SIZE-len)
  return -EINVAL;
 if (file != NULL)
  switch (flags & MAP_TYPE)
  {
  //MAP_SHARED表示执行线性区中的页可以被几个进程所共享
  //MAP_PRIVATE 含义相反
  case MAP_SHARED:
   //如果映射页面要求可被写,但是文件访问模式不允许写,出错
   if ((prot & PROT_WRITE) && !(file->f_mode & 2))
    return -EACCES;
   /* fall through */
  case MAP_PRIVATE: //文件不可被读,返回,好像读是至少的权限了
   if (!(file->f_mode & 1))
    return -EACCES;
   break;
  default:
   return -EINVAL;
  }
 /*
  * obtain the address to map to. we verify (or select) it and ensure
  * that it represents a valid section of the address space.
  */
 if (flags & MAP_FIXED) { 
  //表示区间的起始地址必须由addr所指定
  //那么这样来说,addr必须是页的边界才合法了
  if (addr & ~PAGE_MASK) 
   return -EINVAL;
  if (len > TASK_SIZE || addr > TASK_SIZE - len)
   return -EINVAL;
 } else {
  struct vm_area_struct * vmm;
  /* Maybe this works.. Ugly it is. */
  //这里的1G-1.5G可能是对共享内存的一个硬限制了~~~
  addr = SHM_RANGE_START;    /*1G*/
  while (addr+len < SHM_RANGE_END)  /*1.5G*/
  {
   //这里是进行寻找,至少是寻找vm_start vm_end之间有交集
   for (vmm = current->mmap ; vmm ; vmm = vmm->vm_next)
   {
    if (addr >= vmm->vm_end)
     continue;
    if (addr + len <= vmm->vm_start)
     continue;
    // addr + len >= vm_start && addr <= vm_end
    addr = PAGE_ALIGN(vmm->vm_end);
    break;
   }
   if (!vmm)
    break;
  }
  if (addr+len >= SHM_RANGE_END)
   return -ENOMEM;
 }
 /*
  * determine the object being mapped and call the appropriate
  * specific mapper. the address has already been validated, but
  * not unmapped, but the maps are removed from the list.
  */
 if (file && (!file->f_op || !file->f_op->mmap))  //文件
  return -ENODEV;
  
 mask = 0;
 if (prot & (PROT_READ | PROT_EXEC))
  mask |= PAGE_READONLY;
  
 if (prot & PROT_WRITE)
  if ((flags & MAP_TYPE) == MAP_PRIVATE)  //独有的
   mask |= PAGE_COPY;
  else
   mask |= PAGE_SHARED;
   
 if (!mask)
  return -EINVAL;
 do_munmap(addr, len); /* Clear old maps */  if (file)
  error = file->f_op->mmap(file->f_inode, file, addr, len, mask, off);
 else  //匿名映射
  error = anon_map(NULL, NULL, addr, len, mask, off);
 
 if (!error)
  return addr;     //success
 if (!current->errno)
  current->errno = -error;  //设置错误标号
 return -1;
}
asmlinkage int sys_mmap(unsigned long *buffer)
{
 int error;
 unsigned long flags;
 struct file * file = NULL;
 error = verify_area(VERIFY_READ, buffer, 6*4);
 if (error)
  return error;
  
 flags = get_fs_long(buffer+3);
 
 if (!(flags & MAP_ANONYMOUS))  //MAP_ANONYMOUS表示没有文件同这个线性区相关
 {
  //!MAP_ANONYMOUS
  unsigned long fd = get_fs_long(buffer+4);
  if (fd >= NR_OPEN || !(file = current->filp[fd]))
   return -EBADF;
 }
 
 //int do_mmap(struct file * file, unsigned long addr, unsigned long len,
 //unsigned long prot, unsigned long flags, unsigned long off)
 //从此就可以看出了buffer的参数的结构了吧
 return do_mmap(file, get_fs_long(buffer), get_fs_long(buffer+1),
  get_fs_long(buffer+2), flags, get_fs_long(buffer+5));
}
//应该这个函数是一个通用的umap函数
//实际是对区域(addr,addr+len)以及area的范围来调用特定的函数的
//要删除的区域就是(addr,addr+len)
void unmap_fixup(struct vm_area_struct *area,unsigned long addr, size_t len)
{
 struct vm_area_struct *mpnt;
 unsigned long end = addr + len;
 //不能进行增加的哦
 if (addr < area->vm_start || addr >= area->vm_end ||
     end <= area->vm_start || end > area->vm_end ||
     end < addr)
 {
  printk("unmap_fixup: area=%lx-%lx, unmap %lx-%lx!!\n",
         area->vm_start, area->vm_end, addr, end);
  return;
 }
 //整个区域的删除,就直接调用了close进行删除线性区了
 if (addr == area->vm_start && end == area->vm_end) {
  if (area->vm_ops && area->vm_ops->close)
   area->vm_ops->close(area);
  return;
 }
 //(addr,addr+len)  //对于一个方向的缩减
 if (addr >= area->vm_start && end == area->vm_end)
  area->vm_end = addr;
  
 if (addr == area->vm_start && end <= area->vm_end)
 {
  area->vm_offset += (end - area->vm_start);  //截取的大小照成的偏移吧
  area->vm_start = end;
 }
 /* Unmapping a hole */
 //中间的截取,两边剩余,而中间将产生空洞
 //实际是创建了两个小的区域了
 if (addr > area->vm_start && end < area->vm_end)
 {
  /* Add end mapping -- leave beginning for below */
  mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL);
  *mpnt = *area;
  mpnt->vm_offset += (end - area->vm_start);
  mpnt->vm_start = end;  //后半个~~~
  if (mpnt->vm_inode)
   mpnt->vm_inode->i_count++;
  insert_vm_struct(current, mpnt);
  area->vm_end = addr; /* Truncate area */
 }
 
 //will fall through
 /* construct whatever mapping is needed */
 mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL);
 *mpnt = *area;  //值复制
 insert_vm_struct(current, mpnt);
}

asmlinkage int sys_mprotect(unsigned long addr, size_t len, unsigned long prot)
{
 return -EINVAL; /* Not implemented yet */
}
asmlinkage int sys_munmap(unsigned long addr, size_t len)
{
 return do_munmap(addr, len);
}
/*
 * Munmap is split into 2 main parts -- this part which finds
 * what needs doing, and the areas themselves, which do the
 * work.  This now handles partial unmappings.
 * Jeremy Fitzhardine <[email protected]>
 */
//addr必须页对齐
int do_munmap(unsigned long addr, size_t len)
{
 struct vm_area_struct *mpnt, **npp, *free;
 if ((addr & ~PAGE_MASK) || addr > TASK_SIZE || len > TASK_SIZE-addr)
  return -EINVAL;
 if ((len = PAGE_ALIGN(len)) == 0)
  return 0;
 /*
  * Check if this memory area is ok - put it on the temporary
  * list if so..  The checks here are pretty simple --
  * every area affected in some way (by any overlap) is put
  * on the list.  If nothing is put on, nothing is affected.
  */
 npp = &current->mmap; //一个链表作用
 
 free = NULL;
 for (mpnt = *npp; mpnt != NULL; mpnt = *npp)
 {
  unsigned long end = addr+len;
  //对于这个检查,是没有任何交汇的,当然没有什么要释放的了~~~
  //难道可以addr<end不是一定的啊?,呃或许这里允许len<0吧
  if ((addr < mpnt->vm_start && end <= mpnt->vm_start) ||
      (addr >= mpnt->vm_end && end > mpnt->vm_end))
  {
   npp = &mpnt->vm_next;
   continue;
  }
  *npp = mpnt->vm_next;
  mpnt->vm_next = free;
  free = mpnt;   //这里需要注意的是 free是指向的一个临时链表的结构!!
 }
 if (free == NULL)
  return 0;
 /*
  * Ok - we have the memory areas we should free on the 'free' list,
  * so release them, and unmap the page range..
  * If the one of the segments is only being partially unmapped,
  * it will put new vm_area_struct(s) into the address space.
  */
 while (free) {
  unsigned long st, end;
  mpnt = free;
  free = free->vm_next;
  st = addr < mpnt->vm_start ? mpnt->vm_start : addr;
  end = addr+len;
  end = end > mpnt->vm_end ? mpnt->vm_end : end;
  if (mpnt->vm_ops && mpnt->vm_ops->unmap)  //本身函数的调用,如果没有就调用下面的通用函数
   mpnt->vm_ops->unmap(mpnt, st, end-st);
  else
   unmap_fixup(mpnt, st, end-st);
  kfree(mpnt);
 }
 unmap_page_range(addr, len);
 return 0;
}
/* This is used for a general mmap of a disk file */
//对于普通磁盘文件的映射
int generic_mmap(struct inode * inode, struct file * file,
 unsigned long addr, size_t len, int prot, unsigned long off)
{
   struct vm_area_struct * mpnt;
 extern struct vm_operations_struct file_mmap;
 struct buffer_head * bh;
 if (prot & PAGE_RW) /* only PAGE_COW or read-only supported right now */
  return -EINVAL;
  
 if (off & (inode->i_sb->s_blocksize - 1))
  return -EINVAL;
 if (!inode->i_sb || !S_ISREG(inode->i_mode))
  return -EACCES;
 if (!inode->i_op || !inode->i_op->bmap)
  return -ENOEXEC;
 if (!(bh = bread(inode->i_dev,bmap(inode,0),inode->i_sb->s_blocksize)))
  return -EACCES;
  
 //记录被访问时间,设置脏了
 if (!IS_RDONLY(inode)) {
  inode->i_atime = CURRENT_TIME;
  inode->i_dirt = 1;
 }
 
 brelse(bh);
 mpnt = (struct vm_area_struct * ) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
 if (!mpnt)
  return -ENOMEM;
 unmap_page_range(addr, len); 
 mpnt->vm_task = current;
 mpnt->vm_start = addr;
 mpnt->vm_end = addr + len;
 mpnt->vm_page_prot = prot;
 mpnt->vm_share = NULL;
 mpnt->vm_inode = inode;
 inode->i_count++;
 mpnt->vm_offset = off;
 mpnt->vm_ops = &file_mmap;
 
 insert_vm_struct(current, mpnt);
 
 merge_segments(current->mmap, NULL, NULL);
 
 return 0;
}
//将vm插入到进程队列中,进行的插入是有地址顺序的,
//并进行一些重叠性的检查
void insert_vm_struct(struct task_struct *t, struct vm_area_struct *vmp)
{
 struct vm_area_struct **nxtpp, *mpnt;
 nxtpp = &t->mmap;
 
 for(mpnt = t->mmap; mpnt != NULL; mpnt = mpnt->vm_next)
 {
  //这里找到了,因为nxtpp保存了前一个地址,所以不用担心啊
  if (mpnt->vm_start > vmp->vm_start)
   break;
  nxtpp = &mpnt->vm_next;
  //这里表示有重叠的区域了
  if ((vmp->vm_start >= mpnt->vm_start && vmp->vm_start < mpnt->vm_end) ||
      (vmp->vm_end >= mpnt->vm_start && vmp->vm_end < mpnt->vm_end))
   printk("insert_vm_struct: ins area %lx-%lx in area %lx-%lx\n",
          vmp->vm_start, vmp->vm_end,
          mpnt->vm_start, vmp->vm_end);
 }
 
 vmp->vm_next = mpnt;
 *nxtpp = vmp;  //->mmap=vmp
}
//进行区域的合并,冗余的vm_area_structs将被删除
void merge_segments(struct vm_area_struct *mpnt,map_mergep_fnp mergep, void *mpd)
{
 struct vm_area_struct *prev, *next;
 if (mpnt == NULL)
  return;
 
 for(prev = mpnt, mpnt = mpnt->vm_next;
     mpnt != NULL;
     prev = mpnt, mpnt = next)
 {
  int mp;
  next = mpnt->vm_next;
  
  if (mergep == NULL)
  {
   unsigned long psz = prev->vm_end - prev->vm_start;
   mp = prev->vm_offset + psz == mpnt->vm_offset;
  }
  else  //指明了调用特定的合并函数
   mp = (*mergep)(prev, mpnt, mpd);
  /*
   * Check they are compatible.
   * and the like...
   * What does the share pointer mean?
   */
  if (prev->vm_ops != mpnt->vm_ops ||
      prev->vm_page_prot != mpnt->vm_page_prot ||  //线性区域的页匡的访问权限
      prev->vm_inode != mpnt->vm_inode ||
      prev->vm_end != mpnt->vm_start ||    //合并的条件啊!!
      !mp ||
      prev->vm_share != mpnt->vm_share ||  /* ?? */
      prev->vm_next != mpnt)   /* !!! */  //顺序排放的,应该没有什么问题吧
   continue;
  prev->vm_end = mpnt->vm_end;
  prev->vm_next = mpnt->vm_next;
  kfree_s(mpnt, sizeof(*mpnt));
  mpnt = prev;
 }
}
/*
 * Map memory not associated with any file into a process
 * address space.  Adjecent memory is merged.
 */
//匿名文件关联的内存映射
static int anon_map(struct inode *ino, struct file * file,
      unsigned long addr, size_t len, int mask,
      unsigned long off)
{
   struct vm_area_struct * mpnt;
 if (zeromap_page_range(addr, len, mask))  
  return -ENOMEM;
 mpnt = (struct vm_area_struct * ) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
 if (!mpnt)
  return -ENOMEM;
 mpnt->vm_task = current;
 mpnt->vm_start = addr;
 mpnt->vm_end = addr + len;
 mpnt->vm_page_prot = mask;
 mpnt->vm_share = NULL;
 mpnt->vm_inode = NULL;  //空
 mpnt->vm_offset = 0;
 mpnt->vm_ops = NULL;
 insert_vm_struct(current, mpnt);
 
 merge_segments(current->mmap, ignoff_mergep, NULL);
 return 0;
}
/* Merge, ignoring offsets */ //??
int ignoff_mergep(const struct vm_area_struct *m1,
    const struct vm_area_struct *m2,
    void *data)
{
 if (m1->vm_inode != m2->vm_inode) /* Just to be sure */
  return 0;
 return (struct inode *)data == m1->vm_inode;
}
  文档地址:http://blogimg.chinaunix.net/blog/upfile2/090317145723.pdf
相关阅读 更多 +
排行榜 更多 +
开局一个小兵最新版

开局一个小兵最新版

休闲益智 下载
火柴人联盟2腾讯qq登录版

火柴人联盟2腾讯qq登录版

体育竞技 下载
tsuki odyssey游戏(月兔冒险奥德赛)

tsuki odyssey游戏(月兔冒险奥德赛)

休闲益智 下载