linux1.0版本mmap研究

今天看go的runtime的时候,内存堆初始化的时候是调用mmap申请的虚拟内存,于是搜了下0.11的linux源码,发现还没有实现,就又找了1.0版本的(毕竟越早代码越少都是核心实现- -)

先看代码

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;
    //flas在第四个参数,get_fs_long是从用户空间读到内核空间来    
    flags = get_fs_long(buffer+3);
    if (!(flags & MAP_ANONYMOUS)) {
        unsigned long fd = get_fs_long(buffer+4);
        // 可以看到该版本只支持已打开的fd,还不支持匿名文件
        if (fd >= NR_OPEN || !(file = current->filp[fd]))
            return -EBADF;
    }
    // 最终调用do_mmap来实现
    return do_mmap(file, get_fs_long(buffer), get_fs_long(buffer+1),
        get_fs_long(buffer+2), flags, get_fs_long(buffer+5));
}

do_mmap

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)
        return addr;

    
    if (addr > TASK_SIZE || len > TASK_SIZE || addr > TASK_SIZE-len)
        return -EINVAL;

    if (file != NULL)
        switch (flags & MAP_TYPE) {
        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;
        }
    if (flags & MAP_FIXED) {
        if (addr & ~PAGE_MASK)
            return -EINVAL;
        if (len > TASK_SIZE || addr > TASK_SIZE - len)
            return -EINVAL;
    } else {
        struct vm_area_struct * vmm;

        addr = SHM_RANGE_START;
        while (addr+len < SHM_RANGE_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和多个地址空间交叉,
                 * 所以在找到第一个之后就将addr设置为vmm指向的地址空间末端,
                 * 然后继续查找从前一个虚拟地址空间的结尾处
                 */
                addr = PAGE_ALIGN(vmm->vm_end);
                break;
            }
            if (!vmm)
                break;
        }
        if (addr+len >= SHM_RANGE_END)
            return -ENOMEM;
    }
    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)
        /* 调用本文件中的generic_mmap函数
          */
        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;

    if (!current->errno)
        current->errno = -error;
    return -1;
}

generic_mmap:

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);

    /* 分配一个虚拟地址空间,运行到这个位置就代表
     * 起始地址addr长度为len的虚拟地址空间只可以
     * 映射到进程的地址空间当中的。
     */
    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;
    /*如果文件被映射到虚拟地址空间,
     *则inode的引用计数会增加1,其中off代表从文件中的第几个字节处
     *开始映射
     */
    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;
}

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)
    {
        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;
}

额,其实我也不知道分析啥,代码都很清晰了,如果熟悉C语言的malloc的话,对这儿的进程的mmap链表就一眼就懂了,就是在mmap中分配的对应大小的内存,而且是与文件的inode绑定的。这样就可以实现用户进程对该内存区域的读写直接映射到文件,相比write,read,可以省一部分从用户空间和内核空间数据搬运的工作

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容