zoukankan      html  css  js  c++  java
  • 为什么/dev/shm中看不到shmget创建的内存文件及其它(下)

    一、如何看到sysV IPC shm文件名
    1、file_system_type.get_sb修改
    正如上篇所说,在用户态无法看到可shm文件的名称,不同的挂载点使用不同的dentry,而对于tmpfs文件,它的readdir的系统实现就是通过dcache_readdir函数来实现的,但是这个dentry的不同本质上是由于
    static int shmem_get_sb(struct file_system_type *fs_type,
        int flags, const char *dev_name, void *data, struct vfsmount *mnt)
    {
        return get_sb_nodev(fs_type, flags, data, shmem_fill_super, mnt);
    }
    函数实现的,它会为每次挂载分配一个新的super_block结构,这个实现在get_sb_nodev中。作为对比我们,proc文件无论挂接在哪里,它看到的内容都是一致的,所以可以以proc文件系统为例进行修改,将这里的shmem_get_sub修改为如下形式
    static int shmem_get_sb(struct file_system_type *fs_type,
        int flags, const char *dev_name, void *data, struct vfsmount *mnt)
    {
        return get_sb_single(fs_type, flags, data, shmem_fill_super, mnt);
    }
    这里注意到一个细节,那就是sys_mount--->>>do_new_mount--->>do_kern_mount---->>>vfs_kern_mount--->>alloc_vfsmnt这个调用连中,vfsmount结构是始终分配的,每次挂载都将会分配一个独立的vfsmount结构,而所谓的single_sb,它只是说它的super_block是唯一的,进一步说,这些vfsmount结构中的mnt_root指针指向的位置是相同的。然后在path_lookup--->>>do_path_lookup--->>>link_path_walk---->>__link_path_walk--->>>do_lookup--->>>__follow_mount--->>lookup_mnt
    这条路径中将会返回这个vfsmount结构的mnt_root作为新的文件夹(这一点在__follow_mount函数中的path->dentry = dget(mounted->mnt_root)完成)。
    2、修改内核挂接属性
    进行上面修改之后,在用户态通过
    mount -t tmpfs none myshm
    挂载,会提示参数错误,这个地方时内核在挂载内核使用的shm_mnt的时候加了选项限制不能从用户态挂载,所以此处也要做相应修改。
    linux-2.6.21mmshmem.c
    static int __init init_tmpfs(void)
        shm_mnt = vfs_kern_mount(&tmpfs_fs_type, MS_NOUSER,删除该选项,从而可以在用户态挂载文件
                    tmpfs_fs_type.name, NULL);
    3、修改dentry DCACHE_UNHASHED 属性
    做上面修改之后,用户态可以挂载文件,在不同挂接点中看到的内容一致,也就是说不同挂接点均可以看到,但是遗憾的时候通过shmget隐形创建的文件依然不可见,此时再次跟踪,发现问题所在。在
    linux-2.6.21mmshmem.c
    struct file *shmem_file_setup(char *name, loff_t size, unsigned long flags)
    {
    ……
        dentry = d_alloc(root, &this);
    ……
    }
    在d_alloc中创建的dentry项刚开始是出于删除状态(也就是DCACHE_UNHASED状态),这个状态也就是内存文件的特征,它可以在内存中的dentry结构引用计数降低为零的时候从内存中直接释放,这也正是tmpfs中"tmp"属性的体现。偏偏在执行文件夹内容显示的dcache_readdir中:
    for (p=q->next; p != &dentry->d_subdirs; p=p->next) {
                    struct dentry *next;
                    next = list_entry(p, struct dentry, d_u.d_child);
                    if (d_unhashed(next) || !next->d_inode)这里对于出于DCACHE_UNHASHED状态的目录项不予显示,所以内核中挂接点依然无法看到
                        continue;
    对于通常的实体文件系统(例如ext2,),它们在调用d_alloc之后一般要通过d_splice_alias--->>>d_rehash--->>_d_rehash
    static void __d_rehash(struct dentry * entry, struct hlist_head *list)
    {

         entry->d_flags &= ~DCACHE_UNHASHED
    ;
         hlist_add_head_rcu(&entry->d_hash, list);
    }
    在这里通过该操作来清除对于文件的删除状态,从而让它在内存中生效。由于shmem_file_setup并没有代劳执行这个操作,所以我们就不按套路出牌,强制在shmem_setup_file函数中,新分配的dentry结构中清除这个标志。
    经过这么一番折腾,用户态终于可以看到内核创建的文件了,由于我是通过IPC_PRIVATE创建了多个共享内存,所以在该文件夹下可以看到很多个同名的文件,它们都是“SYSV00000000”,这也说明一个问题,那就是并非所有的文件系统都不能在同一个文件夹下有重名文件,文件系统的限制只是为了避免二义性。
    二、为什么do_shmat中每次会分配一个struct file结构
    可以看到,在newseg函数中,已经通过shmem_setup_file分配了一个struct file结构,但是在每次执行shmat的时候还会再次分配一个结构,这个结构是不是冗余的呢?后来我看了一下2.4.37的内核版本,在sys_shmat调用中的确是没有分配这个结构的,使用的就是newseg中创建的那个file结构。所以为什么在新的版本中必须要再次分配还真是不太容易理解。但是看一下两个文件的差别,可以看新的版本的shm_file_operations定义为
    static const struct file_operations shm_file_operations = {
        .mmap        = shm_mmap,
        .fsync        = shm_fsync,
        .release    = shm_release,
        .get_unmapped_area    = shm_get_unmapped_area,
    };
    而2.4版本中对应内容为
    static struct file_operations shm_file_operations = {
        mmap:    shm_mmap
    };
    可见新的版本中定义了一些新的file_operations接口。并且其中还有一个判断使用到了这个结构
    int is_file_shm_hugepages(struct file *file)
    {
        int ret = 0;

        if (file->f_op == &shm_file_operations) {
            struct shm_file_data *sfd;
            sfd = shm_file_data(file);
            ret = is_file_hugepages(sfd->file);
        }
        return ret;
    }
    所以猜测这里是为了支持hugepages功能而添加的一个功能?不管如何,这个都是一个猜测了,没有实作,也算是一个疑案吧,知道的同学请指教。
    三、为什么要定义shm_vm_ops结构中的open/close接口
    这个是一个比较特殊的例子,因为它在mmap的时候定义了vm_operations_struct结构中的open和close接口,这两个接口的调用位置分别为
    copy_process-->>copy_mm--->>>dup_mm--->>>dup_mmap
            if (tmp->vm_ops && tmp->vm_ops->open)
                tmp->vm_ops->open(tmp);

    do_exit---->>>exit_mm---->>>mmput--->>exit_mmap--->>remove_vma
        if (vma->vm_ops && vma->vm_ops->close)
            vma->vm_ops->close(vma);
    在通过shmget创建一个tmpfs文件之后,这个文件不论是否执行shmat,这个文件是始终存在的,删除这个文件的方法就是通过shmctl RMID的方法来删除这个文件。但是这里有一个比较奇特但是又比较典型的策略,就是当一个资源被多个实例共享时,如果该资源被删除,那么删除并不马上执行,而只是减少引用计数,只有当计数值降低为零的时候才真正执行删除操作。这一点在文件的删除上比较常见和典型。
    现在假设说一个shm被通过shmctl删除,内核只是对这个文件做了一个标记SHM_DEST属性,而真正的触发则只有框架来支持,也就是说这里只能以“回调”的形式注册给系统,从而在引用附加和移除的时候得到通知并进行计算,进而根据计算结果做出可能的删除操作
    四、shm/shmem文件何时释放
    1、shm文件
    这里所说的shm文件是在shmat中通过file = get_empty_filp();分配的struct file结构。它的示范同样是由mmap框架来完成。在前一节说vm_operations_struct的open/close函数的附近,各自都有对vma区间是否有文件映射的判断,如果是文件映射,会分别递增和递减这个文件的引用次数。
    dup_mmap函数中为
            if (file) {
                struct inode *inode = file->f_path.dentry->d_inode;
                get_file(file);
    remove_vma函数中
        if (vma->vm_file)
            fput(vma->vm_file);
    然后在
    fput--->>__fput
        if (file->f_op && file->f_op->release)
            file->f_op->release(inode, file);
    在其中执行自己私有数据的释放。
    2、shmem文件的释放
    这里所说内容为newseg中创建的文件,它的释放是通过
    shm_destroy--->>>fput--->>>__fput--->>>dput--->>>dentry_iput--->>>iput--->>>generic_delete_inode--->>>shmem_delete_inode
    释放,由于该文件是内存文件,所以inode dentry这些内存结构被删除之后就无法恢复,也意味着它们从系统中消失了。
     
     
     
     
     
  • 相关阅读:
    Linux 基础命令3 shell
    Django 的学习(2) 从adminuser到配置
    Linux巨好用的
    常见任务&基本工具 1 软件包管理
    java学习补全 1
    基础命令1
    java 5 绘图GUI
    Open GL与OpenGLES
    NDK 安装步骤
    转:为什么要有handler机制?
  • 原文地址:https://www.cnblogs.com/tsecer/p/10486261.html
Copyright © 2011-2022 走看看