Linux内核源代码情景分析-共享内存

本文详细分析了Linux内核中关于共享内存的实现,包括shmget()的内存区创建与查找,shmat()的映射过程,以及在内存紧张时的换入换出策略。讲解了从页面异常到映射建立的全过程,以及如何恢复映射,确保多个进程间共享内存的高效使用。
摘要由CSDN通过智能技术生成

    一、库函数shmget()--共享内存区的创建与寻找

asmlinkage long sys_shmget (key_t key, size_t size, int shmflg)
{
	struct shmid_kernel *shp;
	int err, id = 0;

	down(&shm_ids.sem);
	if (key == IPC_PRIVATE) {
		err = newseg(key, shmflg, size);//分配一个共享内存区供本进程专用,最后返回的是一体化的标示号
	} else if ((id = ipc_findkey(&shm_ids, key)) == -1) {//在shm_ids寻找shmid_kernel结构(共享内存区),如果没有找到,id为-1。如果找到了id为标示号。
		if (!(shmflg & IPC_CREAT))//没有找到也不允许创建,那么就出错返回
			err = -ENOENT;
		else
			err = newseg(key, shmflg, size);//否则创建一个共享内存区
	} else if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL)) {//如果找到了,但是要求的是创建,那么也返回出错
		err = -EEXIST;
	} else {//如果找到了,也不要求创建,就是正常情况下了
		shp = shm_lock(id);//通过标示号id,获取共享内存区
		if(shp==NULL)
			BUG();
		if (shp->shm_segsz < size)
			err = -EINVAL;
		else if (ipcperms(&shp->shm_perm, shmflg))
			err = -EACCES;
		else
			err = shm_buildid(id, shp->shm_perm.seq);//最后返回的还是一体化参数
		shm_unlock(id);
	}
	up(&shm_ids.sem);
	return err;//无论是创建还是查找,最后都返回的是一体化的标示号
}
    键值IPC_PRIVATE,即0,是特殊的,它表示要分配一个共享内存区供本进程专用。其他键值则表示要创建或寻找的是"共享"内存区。而标志位IPC_CREAT则表示目的在于创建。

    1、当键值是IPC_PRIVATE时,会调用newseg,分配一个共享内存区供本进程专用,代码如下:

static int newseg (key_t key, int shmflg, size_t size)
{
	int error;
	struct shmid_kernel *shp;
	int numpages = (size + PAGE_SIZE -1) >> PAGE_SHIFT;
	struct file * file;
	char name[13];
	int id;

	if (size < SHMMIN || size > shm_ctlmax)
		return -EINVAL;

	if (shm_tot + numpages >= shm_ctlall)
		return -ENOSPC;

	shp = (struct shmid_kernel *) kmalloc (sizeof (*shp), GFP_USER);//分配shmid_kernel结构
	if (!shp)
		return -ENOMEM;
	sprintf (name, "SYSV%08x", key);
	file = shmem_file_setup(name, size);//在特殊文件系统"shm"中建立映射文件
	error = PTR_ERR(file);
	if (IS_ERR(file))
		goto no_file;

	error = -ENOSPC;
	id = shm_addid(shp);//将shmid_kernel结构链入shm_ids
	if(id == -1) 
		goto no_id;
	shp->shm_perm.key = key;
	shp->shm_flags = (shmflg & S_IRWXUGO);
	shp->shm_cprid = current->pid;
	shp->shm_lprid = 0;
	shp->shm_atim = shp->shm_dtim = 0;
	shp->shm_ctim = CURRENT_TIME;
	shp->shm_segsz = size;
	shp->shm_nattch = 0;
	shp->id = shm_buildid(id,shp->shm_perm.seq);//将这个标识号转换成一个一体化的标示号
	shp->shm_file = file;//指向新建立的file
	file->f_dentry->d_inode->i_ino = shp->id;
	file->f_op = &shm_file_operations;//最后又重新设置了一遍f_op,这里是shm_file_operations,而不是shmem_file_operations
	shm_tot += numpages;
	shm_unlock (id);
	return shp->id;//返回的是一体化的标示号

no_id:
	fput(file);
no_file:
	kfree(shp);
	return error;
}
    shmid_kernel结构如下:

struct shmid_kernel /* private to the kernel */
{	
	struct kern_ipc_perm	shm_perm;
	struct file *		shm_file;
	int			id;
	unsigned long		shm_nattch;
	unsigned long		shm_segsz;
	time_t			shm_atim;
	time_t			shm_dtim;
	time_t			shm_ctim;
	pid_t			shm_cprid;
	pid_t			shm_lprid;
};
    shmem_file_setup,在特殊文件系统"shm"中建立映射文件,代码如下:

struct file *shmem_file_setup(char * name, loff_t size)
{
	int error;
	struct file *file;
	struct inode * inode;
	struct dentry *dentry, *root;
	struct qstr this;
	int vm_enough_memory(long pages);

	error = -ENOMEM;
	if (!vm_enough_memory((size) >> PAGE_SHIFT))
		goto out;

	this.name = name;
	this.len = strlen(name);
	this.hash = 0; /* will go */
	root = shmem_fs_type.kern_mnt->mnt_root;//shm特殊文件系统的根节点的dentry结构
	dentry = d_alloc(root, &this);//分配shm节点的dentry结构
	if (!dentry)
		goto out;

	error = -ENFILE;
	file = get_empty_filp();
	if (!file)
		goto put_dentry;

	error = -ENOSPC;
	inode = shmem_get_inode(root->d_sb, S_IFREG | S_IRWXUGO, 0);//分配shm节点的inode结构
	if (!inode) 
		goto close_file;

	d_instantiate(dentry, inode);//shm节点的dentry结构和shm节点的inode结构相关联
	dentry->d_inode->i_size = size;
	file->f_vfsmnt = mntget(shmem_fs_type.kern_mnt);
	file->f_dentry = dentry;//指向刚刚的dentry
	file->f_op = &shmem_file_operations;//设置如下
	file->f_mode = FMODE_WRITE | FMODE_READ;
	inode->i_nlink = 0;	/* It is unlinked */
	return(file);

close_file:
	put_filp(file);
put_dentry:
	dput (dentry);
out:
	return ERR_PTR(error);	
}
    其中shmem_fs_type.kern_mnt->mnt_root是在init_shmem_fs中建立的。

static DECLARE_FSTYPE(shmem_fs_type, "shm", shmem_read_super, FS_LITTER);

static int __init init_shmem_fs(void)
{
	int error;
	struct vfsmount * res;

	if ((error = register_filesystem(&shmem_fs_type))) {
		printk (KERN_ERR "Could not register shmem fs\n");
		return error;
	}

	res = kern_mount(&shmem_fs_type);
	if (IS_ERR (res)) {
		printk (KERN_ERR "could not kern_mount shmem fs\n");
		unregister_filesystem(&shmem_fs_type);
		return PTR_ERR(res);
	}

	devfs_mk_dir (NULL, "shm", NULL);
	return 0;
}
    shmem_get_inode,分配shm节点的inode结构,代码如下:

struct inode *shmem_get_inode(struct super_block *sb, int mode, int dev)
{
	struct inode * inode;

	spin_lock (&sb->u.shmem_sb.stat_lock);
	if (!sb->u.shmem_sb.free_inodes) {
		spin_unlock (&sb->u.shmem_sb.stat_lock);
		return NULL;
	}
	sb->u.shmem_sb.free_inodes--;
	spin_unlock (&sb->u.shmem_sb.stat_lock);

	inode = new_inode(sb);
	if (inode) {
		inode->i_mode = mode;
		inode->i_uid = current->fsuid;
		inode->i_gid = current->fsgid;
		inode->i_blksize = PAGE_CACHE_SIZE;
		inode->i_blocks = 0;
		inode->i_rdev = to_kdev_t(dev);
		inode->i_mapping->a_ops = &shmem_aops;//shmem_aops设置如下
		inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
		spin_lock_init (&inode->u.shmem_i.lock);
		switch (mode & S_IFMT) {
		default:
			init_special_inode(inode, mode, dev);
			break;
		case S_IFREG://i_op和i_fop设置如下
			inode->i_op = &shmem_inode_operations;
			inode->i_fop = &shmem_file_operations;
			break;
		case S_IFDIR:
			inode->i_op = &shmem_dir_inode_operations;
			inode->i_fop = &shmem_dir_operations;
			break;
		case S_IFLNK:
			inode->i_op = &page_symlink_inode_operations;
			break;
		}
		spin_lock (&shmem_ilock);
		list_add (&inode->u.shmem_i.list, &shmem_inodes);
		spin_unlock (&shmem_ilock);
	}
	return inode;
}
    inode->i_op = &shmem_inode_operations,代码如下:

static struct inode_operations shmem_inode_operations = {
	truncate:	shmem_truncate,
};
    inode->i_fop = &shmem_file_operations,代码如下:

static struct file_operations shmem_file_operations = {
	mmap:		shmem_mmap
};

    inode->i_mapping->a_ops = &shmem_aops,代码如下:

static struct address_space_operations shmem_aops = {
	writepage: shmem_writepage
};
    返回到 shmem_file_setup,file->f_op = &shmem_file_operations,如下:

static struct file_operations shmem_file_operations = {
	mmap:		shmem_mmap
};
   

    返回到newseg,shm_addid,将shmid_kernel结构链入shm_ids,代码如下:

static inline int sh
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值