共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。这是因为所有进程共享同一块内存,共享内存在各种
进程间通信方式中具有最高的效率。访问共享内存区域和访问进程独有的内存区域一样快,并不需要通过系统调用或者其
它需要切入内核的过程来完成。同时它也避免了对数据的各种不必要的复制。例如 两个不同进程A、B共享内存的意思是,
同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。
由于多个进程共享同一块内存区域,必然需要某种同步机制,互斥锁和信号量都可以。
其中有关共享内存的一些基本的定义请重点参见 共享模型、绑定、脱离 :linux共享内存
共享内存实现步骤:
1、创建共享内存:shmget函数;
int shmget(key_t key,int size,int shmflg)
key: 数据类型key_t是在头文件sys/types.h中定义的,它是一个长整形的数据。
我们可以利用 ftok()函数创建key, 当成功执行的时候,一个key_t值将会被返回,否则-1被返回。我们可以使用,
strerror(errno)来确定具体的错误信息。
size:共享内存大小
返回值:成功则返回共享内存标识符;失败返回-1;
2、映射共享内存(连接到自身的地址空间中):shmat函数;
int shmat(int shmid,char* shmaddr,int flag)
shmid:返回的共享存储标识符;
shmaddr:共享内存在进程中的地址,不同进程这个地址不一样,通常设为0;
flag:决定以什么方式来确定映射的地址(通常为0);
返回值:成功返回共享内存映射到进程中的地址;失败返回-1;
3、解除映射:shmdt
int shmdt(char *shmaddr)
不需要共享内存时,需要把它从进程地址空间中脱离;
4、控制释放:shmctl
int shmctl( int shmid , int cmd , struct shmid_ds *buf );
int shmid是共享内存的ID。
int cmd是控制命令,可取值如下:
IPC_STAT 得到共享内存的状态
IPC_SET 改变共享内存的状态
IPC_RMID 删除共享内存
struct shmid_ds *buf是一个结构体指针。IPC_STAT的时候,取得的状态放在这个结构体中。
如果要改变共享内存的状态,用这个结构体指定。
返回值: 成功:0 失败:-1
下面我们举一个例子,下面的代码在父进程和子进程之间利用共享内存进行通信,父进程向共享内存中写入数据,
子进程读取数据。两个进程之间的控制采用了信号量的方法,父进程写入数据成功后信号量加1.子进程在访问信号
量之前先等待信号。
// ipc_shm.c
#include <stdio.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <string.h>
typedef int sem_t;
union semun {// 信号量操作的联合结构
int val;
struct semid_ds *buf;
unsigned short *array;
} arg;
sem_t CreateSem(key_t key, int value)// 建立信号量, 键值key 及信号量的初始值value
{
union semun sem;
sem_t semid; // 信号量ID
sem.val = value;
semid = semget(key,value,IPC_CREAT|0666); //获得信号量ID
if (-1 == semid)
{
printf("create semaphore error\n");
return -1;
}
semctl(semid,0,SETVAL,sem);// 发送命令,建立value个初始值的信号量
return semid;
}
/*
struct sembuf{
ushort sem_num;
short sem_op;
short sem_flg;
};
*/
void SetvalueSem(sem_t semid, int value)// 设置信号量的值
{
union semun sem;
sem.val = value;
semctl(semid,0,SETVAL,sem);
return ;
}
int GetvalueSem(sem_t semid)// 获得信号量的值
{
union semun sem;
return semctl(semid,0,GETVAL,sem);
}
void DestroySem(sem_t semid)// 销毁信号量
{
union semun sem;
sem.val = 0;
semctl(semid,0,IPC_RMID,sem);
}
int Sem_P(sem_t semid)// 增加信号量
{
struct sembuf sops={0,+1,IPC_NOWAIT}; // 建立信号量结构值
return (semop(semid,&sops,1));// 发送命令
}
int Sem_V(sem_t semid)//减少信号量值
{
struct sembuf sops={0,-1,IPC_NOWAIT};// 建立信号量结构值
return (semop(semid,&sops,1));
}
static char msg[]="你好,共享内存\n";
int main(void)
{
key_t key;
int semid,shmid;
char i,*shms,*shmc;
struct semid_ds buf;
int value = 0;
char buffer[80];
pid_t p;
key = ftok("./ipc_shm.c", 0);// 生成键值
shmid = shmget(key,1024,IPC_CREAT|0604);
semid = CreateSem(key,0);// 0 ? 1
p = fork();
if(p > 0)// 父进程
{
/* 父进程 */
/* 建立共享内存 */
shms = (char *)shmat(shmid,0,0);// 挂接共享内存
memcpy(shms, msg, strlen(msg)+1);// 赋值内容
sleep(2);
Sem_P(semid);// 获得共享内存的信号量
shmdt(shms);// 摘除共享内存
DestroySem(semid);// 销毁信号量
}
else if(p == 0)
{
shmc = (char *)shmat(shmid,0,0);// 挂接共享内存
Sem_V(semid);// 减少信号量
printf("共享内存的值为:%s\n",shmc);//
shmdt(shmc);
}
if(shmctl(shmid,IPC_RMID,NULL)==-1)
printf("shmctl delete error\n");
return 0;
//gcc -o shm icp_shm.c -g
}
在 ubuntu 下sleep时貌似信号量直接跳过,后再休眠。等待下一步实验