使用消息队列时,一个进程要向队列中写入消息,这要引起从用户地址空间向内核地址空间的一次复制,同样一个进程进行消息读取时也要进行一次复制。共享内存的优点是完全省去了这些操作。共享内存会映射到进程的虚拟地址空间,进程对其可以直接访问,避免了数据的复制过程因此,共享内存是GNU/Linux现在可用的最快速的IPC机制。进程退出时会自动和已经挂接的共享内存区段分离,但是仍建议当进程不再使用共享区段时调用shmdt来卸载区段。主要有以下四个函数:
shmget( ) 创建一个新的共享内存区段
取得一个共享内存区段的描述符
shmctl( ) 取得一个共享内存区段的信息
为一个共享内存区段设置特定的信息
移除一个共享内存区段
shmat( ) 挂接一个共享内存区段
shmdt( ) 于一个共享内存区段的分离1. shmget(key, size,flag):int shmid = shmget(key_t key,int size, int flag) 该函数的第一个参数是一个用来标识共享内存块的键值。彼此无关的进程可以通过指定同一个键以获取对同一个共享内存块的访问。不幸的是,其它程序也可能挑选了同样的特定值作为自己分配共享内存的键值,从而产生冲突。用特殊常量IPC_PRIVATE作为键值可以保证系统建立一个全新的共享内存块。
该函数的第二个参数指定了所申请的内存块的大小。因为这些内存块是以页面为单位进行分配的,实际分配的内存块大小将被扩大到页面大小的整数倍。
第三个参数是一组标志,通过特定常量的按位或操作来shmget。这些特定常量包括:
IPC_CREAT:这个标志表示应创建一个新的共享内存块。通过指定这个标志,我们可以创建一个具有指定键值的新共享内存块。
IPC_EXCL:这个标志只能与 IPC_CREAT 同时使用。当指定这个标志的时候,如果已有一个具有这个键值的共享内存块存在,则shmget会调用失败。也就是说,这个标志将使线程获得一个“独有”的共享内存块。如果没有指定这个标志而系统中存在一个具有相同键值的共享内存块,shmget会返回这个已经建立的共享内存块,而不是重新创建一个。
模式标志:这个值由9个位组成,分别表示属主、属组和其它用户对该内存块的访问权限。其中表示执行权限的位将被忽略。指明访问权限的一个简单办法是利用中指定,并且在手册页第二节stat条目中说明了的常量指定。例如,S_IRUSR和S_IWUSR分别指定了该内存块属主的读写权限,而 S_IROTH和S_IWOTH则指定了其它用户的读写权限。 下面例子中shmget函数创建了一个新的共享内存块(当shm_key已被占用时则获取对一个已经存在共享内存块的访问),且只有属主对该内存块具有读写权限,其它用户不可读写。
int segment_id = shmget (shm_key, getpagesize (), IPC_CREAT | S_IRUSR| S_IWUSR ); 如果调用成功,shmget将返回一个共享内存标识符。如果该共享内存块已经存在,系统会检查访问权限,同时会检查该内存块是否被标记为等待摧毁状态。
返回值: 正确:共享存储区的内部标识符shmid, 错误:-12. char* shmat(int shmid, char* shmaddr, int msflag)要让一个进程获取对一块共享内存的访问,这个进程必须先调用 shmat(SHared Memory Attach,绑定到共享内存)。将 shmget 返回的共享内存标识符 SHMID 传递给这个函数作为第一个参数。该函数的第二个参数是一个指针,指向您希望用于映射该共享内存块的进程内存地址;如果您指定NULL则Linux会自动选择一个合适的地址用于映射。第三个参数是一个标志位,包含了以下选项: SHM_RND表示第二个参数指定的地址应被向下靠拢到内存页面大小的整数倍。如果您不指定这个标志,您将不得不在调用shmat的时候手工将共享内存块的大小按页面大小对齐。 SHM_RDONLY表示这个内存块将仅允许读取操作而禁止写入。 如果这个函数调用成功则会返回绑定的共享内存块对应的地址。通过 fork 函数创建的子进程同时继承这些共享内存块;如果需要,它们可以主动脱离这些共享内存块。 当一个进程不再使用一个共享内存块的时候应通过调用 shmdt(Shared Memory Detach,脱离共享内存块)函数与该共享内存块脱离。将由 shmat 函数返回的地址传递给这个函数。如果当释放这个内存块的进程是最后一个使用该内存块的进程,则这个内存块将被删除。对 exit 或任何exec族函数的调用都会自动使进程脱离共享内存块。
3. 撤销映射int shmdt(const void *shmaddr)将一个共享存储区从进程的虚拟空间中断开4. 对共享内存的操作shmctl(int shmid, int cmd, struct shmid_ds *buf)调用 shmctl("Shared Memory Control",控制共享内存)函数会返回一个共享内存块的相关信息。同时 shmctl 允许程序修改这些信息。该函数的第一个参数是一个共享内存块标识。
要获取一个共享内存块的相关信息,则为该函数传递 IPC_STAT 作为第二个参数,同时传递一个指向一个 struct shmid_ds 对象的指针作为第三个参数。
要删除一个共享内存块,则应将 IPC_RMID 作为第二个参数,而将 NULL 作为第三个参数。当最后一个绑定该共享内存块的进程与其脱离时,该共享内存块将被删除。
您应当在结束使用每个共享内存块的时候都使用 shmctl 进行释放,以防止超过系统所允许的共享内存块的总数限制。调用 exit 和 exec 会使进程脱离共享内存块,但不会删除这个内存块。 要查看其它有关共享内存块的操作的描述,请参考shmctl函数的手册页。
最后附上一段代码:父子进程之间的共享内存的代码#include #include #include #include int main() { printf("page size=%d\n",getpagesize()); int segment_id;
char* shared_memory; struct shmid_ds shmbuffer; int segment_size; const int shared_segment_size = 0x25500; /* 分配一个共享内存块 */ segment_id = shmget(IPC_PRIVATE, shared_segment_size, IPC_CREAT|IPC_EXCL|S_IRUSR|S_IWUSR ); /* 绑定到共享内存块 */ printf("id = %d\n", segment_id); shared_memory = (char*)shmat(segment_id, 0, 0); printf("shared memory attached at address %p\n", shared_memory); shmctl(segment_id, IPC_STAT, &shmbuffer); segment_size = shmbuffer.shm_segsz; printf("segment size: %d\n", segment_size); printf( "Number of attaches %d\n",( int )shmbuffer.shm_nattch ); sprintf(shared_memory, "Hello, world."); /* 在共享内存中写入一个字符串 */ shmdt(shared_memory); /* 脱离该共享内存块 */if(fork() == 0)
{
shmctl(segment_id, IPC_STAT, &shmbuffer); printf( "Number of attaches %d\n",( int )shmbuffer.shm_nattch ); printf("id = %d\n", segment_id); shared_memory = (char*)shmat(segment_id, (void*) 0x500000, 0);/* 重新绑定该内存块 */
shmctl(segment_id, IPC_STAT, &shmbuffer); printf( "Number of attaches %d\n",( int )shmbuffer.shm_nattch ); printf("%s\n", shared_memory); /* 输出共享内存中的字符串 */ shmdt(shared_memory); /* 脱离该共享内存块 */ } else
{ sleep(1); shmctl(segment_id, IPC_RMID, 0);/* 释放这个共享内存块 */
} return 0; }
阅读(2113) | 评论(0) | 转发(0) |