标题：共享内存
共享内存的创建
共享内存是存在于内核级别的一种资源，在shell中可以使用ipcs命令来查看当前系统IPC中的状态，在文件系统/proc目录下有对其描述的相应文件。函数shmget可以创建或打开一块共享内存区。函数原型如下：
#include <sys/shm.h>
int shmget( key_t key, size_t size, int flag );
函数中参数key用来变换成一个标识符，而且每一个IPC对象与一个key相对应。当新建一个共享内存段时，size参数为要请求的内存长度（以字节为单位）。
注意：内核是以页为单位分配内存，当size参数的值不是系统内存页长的整数倍时，系统会分配给进程最小的可以满足size长的页数，但是最后一页的剩余部分内存是不可用的。
当打开一个内存段时，参数size的值为0。参数flag中的相应权限位初始化ipc_perm结构体中的mode域。同时参数flag是函数行为参数，它指定一些当函数遇到阻塞或其他情况时应做出的反应。shmid_ds结构初始化如表14-4所示。
初始化
shmid_ds结构数据
初 值
shmid_ds结构数据
初 值
shm_lpid
0
shm_dtime
0
shm_nattach
0
shm_ctime
系统当前值
shm_atime
0
shm_segsz
参数 size
下面实例演示了使用shmget函数创建一块共享内存。程序中在调用shmget函数时指定key参数值为IPC_PRIVATE，这个参数的意义是创建一个新的共享内存区，当创建成功后使用shell命令ipcs来显示目前系统下共享内存的状态。命令参数-m为只显示共享内存的状态。
（1）在vi编辑器中编辑该程序如下：
程序清单14-8 create_shm.c 使用shmget函数创建共享内存
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <stdio.h>
#define BUFSZ 4096
int main ( void ) 
{
int shm_id; /*共享内存标识符*/
shm_id=shmget(IPC_PRIVATE, BUFSZ, 0666 ) ;
//shm_id=shmget(999,BUFSZ,0666|O_CREAT);
if (shm_id < 0 ) { /*创建共享内存*/
perror( "shmget" ) ;
exit ( 1 );
}
printf ( "successfully created segment : %d \n", shm_id ) ;
system( "ipcs -m"); /*调用ipcs命令查看IPC*/
exit( 0 );
}
（2）在shell中编译该程序如下：
$gcc create_shm.c -o create_shm
（3）在shell中运行该程序如下：
$./ create_shm 
successfully created segment : 2752516 
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status 
0x00000000 65536 root 600 393216 2 dest 
0x00000000 2654209 root 666 4096 0 
0x0056a4d5 2686978 root 600 488 1 
0x0056a4d6 2719747 root 600 131072 1 
0x00000000 2752516 root 666 4096 0 
上述程序中使用shmget函数来创建一段共享内存，并在结束前调用了系统shell命令ipcs –m来查看当前系统IPC状态。
共享内存的操作
由于共享内存这一特殊的资源类型，使它不同于普通的文件，因此，系统需要为其提供专有的操作函数，而这无疑增加了程序员开发的难度（需要记忆额外的专有函数）。使用函数shmctl可以对共享内存段进行多种操作，其函数原型如下：
#include <sys/shm.h>
int shmctl( int shm_id, int cmd, struct shmid_ds *buf );
函数中参数shm_id为所要操作的共享内存段的标识符，struct shmid_ds型指针参数buf的作用与参数cmd的值相关，参数cmd指明了所要进行的操作，其解释如表14-5所示。
cmd参数详解
cmd的值
意 义
IPC_STAT
取shm_id所指向内存共享段的shmid_ds结构，对参数buf指向的结构赋值
IPC_SET
使用buf指向的结构对sh_mid段的相关结构赋值，只对以下几个域有作用，shm_perm.
uid shm_perm.gid以及shm_perm.mode
注意此命令只有具备以下条件的进程才可以请求：
1．进程的用户ID等于shm_perm.cuid或者等于shm_perm.uid
2．超级用户特权进程
IPC_RMID
删除shm_id所指向的共享内存段，只有当shmid_ds结构的shm_nattch域为零时，才会真正执行删除命令，否则不会删除该段
注意此命令的请求规则与IPC_SET命令相同
SHM_LOCK
锁定共享内存段在内存，此命令只能由超级用户请求
SHM_UNLOCK
对共享内存段解锁，此命令只能由超级用户请求
使用函数shmat将一个存在的共享内存段连接到本进程空间，其函数原型如下：
#include <sys/shm.h>
void *shmat( int shm_id, const void *addr, int flag );
函数中参数shm_id指定要引入的共享内存，参数addr与flag组合说明要引入的地址值，通常只有2种用法，addr为0，表明让内核来决定第1个可以引入的位置。addr非零，并且flag中指定SHM_RND，则此段引入到addr所指向的位置（此操作不推荐使用，因为不会只对一种硬件上运行应用程序，为了程序的通用性推荐使用第1种方法），在flag参数中可以指定要引入的方式（读写方式指定）。
%说明：函数成功执行返回值为实际引入的地址，失败返回–1。shmat函数成功执行会将shm_id段的shmid_ds结构的shm_nattch计数器的值加1。
当对共享内存段操作结束时，应调用shmdt函数，作用是将指定的共享内存段从当前进程空间中脱离出去。函数原型如下：
#include <sys/shm.h>
int shmdt( void *addr);
参数addr是调用shmat函数的返回值，函数执行成功返回0，并将该共享内存的shmid_ds结构的shm_nattch计数器减1，失败返回–1。
下面实例演示了操作共享内存段的流程。程序的开始部分先检测用户是否有输入，如出错则打印该命令的使用帮助。接下来从命令行读取将要引入的共享内存ID，使用shmat函数引入该共享内存，并在分离该内存之前睡眠3秒以方便查看系统IPC状态。
（1）在vi编辑器中编辑该程序如下：
程序清单14-9 opr_shm.c 操作共享内存段
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <stdio.h>
int main ( int argc, char *argv[] )
{
int shm_id ;
char * shm_buf;
if ( argc != 2 ){ /* 命令行参数错误 */
printf ( "USAGE: atshm <identifier>" ); /*打印帮助消息*/
exit (1 );
}
shm_id = atoi(argv[1]); /*得到要引入的共享内存段*/
/*引入共享内存段，由内核选择要引入的位置*/
if ( (shm_buf = shmat( shm_id, 0, 0)) < (char *) 0 ){
perror ( "shmat" );
exit (1);
}
printf ( " segment attached at %p\n", shm_buf ); /*输出导入的位置*/
system("ipcs -m");
sleep(3); /* 休眠 */
if ( (shmdt(shm_buf)) < 0 ) { /*与导入的共享内存段分离*/
perror ( "shmdt");
exit(1);
}
printf ( "segment detached \n" );
system ( "ipcs -m " ); /*再次查看系统IPC状态*/
exit ( 0 );
}
（2）在shell中编译该程序如下：
$gcc opr_shm.c -o opr_shm
（3）在shell中运行该程序如下：
$./ opr_shm 2752516
segment attached at 0xb7f29000
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status 
0x00000000 65536 root 600 393216 2 dest 
0x00000000 2654209 root 666 4096 0 
0x0056a4d5 2686978 root 600 488 1 
0x0056a4d6 2719747 root 600 131072 1 
0x00000000 2752516 root 666 4096 1
segment detached
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status 
0x00000000 65536 root 600 393216 2 dest 
0x00000000 2654209 root 666 4096 0 
0x0056a4d5 2686978 root 600 488 1 
0x0056a4d6 2719747 root 600 131072 1 
0x00000000 2752516 root 666 4096 0
上述程序中从命令行中读取所要引入的共享内存ID，并使用shmat函数引入该内存到当前的进程空间中。注意在使用shmat函数时，将参数addr的值设为0，所表达的意义是由内核来决定该共享内存在当前进程中的位置。由于在编程的过程中，很少会针对某一个特定的硬件或系统编程，所以由内核决定引入位置也就是shmat推荐的使用方式。在导入后使用shell命令ipcs –m来显示当前的系统IPC的状态，可以看出输出信息中nattch字段为该共享内存时的引用值，最后使用shmdt函数分离该共享内存并打印系统IPC的状态。
共享内存使用注意事项
共享内存相比其他几种方式有着更方便的数据控制能力，数据在读写过程中会更透明。当成功导入一块共享内存后，它只是相当于一个字符串指针来指向一块内存，在当前进程下用户可以随意的访问。缺点是，数据写入进程或数据读出进程中，需要附加的数据结构控制。
结构示意
%说明：图中两个进程同时遵循一定的规则来读写该内存。同时，在多进程同步或互斥上也需要附加的代码来辅助共享内存机制。
在共享内存段中都是以字符串的默认结束符为一条信息的结尾。每个进程在读写时都遵守这个规则，就不会破坏数据的完整性。