共享内存的机制允许两个或多个进程共享一个给定的内存区域。
共享内存,是分配一块能被其他进程访问的内存,实现是通过将内存去
映射到共享它的进程的地址空间,使进程间的数据传送不再涉及内核,即进程间通信不需要通过进入内核的系统调用来实现。
共享内存与其他的进程间通信最大的优点是:数据的
复制只有两次,一次是从输入文件到共享内存区,一次从共享内存区到输出文件。而其他的方式则是需要复制4次:服务器将输入文件读入自己的进程空间,再从自己的进程空间写入管道/消息队列等;客户进程从管道/消息队列中读出数据到自己的进程空间,最后输出到客户指定的文件中。
通常,进程间信号量会被用来实现共享内存时的访问
同步问题。
内核会为每一个共享内存段设置了一个shhmid_ds结构体(定义在linux/shm.h中):
struct shmid_ds {
struct ipc_perm shm_perm; /* operation perms */
int shm_segsz; /* size of segment (bytes) 该共享内存段所占的字节数 */
time_t shm_atime; /* last attach time */
time_t shm_dtime; /* last detach time */
time_t shm_ctime; /* last change time */
unsigned short shm_cpid; /* pid of creator 创建该共享内存段的进程id*/
unsigned short shm_lpid; /* pid of last operator 最后访问该共享内存段的进程id*/
short shm_nattch; /* no. of current attaches 连接到该共享内存段的进程个数*/
/* the following are private */
unsigned short shm_npages; /* size of segment (pages) */
unsigned long *shm_pages; /* array of ptrs to frames -> SHMMAX */
struct vm_area_struct *attaches; /* descriptors for attaches */
};
内核为共享内存机制提供了下面几个系统调用:
创建(或获取)共享内存段id
int shmget ( key_t key, int size, int shmflg );
设置某个共享内存段
int shmctl ( int shmqid, int cmd, struct shmid_ds *buf );
连接到某个共享内存段
int shmat ( int shmid, char *shmaddr, int shmflg);
对应共享内存段结构体的shm_nattch自增。
返回值就是获取到的对应共享内存段的地址。
解开到某个共享内存段的连接(记得做这件事)
int shmdt ( char *shmaddr );
对应共享内存段结构体的shm_nattch自减,如果减到0,则内核会清除这个共享内存段。
共享内存段的地址空间:
使用以上系统调用获取到的共享内存的地址是映射到当前内存地址空间的,其地址处在堆的上方,栈的下方,并且紧靠着在栈下方。
下面是一个使用共享内存的实例,把这个程序稍作修改,同时运行两个进程,即可实现对共享内存的同时访问。
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <ctype.h>
#include <string.h>
#define SEGSIZE 100
void writeshm(int shmid, char *segptr, char *text)
{
strcpy(segptr, text);
printf("Write Done...\n");
}
void readshm(int shmid, char *segptr)
{
printf("Read from segptr: %s\n", segptr);
}
void removeshm(int shmid)
{
shmctl(shmid, IPC_RMID, 0);
printf("Shared memory segment marked for deletion\n");
}
int main(int argc, char *argv[])
{
key_t key;
int shmid;
char *segptr;
/* 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 a 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 = (char *)shmat(shmid, 0, 0)) == (char *)-1)
{
perror("shmat");
exit(1);
}
//char mywords[] = "hello!";
//writeshm(shmid, segptr, mywords);
readshm(shmid, segptr);
removeshm(shmid);
return 0;
}
原文资料: http://www.tldp.org/LDP/lpg/node65.html