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),刚开始学习网络编程,如有不正确之处请大家多多指正。