您的位置:首页 > 其它

进程间的通信之共享内存

2017-07-25 11:06 232 查看
一、共享内存        

共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。两个不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间,这一块空间是内核专门提供给多个进程交换信息的。但是内核并没有实现对这个内存进行同步,必然需要某种同步机制,互斥锁和信号量都可以。采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。



二、函数原型

1.创建/打开共享内存

 int shmget(key_t key,  int size,  int shmflag );
key:IPC_PRIVATE或ftok的返回值
size: 共享内存的大小
shmflag: 共享内存的权限,同open函数
返回值: 成功:共享内存段标识符  出错:-1
注意:这个key值主要是针对不同的进程如何去获取所要的共享内存
IPC_PRIVATE:这个的默认值是0,这样会导致不同进程(无血缘的)之间无法去获取同一块内存id,在有血缘进程间还是可以使用的,但是必须在fork函数之前使用。ftok返回:key_t ftok(const char *fname, int proj_id);fname 随便一个已经存在的文件, proj_id 可以任意取值,但是必须在0-255.key还可以自己任意指定一个没有用过的关联值。用ipcs查看已经用过的key值。
shmflag:加上IPC_CREAT这个flag,例如0666 | IPC_CREAT。因为不同进程获取同一块内存id,第一个执行的进程是创建,后面执行的进程都是打开。
2.映射共享内存,把指定的共享内存映射到进程地址空间用于访问
void* shmat(int shmid, const void * shmaddr, int shmflag);
shmid: 共享内存标识符,即上一个函数的成功返回值
shmaddr: 将共享内存映射到指定地址,NULL 系统自动完成映射
shmflag: 对该内存的读写权限,一般为0,可以读写, SHM_RDONLY 只读
返回值: 成功:映射后的地址  出错:-1
注意:shmaddr可以为字符数组,结构体, 也可以为NULL,成功之后再把返回的地址赋给某一个变量。
3.撤销共享内存映射
int shmdt(const void * shmaddr);
shmaddr:共享内存映射后的地址
返回值:成功:0   出错:-1
注意:这个只是让原本指向这块共享内存的地址不指向这里,并没有删除这块内存
4.控制共享内存
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmid: 共享内存标识符
cmd:IPC_STAT(获取对象属性)
    IPC_SET(获取对象属性)
    IPC_RMID(删除对象)
buf:指定IPC_STAT/IPC_SET时用以保存/设置属性
返回值:成功: 0  出错: -1
注意:只要有一个进程进行删除就行

三、代码示例

shm_write.c
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/shm.h>
#include<unistd.h>
#include<string.h>
#include<sys/sem.h>

#define BUF_SIZE 1024

int p(int semid)
{
struct sembuf sem_p;
sem_p.sem_num = 0;
sem_p.sem_op = -1;
sem_p.sem_flg = SEM_UNDO;
if(semop(semid, &sem_p, 1) == -1)
{
perror("semop failed!");
exit(1);
}
return 0;
}

int v(int semid)
{
struct sembuf sem_v;
sem_v.sem_num = 0;
sem_v.sem_op = 1;
sem_v.sem_flg = SEM_UNDO;
if(semop(semid, &sem_v, 1) == -1)
{
perror("semop failed!");
exit(1);
}

return 0;
}

int main(int argc, char* argv[])
{
void* shmaddr;
char* buf;
int shm_id;
int sem_write;
int sem_read;

if (-1 == (sem_write = semget((key_t)1245, 1, 0666 | IPC_CREAT)))
{
perror("semget failed!");
exit(1);
}
if (-1 == (sem_read = semget((key_t)1243, 1, 0666 | IPC_CREAT)))
{
perror("semget failed!");
exit(1);
}
if (semctl(sem_write, 0, SETVAL, 1) < 0)
{
perror("semctl failed!");
exit(1);
}
if (semctl(sem_read, 0, SETVAL, 0) < 0)
{
perror("semctl failed!");
exit(1);
}
if (-1 == (shm_id = shmget((key_t)1211, BUF_SIZE, 0666 | IPC_CREAT)))
{
perror("shmat failed!");
exit(1);
}
if ((void*)(-1) == (shmaddr = shmat(shm_id, NULL, 0)))
{
perror("shmat failed!");
exit(1);
}
buf = (char*)shmaddr;

while(1)
{
p(sem_write);
printf("write buf: ");
fgets(buf, BUF_SIZE, stdin);
buf[strlen(buf) - 1] = '\0'; //Remove the newline, Plus the end character
v(sem_read);
if (strncmp(buf, "bye", 3) == 0)
{

break;
}
usleep(500);
}

if (shmdt(shmaddr) < 0)
{
perror("shmdt failed!");
exit(1);
}
if (shmctl(shm_id, IPC_RMID, NULL) < 0)
{
perror("shmctl failed!");
exit(1);
}
#if 0
if (semctl(sem_read, 0, IPC_RMID, 0) < 0)
{
perror("semctl failed!");
exit(1);
}
if (semctl(sem_write, 0, IPC_RMID, 0) < 0)
{
perror("semctl failed!");
exit(1);
}
#endif
return 0;
}
shm_read.c
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/shm.h>
#include<unistd.h>
#include<string.h>
#include<sys/sem.h>

#define BUF_SIZE 1024

int p(int semid)
{
struct sembuf sem_p;
sem_p.sem_num = 0;
sem_p.sem_op = -1;
sem_p.sem_flg = SEM_UNDO;
if(semop(semid, &sem_p, 1) == -1)
{
perror("semop failed!");
exit(1);
}
return 0;
}

int v(int semid)
{
struct sembuf sem_v;
sem_v.sem_num = 0;
sem_v.sem_op = 1;
sem_v.sem_flg = SEM_UNDO;
if(semop(semid, &sem_v, 1) == -1)
{
perror("semop failed!");
exit(1);
}

return 0;
}

int main(int argc, char* argv[])
{
void* shmaddr;
char* buf;
int shm_id;
int sem_read;
int sem_write;

if (-1 == (sem_write = semget((key_t)1245, 1, 0666 | IPC_CREAT)))
{
perror("semget failed!");
exit(1);
}
if (-1 == (sem_read = semget((key_t)1243, 1, 0666 | IPC_CREAT)))
{
perror("semget failed!");
exit(1);
}

if (semctl(sem_write, 0, SETVAL, 1) < 0)
{
perror("semctl failed!");
exit(1);
}
if (semctl(sem_read, 0, SETVAL, 0) < 0)
{
perror("semctl failed!");
exit(1);
}

if (-1 == (shm_id = shmget((key_t)1211, BUF_SIZE, 0666 | IPC_CREAT)))
{
perror("shmat failed!");
exit(1);
}
if ((void*)(-1) == (shmaddr = shmat(shm_id, NULL, 0)))
{
perror("shmat failed!");
exit(1);
}
buf = (char*)shmaddr;

while(1)
{
p(sem_read);
printf("read buf: %s\n", buf);

if (strncmp(buf, "bye", 3) == 0)
{
break;
}
else
{
v(sem_write);
}

}

if (shmdt(shmaddr) < 0)
{
perror("shmdt failed!");
exit(1);
}

return 0;
}


这里面用到2个system v信号量,用1个信号量很容易在一个进程里面循环读取缓冲区的数据,我本来是用一个信号量,再加上sleep函数切换2个进程cpu的控制权。但是睡眠时间难以把控,而且影响性能。还有一个#if 0注释掉的一段信号量删除代码,也有点问题。希望有大神指正一下。

    
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  通信