linux共享内存的设计

版权声明:本文为博主原创,无版权,未经博主允许可以随意转载,无需注明出处,随意修改或保持可作为原创! https://blog.csdn.net/dog250/article/details/5303709

posix的共享内存是通过用户空间挂在的tmpfs文件系统实现的,而system V的共享内存是由内核本身的tmpfs实现的,这里可以看出,二者其实是用同一种机制实现的,不同的是用户接口不同,posix旨在提供一套统一的可用接口而不是实现,因此posix的上层根本就不会在意其机制是内核实现的还是用户空间实现的,因此用文件系统实现再好不过了,在linux上它实际上是通过用户空间挂载的tmpfs实现,system v的共享内存就不一样,它严格由操作系统内核提供接口和实现,因此linux中有关于共享内存的shmget/at等系统调用,虽然它们的底层实现一致,但是system v的共享内存作为内核提供的ipc之一,由内核直接支持,而posix就不一定了,在某些系统上由内核直接实现,而在另一些系统比如linux上由用户空间实现,posix的共享内存严格依赖用户空间tmpfs的挂载,在shm_open中有判断:

int shm_open (const char *name, int oflag, mode_t mode)

{

size_t namelen;

char *fname;

int fd;

__libc_once (once, where_is_shmfs); //找到tmpfs的挂载点

if (mountpoint.dir == NULL)

{

__set_errno (ENOSYS); //如果没有挂载tmpfs,那么就说明它还有被实现,返回错误码

return -1;

}

while (name[0] == '/')

++name;

if (name[0] == '/0')

{

__set_errno (EINVAL);

return -1;

}

namelen = strlen (name);

fname = (char *) alloca (mountpoint.dirlen + namelen + 1);

__mempcpy (__mempcpy (fname, mountpoint.dir, mountpoint.dirlen), name, namelen + 1);

fd = open (fname, oflag | O_NOFOLLOW, mode);

...

return fd;

}

posix的共享内存机制实际上在库过程中以及用户空间的其他部分被展示为完全的文件系统的调用过程,在调用完shm_open之后,需要调用mmap来将tmpfs的文件映射到地址空间,接着就可以操作这个文件了,需要注意的是,别的进程也可以操作这个文件,因此这个文件其实就是共享内存。反观system v的共享内存,内核直接实现了shmget/at系统调用,虽然最终也是靠tmpfs来实现的,但是接口设计上和posix完全不同,posix旨在提供所有系统都一致的接口,而system v只在于实现自己的逻辑,共享内存其实只是sysv中ipc的一部分,最终的管理数据结构也是ipc的而不是共享内存的,比如newseg(ipc/shm.c文件中)函数中的shm_addid函数调用的是ipc_addid(在文件ipc/util.c中),我们看一下这个newseg函数:

static int newseg (key_t key, int shmflg, size_t size)

{

...

} else {

sprintf (name, "SYSV%08x", key);

file = shmem_file_setup(name, size, VM_ACCOUNT); //最终落实到tmpfs

}

...

id = shm_addid(shp); //加入到ipc统一的管理数据结构中

...//设置shp的各个字段的值

return shp->id;

}

注意shmem_file_setup这个调用只是使用了tmpfs的一个实例,从它的实现可以看出有这样一句:file->f_vfsmnt = mntget(shm_mnt);使用了shm_mnt,那么这个shm_mnt是什么时候初始化的呢?在init_tmpfs中有:

shm_mnt = do_kern_mount(tmpfs_fs_type.name, MS_NOUSER, tmpfs_fs_type.name, NULL);

从linux的Document中可以看出,内核中起码要有一个tmpfs的实例,就是shm_mnt,这就是专门为system v的共享内存准备的,和是否编译TMPFS无关,虽然posix也使用了tpfs,但是却不是和内核实现的system v共享内存使用的一样的实例,后者的mnt实例是在用户执行mount tmpfs时创建的,实例虽然不一样但操作还是一样的,就像类实例虽不同,但是类是同一个类,数据不同但是代码相同。具体的tmpfs的file_operations等等就不赘述了。所以可以看出,不管是sysyem v的还是posix的共享内存,在计算机重新启动之后都会不复存在,因为数据室存在物理存储器或者交换分区的,它们不同的是,posix是以用户空间文件实现的,并且用户空间操作的是文件描述符,文件描述符是属于task_struct的,因此进程退出之后共享内存将递减共享计数,如果为0则销毁,因此posix的共享内存虽然使用open来创建或打开共享内存并没有用close而是用unlink来关闭的,作用就是在计数为0的时候销毁它,看看system v的共享内存,虽然也是由文件实现的,但是文件并没有加入到进程的打开文件表,而是作为进程的一种内禀属性被使用的,因此在进程退出时并不关闭文件,只要机器不重启或者该共享内存不显式销毁,那么共享内存将一直存在,进程和文件其实是互不相关的两个概念,只是进程打开的文件才和进程相关,在进程结束的时候只是能保证与之相关的文件被关闭,而别的文件比如sysv的共享内存使用的文件结构没有和进程关联,因此不会被关闭。其实close并不操作什么,而是仅仅刷新缓冲区,然后递减文件的使用者计数:

int filp_close(struct file *filp, fl_owner_t id)

{

int retval = 0;

if (!file_count(filp)) {

printk(KERN_ERR "VFS: Close: file count is 0/n");

return 0;

}

if (filp->f_op && filp->f_op->flush)

retval = filp->f_op->flush(filp);

dnotify_flush(filp, id);

locks_remove_posix(filp, id);

fput(filp); //linux的懒惰特性决定了这个fput操作才是最重要的。

return retval;

}

没有更多推荐了,返回首页