进程间通信——共享内存

共享内存(Shared Memory)
共享内存,简单的说就是被多个进程共享的内存。它在各种进程通信方法中是最快的,因为它是将信息直接映射到内存中,

省去了其它 IPC方法的中间步骤。
1.shmid_ds
共享内存也有一个给系统内存用来保存相关信息的机构,就是shmid_ds。
struct shmid_ds{
struct ipc_perm shm_perm; //operation
int shm_segsz; //size of segment
_kernel_time_t shm_atime; //last attach time
_kernel_time_t shm_dtime; //last detach time
_kernel_time_t shm_ctime; //last change time
_kernel_ipc_pid_t shm_cpid; //pid of creator
_kernel_ipc_pid_t shm_lpid //pid of last operator
unsigned short shm_nattch; //no. of current attaches
unsigned short shm_unused; //compatibility
void *shm_unused2; //ditto - used by DIPC
void *shm_unused3; //unused
}


其中:
shm_perm 成员储存了共享内存对象的存取权限及其它一些信息。
shm_segsz 成员定义了共享的内存大小(以字节为单位)。
shm_atime 成员保存了最近一次进程连接共享内存的时间。
shm_dtime 成员保存了最近一次进程断开与共享内存的连接的时间。
shm_ctime 成员保存了最近一次 shmid_ds 结构内容改变的时
shm_cpid 成员保存了创建共享内存的进程的 pid。
shm_lpid 成员保存了最近一次连接共享内存的进程的 pid。
shm_nattch 成员保存了与共享内存连接的进程数目。
剩下的三个成员被内核保留使用,这里就不介绍了。

有关的函数
1、sys_shmget()函数
使用shmget()函数来创建新的获取得已有的共享内存。
系统调用:shmget()
函数声明:int shmget(key_t key,int size,int shmflg);
返回值:shared memory segment identigier on success
-1 on error: errno =

shmget()函数的第一个参数key 是共享内存的关键字;第二个参数 size 是创建的共享内存的大小,以字节为单位。第三个参

数 shmflg 是控制函数行为的标志量,其取值的含义和作用和 msgget()及semget()函数的对应参数都是相同的.

int open_shm(key_t keyval, int segsize)
{
int shmid;
if(shmid=shmget(keyval, segsize,IPC_CREAT|0660))==-1)
{
return(-1);
}
return(shmid);
}


2、shmat()函数
当一个进程使用 shmget()函数得到了共享内存的标识符之后,就可以使用shmat()函数来将共享内存映射到进程自己的内

存空间内。
系统调用:shmat()
函数声明: int shmat(int shmid, char *shmaddr, int shmflg);
返回值: address at which segment was attached to the process, or
-1 on error: errno =
第一个参数是共享内存的标识符。
第二个参数 shmaddr 指定了共享内存映射的地址。因为这样必须要预先分配内存,十分不便,所以我们在使用时常常将这

个参数置零,这样系统会自动为映射分配一块未使用的内存。如果指定了地址,可以给第三个参数 shmflg 指定SHM_RND

标志来强迫将内存大小设定为页面的尺寸。
如果指定了 SHM_RDONLY参数,共享内存将被映射成只读。
映射成功后,函数返回指向映射内存的指针。
下面的这段代码演示了 shmat()函数的使用:
char *attach_segment( int shmid )
{
return(shmat(shmid, 0, 0));
}
得到了映射内存的指针之后,我们就可以像读写普通内存一样对共享内存进行读写了。

3、shmctl()函数
和前两个 IPC 对象一样,共享内存也有一个直接对其进行操作的函数,就是 shmctl()函数。
系统调用: shmctl()
函数声明:int shmctl(int shmqid, int cmd, struct shmid_ds *buf);
返回值:0 on success
-1 on error: errno =

这个函数和 msgget()函数十分相似,用法也相同。它支持的操作有:
IPC_STAT 获得共享内存的信息。
IPC_SET 设定共享内存的信息。
IPC_RMID 删除共享内存。
需要说明的是,当执行 IPC_RMID 操作时,系统并不是立即将其删除,而只是将其标为待删,然后等待与其连接的进程断开连接。只有当所有的连接都断开以后系统才执行真正的删除操作。当然,如果执行 IPC_RMID 的时候没有任何的连接,删除将是立即的。

4、shmdt()函数
当一个进程不再需要某个共享内存的映射时,就应该使用 shmdt()函数断开映射。
系统调用: shmdt()
函数声明: int shmdt ( char *shmaddr );
返回值: -1 on error: errno = EINVAL (Invalid attach address passed)
shmdt()函数唯一的参数是共享内存映射的指针。

共享内存应用举例——shmtool,交互式的共享内存使用工具
shmtool.c

#include
#include
#include

#define SEGSIZE 100
writeshm(int shmid, char *segptr, char *text)
{
strcpy(segptr, text);
printf("Done..\n");
}

readshm(int shmid, char *segptr)
{
printf("segptr: %s\n", segptr);
}

removeshm(int shmid)
{
shmctl(shmid, IPC_RMID, 0);
printf("Shared memory segment marked for deletion\n");
}

changemode(int shmid, char *mode)
{
struct shmid_ds myshmds;

/*Get current values for internal data structure */
shmctl(shmid, IPC_STAT, &myshmds);

/* Display old permissions */
printf("Old permissions were:%o\n", myshmds.shm_perm.mode);

/*Convert and load the mode */
sscanf(mode, "%o", &myshmds.shm_perm.mode);

/* Update the mode */
shmctl(shmid, IPC_SET, &myshmds);

printf("New permissions are: %o", myshmds.shm_perm.mode);

}
usage()
{
fprintf(stderr, "shmtool - A utility for tinkering with shared memory\n");
fprintf(stderr, "\nUSAGE: shmtool (w)rite \n");
fprintf(stderr, " (r)ead\n");
fprintf(stderr, " (d)elete\n");
fprintf(stderr, " (m)ode change \n");
exit(1);
}
main(int argc, char *argv[])
{
key_t key;
int shmid,cntr;
char *segptr;
if(argc==1) usage();

/*Create unique key via call to ftok() */
key =ftok(".", 'S');

/*Open the shared memory segment -create if necessary */
if((shmid =shmget(key, SEGSIZE, IPC_CREAT|IPC_EXCL|0666))==-1)
{
printf("Shared memory segment exists - opening as client\n");
/* Segment probably already exists - try as client */
if((shmid = shmget(key, SEGSIZE, 0))==-1)
{
perror("shmget");
exit(1);
}


}
else{
printf("Creating new shared memory segment\n");

}

/* Attach (map) the shared memory segment into the current process */
if((segptr= shmat(shmid,0,0))==-1)
{
perror("shmat");
exit(1);

}

switch(tolower(argv[1][0]))
{
case 'w':
writeshm(shmid, segptr, argv[2]);
break;
case 'r':
readshm(shmid, segptr);
break;
case 'd':
removeshm(shmid);
break;
case 'm':
changemode(shmid, argv[2]);
break;
default: usage();

}

}

/home/l/g/tomotoboy/ipc/shm >shmtool r
Shared memory segment exists - opening as client
segptr: test
/home/l/g/tomotoboy/ipc/shm >shmtool w "hello:this is a test"
Shared memory segment exists - opening as client
Done..
/home/l/g/tomotoboy/ipc/shm >shmtool r
Shared memory segment exists - opening as client
segptr: hello:this is a test
/home/l/g/tomotoboy/ipc/shm >shmtool d
Shared memory segment exists - opening as client
Shared memory segment marked for deletion
/home/l/g/tomotoboy/ipc/shm >shmtool r
Creating new shared memory segment
segptr:

你可能感兴趣的:(Linux/Unix,C,C++,C#)