C programming in the UNIX environment的编程手册,一般都会为进程间用共享内存的方法通信提供两组方法:
1. SYSTEM V定义的
int shmget(key_t key, int size, int shmflg); //得到一个共享内存标识符或创建一个共享内存对象
void *shmat(int shmid, const void *shmaddr, int shmflg); //把共享内存区对象映射到调用进程的地址空间
int shmdt(const void *shmaddr); //断开共享内存连接
int shmctl(int shmid, int cmd, struct shmid_ds *buf); //完成对共享内存的控制可以修改共享内存信息,也可以删除这片贡献内存。
例子代码如下:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <error.h>
#define SIZE 1024
int main()
{
int shmid ;
char *shmaddr ;
struct shmid_ds buf ;
int flag = 0 ;
int pid ;
shmid = shmget(IPC_PRIVATE, SIZE, IPC_CREAT|0600 ) ;
if ( shmid < 0 )
{
perror("get shm ipc_id error") ;
return -1 ;
}
pid = fork() ;
if ( pid == 0 )
{
shmaddr = (char *)shmat( shmid, NULL, 0 ) ;
if ( (int)shmaddr == -1 )
{
perror("shmat addr error") ;
return -1 ;
}
strcpy( shmaddr, "Hi, I am child process!\n") ;
shmdt( shmaddr ) ;
return 0;
} else if ( pid > 0) {
sleep(3 ) ;
flag = shmctl( shmid, IPC_STAT, &buf) ;
if ( flag == -1 )
{
perror("shmctl shm error") ;
return -1 ;
}
printf("shm_segsz =%d bytes\n", buf.shm_segsz ) ;
printf("parent pid=%d, shm_cpid = %d \n", getpid(), buf.shm_cpid ) ;
printf("chlid pid=%d, shm_lpid = %d \n",pid , buf.shm_lpid ) ;
shmaddr = (char *) shmat(shmid, NULL, 0 ) ;
if ( (int)shmaddr == -1 )
{
perror("shmat addr error") ;
return -1 ;
}
printf("%s", shmaddr) ;
shmdt( shmaddr ) ;
shmctl(shmid, IPC_RMID, NULL) ;
}else{
perror("fork error") ;
shmctl(shmid, IPC_RMID, NULL) ;
}
return 0 ;
}
编译 gcc shm.c –o shm。
执行 ./shm,执行结果如下:
shm_segsz =1024 bytes
shm_cpid = 9503
shm_lpid = 9504
Hi, I am child process!
2. POSIX定义的:
int shm_open(const char *name, int oflag, mode_t mode); //最主要的操作也是默认的操作就是在/dev/shm/下面,建立一个文件,文件名字是用户自己输入的。
参数:name 共享内存区的名字(不能为空), cflag 标志位 mode 权限位 返回值:成功返回0,出错返回-1
oflag参数必须含有O_RDONLY和O_RDWR标志,还可以指定如下标志:O_CREAT,O_EXCL或O_TRUNC.
mode参数指定权限位,它指定O_CREAT标志的前提下使用。 shm_open的返回值是一个整数描述字,它随后用作mmap的第五个参数。
int shm_unlink(const char *name); //删除一个共享内存区,返回值,0成功,-1失败。
int ftruncate(int fd, off_t length); //分配大小
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); //将一个文件或者其它对象映射进内存
返回值:如果mmap成功则返回映射首地址,如果出错则返回常数MAP_FAILED。
参数:
addr 指向映射存储区的起始地址;
len 映射的字节
prot 对映射存储区的保护要求
flag 标志位
filedes 要被映射文件的描述符
off 要映射字节在文件中的起始偏移量
参数解释如下:整体相当于磁盘文件的对应长度搬移到内存中。如果addr参数为NULL,内核会自己在进程地址空间中选择合适的地址建立映射。
如果addr不是NULL,则给内核一个提示,应该从什么地址开始映射,内核会选择addr之上的某个合适的地址开始映射。建立映射后,真正的
映射首地址通过返回值可以得到。off参数是从文件的什么位置开始映射,必须是页大小的
整数倍(在32位体系统结构上通常是4K)。
prot参数有四种取值:
PROT_EXEC表示映射的这一段可执行,例如映射共享库
PROT_READ表示映射的这一段可读
PROT_WRITE表示映射的这一段可写
PROT_NONE表示映射的这一段不可访问
flag参数有很多种取值,这里只讲两种,
MAP_SHARED多个进程对同一个文件的映射是共享的,一个进程对映射的内存做了修改,另一个进程也会看到这种变化。
MAP_PRIVATE多个进程对同一个文件的映射不是共享的,一个进程对映射的内存做了修改,另一个进程并不会看到这种变化,也不会真的写到文件中去。
int fstat(int fd, struct stat *buf) //当打开一个已经从在的共享内存时,我们调用fstat 来获取有关该对象的信息
int munmap(void *addr,size_t len) // 要删除一个已经建立的映射关系
int msync (void *addr,size_t len,int flags) //实现同步或者异步
由于POSIX标准比较通用,一般建议使用该标准定义的方法集。但在编译的时候可能会出现“undefined reference to `shm_open'”的错误,加上--lrt就可以编过。
客户服务段两进程通信实例:
服务端:
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <semaphore.h>
#include <string.h>
int main(int argc,char **argv)
{
int shm_id;
char *ptr;
sem_t *sem;
if (argc!=2)
{
printf("usage:shm_open<pathname>\n");
return -1;
}
shm_id=shm_open(argv[1],O_RDWR|O_CREAT,0644);/*第一步:创建共享内存区*/
if (shm_id==-1)
{
printf( "open shared memory error.errno=%d,desc=%s.\n", errno, strerror(errno));
return -1;
}
ftruncate(shm_id,100);/*第二步:调整共享内存区大小,shmid问shm_open的返回值*/
sem=sem_open(argv[1],O_CREAT,0644,0);/*创建信号量*/
if (sem==SEM_FAILED)
{
printf( "open semaphore error.errno=%d,desc=%s.\n", errno, strerror(errno));
return -1;
}
ptr=mmap(NULL,100,PROT_READ|PROT_WRITE,MAP_SHARED,shm_id,0);/*第三步:连接共享内存区*/
strcpy(ptr,"\0");
sem_wait(sem);/*第四步:申请信号量*/
printf("server : %s\n",ptr);/*输入共享内存区内容*/
strcpy(ptr,"\0");/*清空共享内存区*/
sem_unlink(argv[1]);/*第五步:删除信号量*/
shm_unlink(argv[1]);/*第六步:删除共享内存区*/
return 0;
}
客户端:
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <semaphore.h>
#include <string.h>
#include <errno.h>
int main(int argc,char **argv)
{
int shm_id;
char *ptr;
sem_t *sem;
if (argc!=2)
{
printf("usage:shm_open <pathname>\n");
return -1;
}
shm_id=shm_open(argv[1],O_RDWR,0);/*第一步:打开共享内存区*/
if (shm_id==-1)
{
printf( "open shared memory error.errno=%d,desc=%s.\n", errno, strerror(errno));
return -1;
}
else
{
printf( "open shared memory ok.\n");
}
sem=sem_open(argv[1],0);/*打开信号量*/
if (sem==SEM_FAILED)
{
printf( "open semaphore error.errno=%d,desc=%s.\n", errno, strerror(errno));
return -1;
}
else
{
printf( "open semaphore ok.\n");
}
ptr=mmap(NULL,100,PROT_READ|PROT_WRITE,MAP_SHARED,shm_id,0);/*连接共享内存区*/
fgets(ptr,10,stdin);/*从键盘读入数据到共享内存区*/
printf("user : %s",ptr);
sem_post(sem);/*释放信号量*/
return 0;
}