3--共享内存的实践到内核--撤销共享内存的映射和控制 - 如何从应用程序进入linux内

来源:百度文库 编辑:神马文学网 时间:2024/05/01 04:33:21
我们还是先从应用界面看起,如果没有看过我们的实践程序的朋友请看这里http://blog.chinaunix.net/u2/64681/showart_1219828.htmlshmdt(shared_memory) == -1这句代码是撤销共享内存,另一句shmctl(shmid, IPC_RMID, 0) == -1是删除共享内存。我们还是按照原来的分析方法这里不再重复如何进入sys_ipc()的,我们把进入sys_ipc()后的代码重要的代码贴出来

    case SHMDT:
        return sys_shmdt ((char __user *)ptr);
。。。。。。省略
    case SHMCTL:
        return sys_shmctl (first, second,
                 (struct shmid_ds __user *) ptr);

也就是看到应用程序与我们内核的系统调用函数参数是对正的,参数的含义我们就不用再重复介绍了,我们分别看一下sys_shmdt()函数和sys_shmctl()函数。

asmlinkage long sys_shmdt(char __user *shmaddr)
{
    struct mm_struct *mm = current->mm;
    struct vm_area_struct *vma, *next;
    unsigned long addr = (unsigned long)shmaddr;
    loff_t size = 0;
    int retval = -EINVAL;

    if (addr & ~PAGE_MASK)
        return retval;

    down_write(&mm->mmap_sem);

    /*
     * This function tries to be smart and unmap shm segments that
     * were modified by partial mlock or munmap calls:
     * - It first determines the size of the shm segment that should be
     * unmapped: It searches for a vma that is backed by shm and that
     * started at address shmaddr. It records it's size and then unmaps
     * it.
     * - Then it unmaps all shm vmas that started at shmaddr and that
     * are within the initially determined size.
     * Errors from do_munmap are ignored: the function only fails if
     * it's called with invalid parameters or if it's called to unmap
     * a part of a vma. Both calls in this function are for full vmas,
     * the parameters are directly copied from the vma itself and always
     * valid - therefore do_munmap cannot fail. (famous last words?)
     */
    /*
     * If it had been mremap()'d, the starting address would not
     * match the usual checks anyway. So assume all vma's are
     * above the starting address given.
     */
    vma = find_vma(mm, addr);

    while (vma) {
        next = vma->vm_next;

        /*
         * Check if the starting address would match, i.e. it's
         * a fragment created by mprotect() and/or munmap(), or it
         * otherwise it starts at this address with no hassles.
         */
        if ((vma->vm_ops == &shm_vm_ops) &&
            (vma->vm_start - addr)/PAGE_SIZE == vma->vm_pgoff) {


            size = vma->vm_file->f_path.dentry->d_inode->i_size;
            do_munmap(mm, vma->vm_start, vma->vm_end - vma->vm_start);
            /*
             * We discovered the size of the shm segment, so
             * break out of here and fall through to the next
             * loop that uses the size information to stop
             * searching for matching vma's.
             */
            retval = 0;
            vma = next;
            break;
        }
        vma = next;
    }

    /*
     * We need look no further than the maximum address a fragment
     * could possibly have landed at. Also cast things to loff_t to
     * prevent overflows and make comparisions vs. equal-width types.
     */
    size = PAGE_ALIGN(size);
    while (vma && (loff_t)(vma->vm_end - addr) <= size) {
        next = vma->vm_next;

        /* finding a matching vma now does not alter retval */
        if ((vma->vm_ops == &shm_vm_ops) &&
            (vma->vm_start - addr)/PAGE_SIZE == vma->vm_pgoff)

            do_munmap(mm, vma->vm_start, vma->vm_end - vma->vm_start);
        vma = next;
    }

    up_write(&mm->mmap_sem);
    return retval;
}

其实这段代码并不复杂,主要是注释多有点吓人,呵呵,如果有内核基础的朋友相信不难理解这段代码,但是对于其他朋友我们要说明一点,vma结构变量是表示虚拟空间的专用数据结构,这段代码首先是找到共享内存在进程中指定地址处的虚拟空间段,然后利用do_munmap()函数撤销对这个虚拟空间段的所有内存映射。do_munmap()函数我们还是在内存管理那部分分析吧,这里朋友们先暂且知道有这个一个函数,从函数名称上也能理解出他的作用。

我们再看另一个控制共享内存的函数sys_shmctl()

asmlinkage long sys_shmctl(int shmid, int cmd, struct shmid_ds __user *buf)
{
    struct shmid_kernel *shp;
    int err, version;
    struct ipc_namespace *ns;

    if (cmd < 0 || shmid < 0) {
        err = -EINVAL;
        goto out;
    }

    version = ipc_parse_version(&cmd);
    ns = current->nsproxy->ipc_ns;

    switch (cmd) { /* replace with proc interface ? */
    case IPC_INFO:
    {
        struct shminfo64 shminfo;

        err = security_shm_shmctl(NULL, cmd);
        if (err)
            return err;

        memset(&shminfo,0,sizeof(shminfo));
        shminfo.shmmni = shminfo.shmseg = ns->shm_ctlmni;
        shminfo.shmmax = ns->shm_ctlmax;
        shminfo.shmall = ns->shm_ctlall;

        shminfo.shmmin = SHMMIN;
        if(copy_shminfo_to_user (buf, &shminfo, version))
            return -EFAULT;

        down_read(&shm_ids(ns).rw_mutex);
        err = ipc_get_maxid(&shm_ids(ns));
        up_read(&shm_ids(ns).rw_mutex);

        if(err<0)
            err = 0;
        goto out;
    }
    case SHM_INFO:
    {
        struct shm_info shm_info;

        err = security_shm_shmctl(NULL, cmd);
        if (err)
            return err;

        memset(&shm_info,0,sizeof(shm_info));
        down_read(&shm_ids(ns).rw_mutex);
        shm_info.used_ids = shm_ids(ns).in_use;
        shm_get_stat (ns, &shm_info.shm_rss, &shm_info.shm_swp);
        shm_info.shm_tot = ns->shm_tot;
        shm_info.swap_attempts = 0;
        shm_info.swap_successes = 0;
        err = ipc_get_maxid(&shm_ids(ns));
        up_read(&shm_ids(ns).rw_mutex);
        if(copy_to_user (buf, &shm_info, sizeof(shm_info))) {
            err = -EFAULT;
            goto out;
        }

        err = err < 0 ? 0 : err;
        goto out;
    }
    case SHM_STAT:
    case IPC_STAT:
    {
        struct shmid64_ds tbuf;
        int result;

        if (!buf) {
            err = -EFAULT;
            goto out;
        }

        if (cmd == SHM_STAT) {
            shp = shm_lock(ns, shmid);
            if (IS_ERR(shp)) {
                err = PTR_ERR(shp);
                goto out;
            }
            result = shp->shm_perm.id;
        } else {
            shp = shm_lock_check(ns, shmid);
            if (IS_ERR(shp)) {
                err = PTR_ERR(shp);
                goto out;
            }
            result = 0;
        }
        err=-EACCES;
        if (ipcperms (&shp->shm_perm, S_IRUGO))
            goto out_unlock;
        err = security_shm_shmctl(shp, cmd);
        if (err)
            goto out_unlock;
        memset(&tbuf, 0, sizeof(tbuf));
        kernel_to_ipc64_perm(&shp->shm_perm, &tbuf.shm_perm);
        tbuf.shm_segsz    = shp->shm_segsz;
        tbuf.shm_atime    = shp->shm_atim;
        tbuf.shm_dtime    = shp->shm_dtim;
        tbuf.shm_ctime    = shp->shm_ctim;
        tbuf.shm_cpid    = shp->shm_cprid;
        tbuf.shm_lpid    = shp->shm_lprid;
        tbuf.shm_nattch    = shp->shm_nattch;
        shm_unlock(shp);
        if(copy_shmid_to_user (buf, &tbuf, version))
            err = -EFAULT;
        else
            err = result;
        goto out;
    }
    case SHM_LOCK:
    case SHM_UNLOCK:
    {
        shp = shm_lock_check(ns, shmid);
        if (IS_ERR(shp)) {
            err = PTR_ERR(shp);
            goto out;
        }

        err = audit_ipc_obj(&(shp->shm_perm));
        if (err)
            goto out_unlock;

        if (!capable(CAP_IPC_LOCK)) {
            err = -EPERM;
            if (current->euid != shp->shm_perm.uid &&
             current->euid != shp->shm_perm.cuid)
                goto out_unlock;
            if (cmd == SHM_LOCK &&
             !current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur)
                goto out_unlock;
        }

        err = security_shm_shmctl(shp, cmd);
        if (err)
            goto out_unlock;
        
        if(cmd==SHM_LOCK) {
            struct user_struct * user = current->user;
            if (!is_file_hugepages(shp->shm_file)) {
                err = shmem_lock(shp->shm_file, 1, user);
                if (!err && !(shp->shm_perm.mode & SHM_LOCKED)){
                    shp->shm_perm.mode |= SHM_LOCKED;
                    shp->mlock_user = user;
                }
            }
        } else if (!is_file_hugepages(shp->shm_file)) {
            shmem_lock(shp->shm_file, 0, shp->mlock_user);
            shp->shm_perm.mode &= ~SHM_LOCKED;
            shp->mlock_user = NULL;
        }
        shm_unlock(shp);
        goto out;
    }
    case IPC_RMID:
    case IPC_SET:
        err = shmctl_down(ns, shmid, cmd, buf, version);
        return err;
    default:
        return -EINVAL;
    }

out_unlock:
    shm_unlock(shp);
out:
    return err;
}

这段代码尽管长,我们也不用全部看,有兴趣可以认真研究,我们说过是从应用程序来顺藤摸瓜的,这里就到了代码中最下面的红色部分即IPC_RMID,我们看到他调用了另一个函数shmctl_down()

static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd,
         struct shmid_ds __user *buf, int version)
{
    struct kern_ipc_perm *ipcp;
    struct shmid64_ds shmid64;
    struct shmid_kernel *shp;
    int err;

    if (cmd == IPC_SET) {
        if (copy_shmid_from_user(&shmid64, buf, version))
            return -EFAULT;
    }

    ipcp = ipcctl_pre_down(&shm_ids(ns), shmid, cmd, &shmid64.shm_perm, 0);
    if (IS_ERR(ipcp))
        return PTR_ERR(ipcp);

    shp = container_of(ipcp, struct shmid_kernel, shm_perm);

    err = security_shm_shmctl(shp, cmd);
    if (err)
        goto out_unlock;
    switch (cmd) {
    case IPC_RMID:
        do_shm_rmid(ns, ipcp);

        goto out_up;
    case IPC_SET:
        ipc_update_perm(&shmid64.shm_perm, ipcp);
        shp->shm_ctim = get_seconds();
        break;
    default:
        err = -EINVAL;
    }
out_unlock:
    shm_unlock(shp);
out_up:
    up_write(&shm_ids(ns).rw_mutex);
    return err;
}

在上面的代码中将会执行重要的函数do_shm_rmid()

static void do_shm_rmid(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
{
    struct shmid_kernel *shp;
    shp = container_of(ipcp, struct shmid_kernel, shm_perm);

    if (shp->shm_nattch){
        shp->shm_perm.mode |= SHM_DEST;
        /* Do not find it any more */
        shp->shm_perm.key = IPC_PRIVATE;
        shm_unlock(shp);
    } else
        shm_destroy(ns, shp);
}

很明显这个数据结构根据ipc机制找到我们以前分配的共享内存的结构struct shmid_kernel *shp,销毁他并释放了所占用的内核空间。
3--共享内存的实践到内核--撤销共享内存的映射和控制 - 如何从应用程序进入linux内 1--共享内存的实践到内核--共享内存的创建 - 如何从应用程序进入linux内核 - 无... 1--共享内存的实践到内核--共享内存的创建 - 如何从应用程序进入linux内核 进程间共享 内存 Linux内核高端内存管理 Linux中应用程序和内核模块的区别 旺的共享空间: eclipse.ini内存设置各参数含义 MySQL内存使用的全局共享讲解 - 数据库应用 - 编程开发 - 伊甸网 Shared memory: Where it belongs in the computer space(共享内存在实践中的应用) Linux环境进程间通信(五): 共享内存(上) Linux环境进程间通信(五): 共享内存(上) Linux环境进程间通信(五): 共享内存(上) Linux环境进程间通信(五): 共享内存(上) Linux环境进程间通信(五): 共享内存(下) Linux环境进程间通信(五): 共享内存(上) linux进程通信(一)--共享内存+信号量 - - JavaEye技术网站 Linux环境进程间通信(五): 共享内存(上) Windows与VMware下的Linux文件如何实现共享 Linux内存使用的体会(原创) Linux操作系统中内存buffer和cache的区别 - Linux - 操作系统 - ... 内存的基本知识和选购 ,“******应用程序出错,内存不能为read”的错误 C语言:内存字节对齐详解 - linux内核/驱动 - LinuxSmartphone java获取本机的空闲内存,和总的内存