进程间通信--共享内存
来源:互联网 发布:js md5算法 编辑:程序博客网 时间:2024/05/21 19:38
一、共享内存的数据结构
共享内存就是分配一块能被其他进程访问的内存。每个共享内存段在内核中维护着一个内部结构shmid_ds(和消息队列、信号量一样),该结构定义在头文件linux/shm.h中,代码如下所示:
struct shmid_ds{
struct ipc_perm shm_perm;
int shm_segsz;
__kernel_time_t shm_atime;
__kernel_time_t shm_dtime;
__kernel_time_t shm_ctime;
__kernel_ipc_pid_t shm_cpid;
__kernel_ipc_pid_t shm_lpid;
ushort shm_nattch;
ushort shm_unused;
void *shm_unused2;
void *shm_unused3;
};
代码中主要字段含义如下:
shm_perm:操作许可,里面包含共享内存的用户ID、组ID等信息。
shm_segsz:共享内存段的大小,以字节为单位。
shm_atime:最后一个进程访问共享内存的时间。
shm_dtime:最后一个进程离开共享内存的时间。
shm_ctime:最后一次修改共享内存的时间。
shm_cpid:创建共享内存的进程ID。
shm_lpid:最后操作共享内存的进程ID。
shm_nattch:当前使用该共享内存段的进程数量。
二、共享内存的创建与操作
1、共享内存区的创建
Linux下使用函数shmget来创建一个共享内存区,或者访问一个已存在的共享内存区。该函数定义在头文件sys/shm.h中,原型如下:
int shmget(key_t key,size_t size,int shmflg);
函数中:参数key是由ftok()得到的键值;参数size以字节为单位指定内存的大小;shmflg为操作标志位,它的值为一些宏,如下所示。
IPC_CREAT:调用shmget时,系统将些值与其他所有共享内存区的key进行比较,如果存在相同的key,说明共享内存区已存在,此时返回该共享内存区标识符,否则新建一个共享内存区并返回其标识符。
IPC_EXCL:该宏必须和IPC_CREAT一起使用,否则没有意义。当shmflg取IPC_CREAT|IPC_ECXL时,表示如果发现信号集已经存在,则返回-1,错误码为EEXIST。
注意:当创建一个新的共享内存区时,size值必须大于0;如果是访问一个已存在的共享内存区,置size为0。
2、共享内存区的操作
在使用共享内存区前,必须通过shmat函数将其附加到进程的地址空间。进程与共享内存就建立了连接。shmat调用成功后就会返回一个指向共享内存区的指针,使用该指针就可以访问共享内存区了,如果失败返回-1。该函数声明在sys/shm.h文件中,具体结构代码原型如下:
void *shmat(int shmid,const void *shmaddr,int shmflg);
参数shmid为shmget的返回值;参数shmflg为存取权限标志;参数shmaddr为共享内存的附加点。参数shmaddr不同取值情况的含义说明如下:
如果为空,则由内核选择一个空闲的内存区;如果非空,返回地址取决于调用者是否给shmflg参数指定了SHM_RND值,如果没有指定,则共享内存区附加到由shmaddr指定的地址;否则附加地址为shmaddr向下舍入一个共享内存低端边界地址后的地址SHMLBA,一个常址)。
通常将参数shmaddr设置为NULL。
当进程结束使用共享内存区时,要通过函数shmdt断开与共享内存区的连接。该函数声明在sys/shm.h文件中,具体结构代码原型如下:
int shmdt(const void* shmaddr);
参数shmaddr为shmat函数的返回值。该函数调用成功后,返回0,否则返回-1。进程脱离共享内存区后,数据结构shmid_ds中的shm_nattch就会减1。但是共享内存段依然存在,shm_nattch为0后,即没有任何进程再使用该共享内存区,共享内存区才在内核中被删除。一般来说,当一个进程终止时,它所附加的共享内存区都会自动脱离。
3、共享内存区的控制
Linux对共享内存区的控制是通过调用函数shmctl来完成的,该函数定义在头文件sys/shm.h中,原型代码如下所示:
int shmctl(int shmid,int cmd,struct shmid_ds *buf);
函数中:参数shmid为共享内存区的标识符;buf为指向shmid_ds结构体的指针;cmd为操作标志位,支持以下3种控制操作。
IPC_RMID:从系统中删除由shmid标识的共享内存区。
IPC_SET:设置内存内存区的shmid_ds结构。
IPC_STAT:读取共享内存区的shmid_ds结构,并将其存储到buf指向的地址中。
三、共享内存的应用实例
本例10-17通过读写者问题(不考虑优先级)来演示共享内存和信号量如何配合使用。这里的读者写者问题要求一个进程读共享内存的时候,其他进程不能写内存:当一个进程写共享内存的时候,其他进程不能读内存。
程序首先定义了一个包含公用函数的头文件10-17.h。
例10-17
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <errno.h>
#define SHM_SIZE 1024
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
};
/*创建信号量函数*/
int createsem(const char *pathname,int proj_id,int members,int init_val)
{
key_t msgkey;
int index,sid;
union semun semopts;
if ((msgkey = ftok(pathname,proj_id)) == -1)
{
perror("ftok error!\n");
return -1;
}
if ((sid = semget(msgkey,members,IPC_CREAT|0666)) == -1)
{
perror("semget call failed.\n");
return -1;
}
/*初始化操作*/
semopts.val = init_val;
for (index = 0;index < members;index++)
{
semctl(sid,index,SETVAL,semopts);
}
return (sid);
}
/*打开信号量函数*/
int opensem(const char *pathname,int proj_id)
{
key_t msgkey;
int sid;
if ((msgkey = ftok(pathname,proj_id)) == -1)
{
perror("ftok error!\n");
return -1;
}
if ((sid = semget(msgkey,0,IPC_CREAT|0666)) == -1)
{
perror("semget call failed.\n");
return -1;
}
return (sid);
}
/*P操作函数*/
int sem_p(int semid,int index)
{
struct sembuf buf ={0,-1,IPC_NOWAIT};
if (index < 0)
{
perror("index of array cannot equals a minus value!");
return -1;
}
buf.sem_num = index;
if (semop(semid,&buf,1) == -1)
{
perror("a wrong operation to semaphore occurred!");
return -1;
}
return 0;
}
/*V操作函数*/
int sem_v(int semid,int index)
{
struct sembuf buf = {0,+1,IPC_NOWAIT};
if (index < 0)
{
perror("index of array cannot equals a minus value!\n");
return -1;
}
buf.sem_num = index;
if (semop(semid,&buf,1) == -1)
{
perror("a wrong operation to semaphore occurred!\n");
return -1;
}
return 0;
}
/*删除信号集函数*/
int sem_delete(int semid)
{
return(semctl(semid,0,IPC_RMID));
}
/*等待信号为1*/
int wait_sem(int semid,int index)
{
while(semctl(semid,index,GETVAL,0) == 0)
{
sleep(1);
}
return 1;
}
/*创建共享内存函数*/
int createshm(char *pathname,int proj_id,size_t size)
{
key_t shmkey;
int sid;
/*获取键值*/
if ((shmkey = ftok(pathname,proj_id)) == -1)
{
perror("ftok error!\n");
return -1;
}
if ((sid = shmget(shmkey,size,IPC_CREAT|0666)) == -1)
{
perror("shmget call failed.\n");
return -1;
}
return (sid);
}
例10-18和例10-19写入和读取程序,两个程序在进入共享内存区之前,首先都检查信号集中信号的值是否为1(相当于是否能进入共享内存区),如果不为1,调用sleep()进入睡眠状态直到信号的值变为1。进入共享内存区之后,将信号的值减1(相当于加锁),这样就实现了互斥访问共享资源。在退出共享内存时,将信号值加1(相当于解锁)。
例10-18
#include "10-17.h"
int main()
{
int semid,shmid;
char *shmaddr;
char write_str[SHM_SIZE];
if ((shmid = createshm(".",'m',SHM_SIZE)) == -1)
{
exit(1);
}
if ((shmaddr = shmat(shmid,(char *)0,0)) == (char *)-1)
{
perror("attach shared memory error!\n");
exit(1);
}
if ((semid = createsem(".",'s',1,1)) == -1)
{
exit(1);
}
while (1)
{
wait_sem(semid,0);
sem_p(semid,0); //P操作
printf("write:");
fgets(write_str,1024,stdin);
int len = strlen(write_str) - 1;
write_str[len] = '\0';
strcpy(shmaddr,write_str);
sleep(10); //使10-19处于阻塞状态
sem_v(semid,0); //V操作
sleep(10); //等待10-19进行读操作
}
}
例10-19
#include "10-17.h"
int main()
{
int semid,shmid;
char *shmaddr;
if ((shmid = createshm(".",'m',SHM_SIZE)) == -1)
{
exit(1);
}
if ((shmaddr = shmat(shmid,(char *)0,0)) == (char *)-1)
{
perror("attach shared memory error!\n");
exit(1);
}
if ((semid = opensem(".",'s')) == -1)
{
exit(1);
}
while (1)
{
printf("reader:");
wait_sem(semid,0); //等待信号值为1
sem_p(semid,0); //P操作
printf("%s\n",shmaddr);
sleep(10); //使10-18处于阻塞状态
sem_v(semid,0); //V操作
sleep(10); //等待10-18进行写操作
}
}
程序说明:
注意10-18和10-19的头文件引用使用的是""这样把头文件10-17放到与10-18,10-19在同一目录下就可以引用了。
同时在两个终端运行10-18和10-19,在10-18端输入字符串“hello,world”,等待一会,看到10-19端输出后,再在10-18端的提示符后输入字符串“new information”。执行的结果如下:
$ ./10-18
write:hello world
write:new information
$ ./10-19
reader:hello world
reader:new information
从程序运行结果可以看出,10-18和10-19进程是同步的,10-18写入的信息均被10-19完整正确地读出,即使去掉程序中的sleep()也不会影响运行结果。
共享内存就是分配一块能被其他进程访问的内存。每个共享内存段在内核中维护着一个内部结构shmid_ds(和消息队列、信号量一样),该结构定义在头文件linux/shm.h中,代码如下所示:
struct shmid_ds{
struct ipc_perm shm_perm;
int shm_segsz;
__kernel_time_t shm_atime;
__kernel_time_t shm_dtime;
__kernel_time_t shm_ctime;
__kernel_ipc_pid_t shm_cpid;
__kernel_ipc_pid_t shm_lpid;
ushort shm_nattch;
ushort shm_unused;
void *shm_unused2;
void *shm_unused3;
};
代码中主要字段含义如下:
shm_perm:操作许可,里面包含共享内存的用户ID、组ID等信息。
shm_segsz:共享内存段的大小,以字节为单位。
shm_atime:最后一个进程访问共享内存的时间。
shm_dtime:最后一个进程离开共享内存的时间。
shm_ctime:最后一次修改共享内存的时间。
shm_cpid:创建共享内存的进程ID。
shm_lpid:最后操作共享内存的进程ID。
shm_nattch:当前使用该共享内存段的进程数量。
二、共享内存的创建与操作
1、共享内存区的创建
Linux下使用函数shmget来创建一个共享内存区,或者访问一个已存在的共享内存区。该函数定义在头文件sys/shm.h中,原型如下:
int shmget(key_t key,size_t size,int shmflg);
函数中:参数key是由ftok()得到的键值;参数size以字节为单位指定内存的大小;shmflg为操作标志位,它的值为一些宏,如下所示。
IPC_CREAT:调用shmget时,系统将些值与其他所有共享内存区的key进行比较,如果存在相同的key,说明共享内存区已存在,此时返回该共享内存区标识符,否则新建一个共享内存区并返回其标识符。
IPC_EXCL:该宏必须和IPC_CREAT一起使用,否则没有意义。当shmflg取IPC_CREAT|IPC_ECXL时,表示如果发现信号集已经存在,则返回-1,错误码为EEXIST。
注意:当创建一个新的共享内存区时,size值必须大于0;如果是访问一个已存在的共享内存区,置size为0。
2、共享内存区的操作
在使用共享内存区前,必须通过shmat函数将其附加到进程的地址空间。进程与共享内存就建立了连接。shmat调用成功后就会返回一个指向共享内存区的指针,使用该指针就可以访问共享内存区了,如果失败返回-1。该函数声明在sys/shm.h文件中,具体结构代码原型如下:
void *shmat(int shmid,const void *shmaddr,int shmflg);
参数shmid为shmget的返回值;参数shmflg为存取权限标志;参数shmaddr为共享内存的附加点。参数shmaddr不同取值情况的含义说明如下:
如果为空,则由内核选择一个空闲的内存区;如果非空,返回地址取决于调用者是否给shmflg参数指定了SHM_RND值,如果没有指定,则共享内存区附加到由shmaddr指定的地址;否则附加地址为shmaddr向下舍入一个共享内存低端边界地址后的地址SHMLBA,一个常址)。
通常将参数shmaddr设置为NULL。
当进程结束使用共享内存区时,要通过函数shmdt断开与共享内存区的连接。该函数声明在sys/shm.h文件中,具体结构代码原型如下:
int shmdt(const void* shmaddr);
参数shmaddr为shmat函数的返回值。该函数调用成功后,返回0,否则返回-1。进程脱离共享内存区后,数据结构shmid_ds中的shm_nattch就会减1。但是共享内存段依然存在,shm_nattch为0后,即没有任何进程再使用该共享内存区,共享内存区才在内核中被删除。一般来说,当一个进程终止时,它所附加的共享内存区都会自动脱离。
3、共享内存区的控制
Linux对共享内存区的控制是通过调用函数shmctl来完成的,该函数定义在头文件sys/shm.h中,原型代码如下所示:
int shmctl(int shmid,int cmd,struct shmid_ds *buf);
函数中:参数shmid为共享内存区的标识符;buf为指向shmid_ds结构体的指针;cmd为操作标志位,支持以下3种控制操作。
IPC_RMID:从系统中删除由shmid标识的共享内存区。
IPC_SET:设置内存内存区的shmid_ds结构。
IPC_STAT:读取共享内存区的shmid_ds结构,并将其存储到buf指向的地址中。
三、共享内存的应用实例
本例10-17通过读写者问题(不考虑优先级)来演示共享内存和信号量如何配合使用。这里的读者写者问题要求一个进程读共享内存的时候,其他进程不能写内存:当一个进程写共享内存的时候,其他进程不能读内存。
程序首先定义了一个包含公用函数的头文件10-17.h。
例10-17
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <errno.h>
#define SHM_SIZE 1024
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
};
/*创建信号量函数*/
int createsem(const char *pathname,int proj_id,int members,int init_val)
{
key_t msgkey;
int index,sid;
union semun semopts;
if ((msgkey = ftok(pathname,proj_id)) == -1)
{
perror("ftok error!\n");
return -1;
}
if ((sid = semget(msgkey,members,IPC_CREAT|0666)) == -1)
{
perror("semget call failed.\n");
return -1;
}
/*初始化操作*/
semopts.val = init_val;
for (index = 0;index < members;index++)
{
semctl(sid,index,SETVAL,semopts);
}
return (sid);
}
/*打开信号量函数*/
int opensem(const char *pathname,int proj_id)
{
key_t msgkey;
int sid;
if ((msgkey = ftok(pathname,proj_id)) == -1)
{
perror("ftok error!\n");
return -1;
}
if ((sid = semget(msgkey,0,IPC_CREAT|0666)) == -1)
{
perror("semget call failed.\n");
return -1;
}
return (sid);
}
/*P操作函数*/
int sem_p(int semid,int index)
{
struct sembuf buf ={0,-1,IPC_NOWAIT};
if (index < 0)
{
perror("index of array cannot equals a minus value!");
return -1;
}
buf.sem_num = index;
if (semop(semid,&buf,1) == -1)
{
perror("a wrong operation to semaphore occurred!");
return -1;
}
return 0;
}
/*V操作函数*/
int sem_v(int semid,int index)
{
struct sembuf buf = {0,+1,IPC_NOWAIT};
if (index < 0)
{
perror("index of array cannot equals a minus value!\n");
return -1;
}
buf.sem_num = index;
if (semop(semid,&buf,1) == -1)
{
perror("a wrong operation to semaphore occurred!\n");
return -1;
}
return 0;
}
/*删除信号集函数*/
int sem_delete(int semid)
{
return(semctl(semid,0,IPC_RMID));
}
/*等待信号为1*/
int wait_sem(int semid,int index)
{
while(semctl(semid,index,GETVAL,0) == 0)
{
sleep(1);
}
return 1;
}
/*创建共享内存函数*/
int createshm(char *pathname,int proj_id,size_t size)
{
key_t shmkey;
int sid;
/*获取键值*/
if ((shmkey = ftok(pathname,proj_id)) == -1)
{
perror("ftok error!\n");
return -1;
}
if ((sid = shmget(shmkey,size,IPC_CREAT|0666)) == -1)
{
perror("shmget call failed.\n");
return -1;
}
return (sid);
}
例10-18和例10-19写入和读取程序,两个程序在进入共享内存区之前,首先都检查信号集中信号的值是否为1(相当于是否能进入共享内存区),如果不为1,调用sleep()进入睡眠状态直到信号的值变为1。进入共享内存区之后,将信号的值减1(相当于加锁),这样就实现了互斥访问共享资源。在退出共享内存时,将信号值加1(相当于解锁)。
例10-18
#include "10-17.h"
int main()
{
int semid,shmid;
char *shmaddr;
char write_str[SHM_SIZE];
if ((shmid = createshm(".",'m',SHM_SIZE)) == -1)
{
exit(1);
}
if ((shmaddr = shmat(shmid,(char *)0,0)) == (char *)-1)
{
perror("attach shared memory error!\n");
exit(1);
}
if ((semid = createsem(".",'s',1,1)) == -1)
{
exit(1);
}
while (1)
{
wait_sem(semid,0);
sem_p(semid,0); //P操作
printf("write:");
fgets(write_str,1024,stdin);
int len = strlen(write_str) - 1;
write_str[len] = '\0';
strcpy(shmaddr,write_str);
sleep(10); //使10-19处于阻塞状态
sem_v(semid,0); //V操作
sleep(10); //等待10-19进行读操作
}
}
例10-19
#include "10-17.h"
int main()
{
int semid,shmid;
char *shmaddr;
if ((shmid = createshm(".",'m',SHM_SIZE)) == -1)
{
exit(1);
}
if ((shmaddr = shmat(shmid,(char *)0,0)) == (char *)-1)
{
perror("attach shared memory error!\n");
exit(1);
}
if ((semid = opensem(".",'s')) == -1)
{
exit(1);
}
while (1)
{
printf("reader:");
wait_sem(semid,0); //等待信号值为1
sem_p(semid,0); //P操作
printf("%s\n",shmaddr);
sleep(10); //使10-18处于阻塞状态
sem_v(semid,0); //V操作
sleep(10); //等待10-18进行写操作
}
}
程序说明:
注意10-18和10-19的头文件引用使用的是""这样把头文件10-17放到与10-18,10-19在同一目录下就可以引用了。
同时在两个终端运行10-18和10-19,在10-18端输入字符串“hello,world”,等待一会,看到10-19端输出后,再在10-18端的提示符后输入字符串“new information”。执行的结果如下:
$ ./10-18
write:hello world
write:new information
$ ./10-19
reader:hello world
reader:new information
从程序运行结果可以看出,10-18和10-19进程是同步的,10-18写入的信息均被10-19完整正确地读出,即使去掉程序中的sleep()也不会影响运行结果。
转自:http://hi.baidu.com/monalisa88188/blog/item/7b10eaae510016f2fbed5049.html
- 进程间通信--共享内存
- 进程间通信--共享内存
- Linux进程通信-共享内存
- 进程间通信-共享内存
- 进程间通信---共享内存
- 进程间通信--共享内存
- 进程间通信--共享内存
- 进程间通信-共享内存
- 进程间通信共享内存
- 进程间通信-共享内存
- 进程间通信---共享内存
- 进程间通信----共享内存
- 【进程间通信】共享内存
- 进程间通信共享内存
- 进程间通信----共享内存
- 进程间通信---共享内存
- 进程间通信--------共享内存
- 进程间通信---共享内存
- sql取两位小数
- 启发式搜索
- 浅谈VS安装部署
- ling to xml 实现 增删改查 功能
- 学习!
- 进程间通信--共享内存
- linux2.4的button的驱动
- 多进程
- 废除emlog的评论功能
- 详解 QT 多线程 TCP 文件接收服务器实例(2)
- 表空间创建格式及属性说明
- poj 1797 kruskal
- javascript判断是否是数组
- Extjs-扩展 Date String Function Number Array