UNP卷二 chapter14 System V共享内存区

1、System V共享内存区

System V共享内存区的学习类比Posix共享内存区,与调用shm_open后,再调用mmap相类似的是,System V先调用shmget,再调用shmat。

对每个共享内存区,内核维护如下的信息的结构,且定义在<bits/shm.h>头文件中。

struct shmid_ds {
	struct ipc_perm		shm_perm;/* operation permission struct*/
	size_t				shm_segsz;/* segment size*/
	pid_t				shm_lpid;/* pid of last operation*/
	pid_t				shm_cpid;/* creator pid*/
	shmatt_t			shm_nattch;/* current # attached*/
	shmat_t				shm_cnattch;/* in-core # attached*/
	time_t				shm_atime;/* last attach time*/
	time_t				shm_dtime;/* last detach time*/
	time_t				shm_ctime;/* last change time of this structure*/
};

2、shmget、shmat、shmdt和shmctl函数

针对shmget函数,创建一个新的共享内存区,或者访问一个已存在的共享内存区。

#include<sys/shm.h>
int shmget(key_t key, size_t size, int oflag);
					//返回:若成功则为共享内存区对象,若出错则为-1

返回值是一个称为共享内存区标识符的整数,被用来指代相应内存区。

key既可以是ftok的返回值,也可以是IPC_PRIVATE(即创建一个独一无二的IPC对象)。

size以字节为单位指定内存区的大小。当实际操作为创建一个新的共享内存区时,必须指定一个不为0的size值。如果实际操作为访问一个已存在的共享内存区,那么size为0

oflag为读写权限值的组合。其还可以与IPC_CREAT或IPC_CREAT | IPC_EXCL按位或,一般设为0。

当实际操作为创建一个新的共享内存区时,该内存区被初始化为size字节的0。

针对shmat函数,为shm创建或打开的共享内存区,通过调用shmat把它附接到调用进程的地址空间。

#include<sys/shm.h>
void* shmat(int shmid, const void* shmaddr, int flag);
					//返回:若成功则为映射区的起始地址,若出错则为-1

其中shmid是由shmget返回的标识符。shmat的返回值是所指定的共享内存区在调用进程内的起始地址。

如果shmaddr为NULL,则内核替调用者选择地址

flag指定访问共享内存区的权限,一般为0;

针对shmdt函数,当一个进程完成某个共享内存区的使用时,可调用shmdt断接这个内存区。

#include<sys/shm.h>
int shmdt(const void* shmaddr);
					//返回:若成功则为0,若出错则为-1

注意断接内存区不代表从内核中删除共享内存区,要删除可利用ipcrm命令指定shmid,或者利用shmctl函数IPC_RMID命令

针对shmctl函数,提供对一个内存区的多种操作。

#include<sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds* buff);
					//返回:若成功则为0,若出错则为-1

该函数提供三个命令:

IPC_RMID:从系统中删除由shmid标识的共享内存区并拆除它。

IPC_SET:给所指定的共享内存区设置其shmid_ds结构的以下三个成员:shm_perm.uid、shm_perm.gid和shm_perm.mode。即来自buff参数所指向的结构中相应成员。shm_ctime的值也用当前时间替换。

IPC_STAT:(通过buff参数)向调用者返回所指定共享区当前的shmid_ds结构。

3、与上述四个函数相关的简单程序

shmget程序:

#include	"unpipc.h"

int
main(int argc, char **argv)
{
	int		c, id, oflag;
	char	*ptr;
	size_t	length;

	oflag = SVSHM_MODE | IPC_CREAT;
	while ( (c = Getopt(argc, argv, "e")) != -1) {
		switch (c) {
		case 'e':
			oflag |= IPC_EXCL;
			break;
		}
	}
	if (optind != argc - 2)
		err_quit("usage: shmget [ -e ] <pathname> <length>");
	length = atoi(argv[optind + 1]);

	id = Shmget(Ftok(argv[optind], 0), length, oflag);//创建指定名字和大小的共享内存区,length指定大小
	ptr = Shmat(id, NULL, 0);//进行映射至调用进程地址空间中

	exit(0);
}

shmrmid程序:

#include	"unpipc.h"

int
main(int argc, char **argv)
{
	int		id;

	if (argc != 2)
		err_quit("usage: shmrmid <pathname>");

	id = Shmget(Ftok(argv[1], 0), 0, SVSHM_MODE);
	Shmctl(id, IPC_RMID, NULL);//利用IPC_RMID命令,从系统中删除指定共享内存区

	exit(0);
}

shmwrite程序:

#include	"unpipc.h"

int
main(int argc, char **argv)
{
	int		i, id;
	struct shmid_ds	buff;
	unsigned char	*ptr;

	if (argc != 2)
		err_quit("usage: shmwrite <pathname>");

	id = Shmget(Ftok(argv[1], 0), 0, SVSHM_MODE);//由于指定大小为0,表明是打开一个现存的共享内存区
	ptr = Shmat(id, NULL, 0);//将共享内存区映射到调用进程地址空间
	Shmctl(id, IPC_STAT, &buff);//获取共享内存区状态信息

		/* 4set: ptr[0] = 0, ptr[1] = 1, etc. */
	for (i = 0; i < buff.shm_segsz; i++)
		*ptr++ = i % 256;//写入相应数据

	exit(0);
}

shmread程序:

#include	"unpipc.h"

int
main(int argc, char **argv)
{
	int		i, id;
	struct shmid_ds	buff;
	unsigned char	c, *ptr;

	if (argc != 2)
		err_quit("usage: shmread <pathname>");

	id = Shmget(Ftok(argv[1], 0), 0, SVSHM_MODE);//打开共享内存区
	ptr = Shmat(id, NULL, 0);//映射至调用进程地址空间
	Shmctl(id, IPC_STAT, &buff);//获取状态信息

		/* 4check that ptr[0] = 0, ptr[1] = 1, etc. */
	for (i = 0; i < buff.shm_segsz; i++)
		if ( (c = *ptr++) != (i % 256))//验证早先写入共享内存区的数据信息
			err_ret("ptr[%d] = %d", i, c);

	exit(0);
}

System V与Posix共享内存的主要区别:Posix共享内存区对象的大小可在任何时刻通过调用ftruncate修改,而System V共享内存区对象的大小是在调用shmget创建时固定下来的。

以上知识点来均来自steven先生所著UNP卷二(version2),刚开始学习网络编程,如有不正确之处请大家多多指正。

猜你喜欢

转载自blog.csdn.net/TT_love9527/article/details/81382449