linux进程间通信-共享内存

共享内存

共享内存是被多个进程共享的一块物理内存

  • 物理内存
  • 映射到各个进程的虚拟内存空间
  • 各进程通过虚拟内存空间地址来操作共享内存
  • 进程间共享数据的最快方法
  • 本身不提供同步机制,可通过信号量同步
  • 子进程不继承父进程创建的共享内存,而是与父进程继续共享那块内存;在子进程中映射的虚拟地址和父进程是一样的。

函数

创建共享内存

 #include <sys/shm.h>
 int shmget(key_t key, size_t size, int shmflg);

其中key为用户指定的共享内存键值;size为共享内存大小;shmflg为IPC_CREAT 、 IPC_EXCL等权限的组合。
如果创建成功,则返回内核中共享内存的标识ID。如果失败则返回-1。

控制共享内存

#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

其中shmid是共享内存的标识ID;buf为指向共享内存属性结构体的指针;cmd可以取一下命令宏:
IPC_STAT 获取共享内存的属性
IPC_SET 设置共享内存的属性
IPC_RMID 删除共享内存
SHM_LOCK 锁定共享内存页面(物理内存不和外存进行换入换出操作)
SHM_UNLOCK 解除锁定共享内存页面

映射共享内存

#include <sys/shm.h>
void* shmat(int shmid, char *shmaddr, int shmflg);

其中shmid是共享内存标识ID;shmaddr映射到进程虚拟空间的地址,一般设置为0(即由操作系统分配);shmflg来设置内存的特性,一般也设置为0;
成功则返回虚拟内存空间的地址;失败则返回-1。

解除映射共享内存

#include <sys/shm.h>
void* shmdt(char *shmaddr);

shmaddr为映射到进程虚拟空间的地址。

简单实例

#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
    int shmid;
//创建共享内存
    if((shmid = shmget(IPC_PRIVATE,1024,
        IPC_CREAT|IPC_EXCL|0777))<0)
    {
        perror("shmget error");
        exit(1);
    }
//映射共享内存
    int *p = (int *)shmat(shmid,0,0);
    if(p == (int *)-1)
    {
        perror("shmat error");
        exit(1);
    }
//对共享内存写入数据
    *p = 100;
    *(p+1) = 200;
    *(p+2) = 300;
//解除共享内存映射
    shmdt(pi);
//删除共享内存
    shmctl(shmid,IPC_RMID,NULL);
    exit(0);
}

共享内存只有一个,使用完毕之后,可以在创建它的进程中删除,也可以在其他进程中删除。

共享内存的同步

使用信号量的PV操作进行同步。
为共享内存定义两个信号量,分别为S1,S2,将这两个信号量的值都初始化为0。
数据生产者:写入数据——V(S1)——P(S2)
数据消费者:P(S1)——读取数据——V(S2)
以上信号量的使用将实现如下逻辑:
1.生产者在写入数据之前,消费者处于阻塞状态,等待生产者写入数据而后读取;
2.生产者写入数据之后,信号量S1被加1,从而允许消费者读取数据;另一方面S2减1,导致生产者被阻塞;
3.在数据消费者的进程中,P(S1)之后允许进程使用共享内存资源,进程不阻塞,从而可以执行读取数据的操作。
4.消费者读完数据之后,对S2加1,解除数据生产者的阻塞状态,使其进程继续运行,产生数据。
如此,共享内存中数据的读写则实现了同步。

创建信号量集(该信号量集中有semnums个信号量)

int semid = semget(IPC_PRIVATE,semnums,
        IPC_CREAT|IPC_EXCL|0777);

初始化信号量集(用semun结构体进行初始化)

int i = 0;
union semun un;
unsigned short *array =
    (unsigned short *)calloc(semnums,
    sizeof(unsigned short));
for(i=0;i<semnums;i++)
{
    array[i]=value;
}
un.array = array;
if(semctl(semid,0,SETALL,un)<0)
{
    perror("semctl error");
    exit(-1);
}
free(array);

P操作(减值操作,使用sembuf结构体。给信号量集semid其中的信号量semnum减去value)

struct sembuf ops[]={{semnum, -value, SEM_UNDO}};
if(semop(semid,ops,sizeof(ops)/sizeof(struct sembuf))<0)
{
    perror("semop error");
}

V操作(加值操作,使用sembuf结构体。给信号量集semid其中的信号量semnum加去value)

struct sembuf ops[]={{semnum, value, SEM_UNDO}};
if(semop(semid,ops,sizeof(ops)/sizeof(struct sembuf))<0)
{
    perror("semop error");
}

销毁信号量集

if(semctl(semid,0,IPC_RMID,NULL)<0)
{
    perror("semctl");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值