共享内存 :内核空间预留出来的一块内存,用于进程间通信
共享内存是一种最高效的进程间通信方式,因为进程可以直接读写内存,不需要任何数据的复制,为了在进程间交换信息,内核专门留出了一块内存区,这段内存可以由要访问的进程将其映射到自己的私有地址空间,因此,进程就可以直接读写这一内存而不需要进行数据的复制,从而大大提高了效率,当然,由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等
(1)int shmget(key_t key, size_t size, int shmflg);
功能:获取共享内存段的ID
参数:
@key IPC_PRIVATE 或 ftok()
@size 申请的共享内存段大小 [4k的倍数]
@shmflg IPC_CREAT | 0666 或 IPC_CREAT | IPC_EXCL
返回值:
成功返回ID,失败返回-1
(2)void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:映射共享内存到用户空间
参数:
@shmid 共享内存段ID
@shmaddr NULL:系统自动完成映射
@shmflg: SHM_RDONLY:只读 0:读写
返回值:
成功返回映射后的地址,失败返回(void *)-1
(3)int shmdt(const void *shmaddr);
功能:撤销映射
参数:
@shmaddr 共享内存映射的地址
注意:当一个进程结束的时候,它映射共享内存,会自动撤销映射
(4)int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:根据命令控制共享内存
参数:
@shmid 共享内存段的ID
@cmd IPC_STAT[获取属性],IPC_SET[设置属性],IPC_RMID[删除IPC对象]
@buf 保存属性
返回值:
成功返回0,失败返回 -1
----------------------------------------------------------------------------
注意:当我们调用shmctl删除共享内存的时候,并不会立即删除。只有当共享内存映射
次数为0,才会删除共享内存对象
-----------------------------------------------------------------------------
实例如下:
shm_r.c
#define SHM_SIZE 4096
typedef struct{
int read_flag;
int write_flag;
char mtxt[1];//标示数据存放地址
}data_t;
int shm_init(const char *pathname,int id)
{
int shmid;
key_t key;
key = ftok(pathname,id);
if(key < 0){
fprintf(stderr,"Fail to ftok : %s!
",strerror(errno));
exit(EXIT_FAILURE);
}
shmid = shmget(key,SHM_SIZE,IPC_CREAT | 0666);
if(shmid < 0){
fprintf(stderr,"Fail to shmget : %s
",strerror(errno));
exit(EXIT_FAILURE);
}
return shmid;
}
void read_shm(void *addr)
{
data_t *pdata;
pdata = (data_t *)addr;
pdata->read_flag = 0;
pdata->write_flag = 1;
while(1){
if(pdata->read_flag){
printf("Read : %s!
",pdata->mtxt);
pdata->write_flag = 1;
pdata->read_flag = 0;
}
}
}
//./a.out pathname
int main(int argc, const char *argv[])
{
int shmid;
void *addr;
if(argc < 2){
fprintf(stderr,"Usage : %s <pathname>
",argv[0]);
exit(EXIT_FAILURE);
}
shmid = shm_init(argv[1],'k');
addr = (void *)shmat(shmid,NULL,0);
if(addr == (void *)-1){
fprintf(stderr,"Fail to shmat : %s
",strerror(errno));
exit(EXIT_FAILURE);
}
read_shm(addr);
return 0;
}
shm_w.c
#define SHM_SIZE 4096
typedef struct{
int read_flag;
int write_flag;
char mtxt[1];//标示数据存放地址
}data_t;
int shm_init(const char *pathname,int id)
{
int shmid;
key_t key;
key = ftok(pathname,id);
if(key < 0){
fprintf(stderr,"Fail to ftok : %s!
",strerror(errno));
exit(EXIT_FAILURE);
}
shmid = shmget(key,SHM_SIZE,IPC_CREAT | 0666);
if(shmid < 0){
fprintf(stderr,"Fail to shmget : %s
",strerror(errno));
exit(EXIT_FAILURE);
}
return shmid;
}
void write_shm(void *addr)
{
data_t *pdata;
pdata = (data_t *)addr;
pdata->read_flag = 0;
pdata->write_flag = 1;
while(1){
if(pdata->write_flag){
fgets(pdata->mtxt,SHM_SIZE - 8,stdin);//读写标志共占8个字节
pdata->mtxt[strlen(pdata->mtxt) - 1] = ' ';
pdata->write_flag = 0;
pdata->read_flag = 1;
}
}
}
//./a.out pathname
int main(int argc, const char *argv[])
{
int shmid;
void *addr;
if(argc < 2){
fprintf(stderr,"Usage : %s <pathname>
",argv[0]);
exit(EXIT_FAILURE);
}
shmid = shm_init(argv[1],'k');
addr = (void *)shmat(shmid,NULL,0);
if(addr == (void *)-1){
fprintf(stderr,"Fail to shmat : %s
",strerror(errno));
exit(EXIT_FAILURE);
}
write_shm(addr);
return 0;
}
运行并执行shm_w.c
运行并执行shm_r.c
思路还是比较简单
上面的代码可以改进吗?
这段代码涉及到信号量的知识,可以查看:linux进程间通信--信号量
先引进:
sem.c
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO(Linux-specific) */
};
void init_sem_value(int sem_id,int sem_num,int value)
{
union semun sem_val;
sem_val.val = value;
if(semctl(sem_id,sem_num,SETVAL,sem_val) < 0)
{
perror("Fail to semctl");
exit(EXIT_FAILURE);
}
return ;
}
int init_sems(const char *filename,int nsem)
{
key_t key;
int sem_id;
key = ftok(filename,'b');
if(key < 0){
perror("Fail to ftok");
exit(EXIT_FAILURE);
}
sem_id = semget(key,nsem,IPC_CREAT|IPC_EXCL| 0666);
if(sem_id < 0)
{
//已经存在
if(errno == EEXIST)
{
//重新获得ID
sem_id = semget(key,nsem,IPC_CREAT | 0666);
}else{
//其他错误
perror("Fail to semget");
exit(EXIT_FAILURE);
}
//不存在,创建好后,初始化
}else{
init_sem_value(sem_id,0,0);//0号信号灯初始化为0
init_sem_value(sem_id,1,1);//1号信号灯初始化为1
}
return sem_id;
}
void P(int sem_id,int sem_num)
{
struct sembuf sem;
sem.sem_num = sem_num;
sem.sem_op = -1;
sem.sem_flg = 0;
if(semop(sem_id,&sem,1) < 0)
{
perror("Fail to semop for P
");
exit(EXIT_FAILURE);
}
return ;
}
void V(int sem_id,int sem_num)
{
struct sembuf sem;
sem.sem_num = sem_num;
sem.sem_op = 1;
sem.sem_flg = 0;
if(semop(sem_id,&sem,1) < 0)
{
perror("Fail to semop for V
");
exit(EXIT_FAILURE);
}
return ;
}
shm_r.c修改如下:
#define SHM_SIZE 4096
int shm_init(const char *pathname,int id)
{
int shmid;
key_t key;
key = ftok(pathname,id);
if(key < 0){
fprintf(stderr,"Fail to ftok : %s!
",strerror(errno));
exit(EXIT_FAILURE);
}
shmid = shmget(key,SHM_SIZE,IPC_CREAT | 0666);
if(shmid < 0){
fprintf(stderr,"Fail to shmget : %s
",strerror(errno));
exit(EXIT_FAILURE);
}
return shmid;
}
void read_shm(void *addr,int semid)
{
char *pdata;
pdata = (char *)addr;
while(1){
//P操作读资源
P(semid,0);//申请读资源
printf("Read : %s!
",pdata);
//V操作写资源
V(semid,1);//释放写资源
}
}
//./a.out pathname
int main(int argc, const char *argv[])
{
int shmid;
int semid;
void *addr;
if(argc < 2){
fprintf(stderr,"Usage : %s <pathname>
",argv[0]);
exit(EXIT_FAILURE);
}
shmid = shm_init(argv[1],'k');
addr = (void *)shmat(shmid,NULL,0);
if(addr == (void *)-1){
fprintf(stderr,"Fail to shmat : %s
",strerror(errno));
exit(EXIT_FAILURE);
}
//信号灯集
semid = init_sems(argv[1],2);
read_shm(addr,semid);
return 0;
}
shm_w.c修改如下:
#define SHM_SIZE 4096
int shm_init(const char *pathname,int id)
{
int shmid;
key_t key;
key = ftok(pathname,id);
if(key < 0){
fprintf(stderr,"Fail to ftok : %s!
",strerror(errno));
exit(EXIT_FAILURE);
}
shmid = shmget(key,SHM_SIZE,IPC_CREAT | 0666);
if(shmid < 0){
fprintf(stderr,"Fail to shmget : %s
",strerror(errno));
exit(EXIT_FAILURE);
}
return shmid;
}
void write_shm(void *addr,int semid)
{
char *data = (char *)addr;
while(1){
P(semid,1);
fgets(data,SHM_SIZE,stdin);
data[strlen(data) - 1] = ' ';
V(semid,0);
}
}
//./a.out pathname
int main(int argc, const char *argv[])
{
int shmid;
int semid;
void *addr;
if(argc < 2){
fprintf(stderr,"Usage : %s <pathname>
",argv[0]);
exit(EXIT_FAILURE);
}
shmid = shm_init(argv[1],'k');
addr = (void *)shmat(shmid,NULL,0);
if(addr == (void *)-1){
fprintf(stderr,"Fail to shmat : %s
",strerror(errno));
exit(EXIT_FAILURE);
}
semid = init_sems(argv[1],2);
write_shm(addr,semid);
return 0;
}
运行结果如上