Linux进程间通信之IPC通信共享内存
一.IPC通信
1.IPC通信(Inter-Process Communication)
三种: 共享内存、消息队列、信号灯
这个IPC对象,肯定是存在于内核中。而且用户空间的文件系统中有没有IPC的文件类型?没有。
1.1 有名管道为什么能实现无亲缘关系的进程之间的通信?
是因为用户空间有管道这种文件类型。
1.2 IPC是不是只能用于亲缘关系进程之间的通信呢?肯定不是
1.3 它是怎样实现无亲缘关系之间的通信呢?也即你是保证用户空间的二个进程对内核中的同一个IPC对象的操作。(ftok)
1.4 IPC对象的打开或创建: 类似于open的函数呢?最后还要进行删除IPC对象。
二.共享内存
1.shmget
打开或创建一个共享内存对象,共享内核在内核是什么样子的?
一块缓存,变类似于用户空间的数组或malloc函数分配的空间一样
所需头文件 #include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
函数原型 int shmget(key_t key, int size, int shmflg);
函数参数 key:IPC_PRIVATE 或 ftok的返回值
size:共享内存区大小
shmflg:同open函数的权限位,也可以用8进制表示法
函数返回值
成功:共享内存段标识符—ID—文件描述符
出错:-1
查看IPC对象 ipcs –m -q -s
删除IPC对象 ipcrm -m id(id号表示要删除哪一个IPC对象)
返回值:共享内存段标识符 IPC的ID号
通过用户空间的IPCS命令就可以查看内核空间的IPC对象,
-m就是看共享内存
-q就是看消息队列
–s就是看信号灯
#include "sys/types.h"
#include "sys/shm.h"
#include "signal.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
int main()
{
int shmid;
shmid=shmget(IPC_PRIVATE,128,0777);
if(shmid <0)
{
printf("creat share memory failure\n");
return -1;
}
printf("creat share memory sucess shmid=%d\n",shmid);
system("ipcs -m");
// system("ipcrm -m shmid");
return 0;
}
2.ftok
ftok:创建key值。(可以实现无亲缘关系的进程间的通信)但是通过IPC_PRIVATE创建的,只能实现亲缘关系的通信。
char ftok(const char *path, char key )
参数:第一个参数:文件路径和文件名;
第二个参数:一个字符。字符不一样的话,那么创建key值就不一样的。
Key值不一样,IPC对象就是不一样。
返回值:正确返回一个key值,出错返回-1.
IPC_PRIVATE操作时,共享内存的key值都一样,都是0,所以使用ftok来创建key值。只要key值是一样的,用户空间的进程通过这个函数打开,则会对内核的同一个IPC对象操作。
#include "sys/types.h"
#include "sys/shm.h"
#include "signal.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
int main()
{
int shmid;
int key;
key=ftok("./a.c",'b');
if(key <0 )
{
printf("creat key fialure\n");
return -2;
}
printf("creat key sucess key=%X\n",key);
shmid=shmget(key,128,IPC_CREAT | 0777);
if(shmid <0)
{
printf("creat share memory failure\n");
return -1;
}
printf("creat share memory sucess shmid=%d\n",shmid);
system("ipcs -m");
// system("ipcrm -m shmid");
return 0;
}
3.shmat
shmat 将共享内存映射到用户空间的地址中,方便我们用户进程的读写操作
能不能用read ,write呢?
为了方便用户空间对共享内存的操作,使用地址映射的方式。
void *shmat (int shmid, const void *shmaddr, int shmflg); //同malloc函数相类似
参数:第一个参数:ID号;
第二个参数:映射到的地址,NULL为系统自动完成的映射;
第三个参数shmflg:
SHM_RDONLY共享内存只读
默认是0,表示共享内存可读写。
返回值:成功:映射后的地址;
失败:NULL。
共享内存特点:
共享内存创建之后,一直存在于内核中,直到被删除或系统关闭;
共享内存和管道不一样,读取后,内容仍在其共享内存中
#include "sys/types.h"
#include "sys/shm.h"
#include "signal.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
int main()
{
int shmid;
int key;
char *p;
key=ftok("./a.c",'b');
if(key <0 )
{
printf("creat key fialure\n");
return -2;
}
printf("creat key sucess key=%X\n",key);
shmid=shmget(key,128,IPC_CREAT | 0777);
if(shmid <0)
{
printf("creat share memory failure\n");
return -1;
}
printf("creat share memory sucess shmid=%d\n",shmid);
system("ipcs -m");
p=(char *)shmat(shmid,NULL,0);
if(p == NULL)
{
printf("shmat function failure\n");
return -3;
}
//write share memory
fgets(p,128,stdin);
//start read share memory
printf("share memory data:%s",p);
printf("second read share memory data:%s",p);
// system("ipcrm -m shmid");
return 0;
}
下面有将共享内存对象删除掉,还要将映射在用户空间的内存释放掉的。
4.shmdt:
将进程里的地址映射删除。
int shmdt(const void *shmaddr);
参数:shmaddr共享内存映射后的地址
返回值:成功:0
出错:-1
5.shmctl:
删除共享内存对象。
函数原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf);
函数参数:shmid:要操作的共享内存标识符。
cmd : IPC_STAT (获取对象属性)— 实现了命令ipcs -m
IPC_SET (设置对象属性)
IPC_RMID (删除对象) —实现了命令ipcrm -m
buf :cmd指定IPC_STAT/IPC_SET时buf用以保存/设置属性信息。
函数返回值:成功:0
出错:-1
#include "sys/types.h"
#include "sys/shm.h"
#include "signal.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
int main()
{
int shmid;
int key;
char *p;
key=ftok("./a.c",'b');
if(key <0 )
{
printf("creat key fialure\n");
return -2;
}
printf("creat key sucess key=%X\n",key);
shmid=shmget(key,128,IPC_CREAT | 0777);
if(shmid <0)
{
printf("creat share memory failure\n");
return -1;
}
printf("creat share memory sucess shmid=%d\n",shmid);
system("ipcs -m");
p=(char *)shmat(shmid,NULL,0);
if(p == NULL)
{
printf("shmat function failure\n");
return -3;
}
//write share memory
fgets(p,128,stdin);
//start read share memory
printf("share memory data:%s",p);
printf("second read share memory data:%s",p);
shmdt(p);//将进程中的地址映射删除掉,下面的memcpy就会出现段错误
// memcpy(p,"abcd",4);
shmctl(shmid,IPC_RMID,NULL);//删除共享内存单元
system("ipcs -m ");
return 0;
}
有亲缘关系进程之间的通信的实例:
1.怎样通过共享内存实现有亲缘关系进程之间的通信框架
2.当使用IPC_PRIVATE 实现有亲缘关系进程之间通信时,fork()函数一定要放在shmget()函数之后
因为如果shmget放在fork之后的话,那么父进程会在内核开辟一块共享内存空间,fork创建子进程之后,就会出现子进程会在内核开辟另一块共享内存空间。这样是无法完成两个进程间的通信的。
#include "sys/types.h"
#include "sys/shm.h"
#include "signal.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
void myfun(int signum)
{
return ;
}
int main()
{
int shmid;
int key;
char *p;
int pid;
shmid=shmget(IPC_PRIVATE,128,IPC_CREAT | 0777);
if(shmid <0)
{
printf("creat share memory failure\n");
return -1;
}
printf("creat share memory sucess shmid=%d\n",shmid);
pid=fork();
if(pid > 0)//parent process
{
signal(SIGUSR2,myfun);
p=(char *)shmat(shmid,NULL,0);
if(p == NULL)
{
printf("parent process:shmat function failure\n");
return -3;
}
while(1)
{
//write share memory
printf("parent process start write share memory:\n");
fgets(p,128,stdin);
kill(pid,SIGUSR1);// child process read data
pause();// wait child process read
}
}
if(pid == 0)//child process
{
signal(SIGUSR1,myfun);
p=(char *)shmat(shmid,NULL,0);
if(p == NULL)
{
printf("child process shmat function failure\n");
return -3;
}
while(1)
{
//只有当一个信号递达且处理方式被捕捉时,pause函数引起挂起操作的进程才会被唤醒
pause();// wait parent process write
//start read share memory
printf("share memory data:%s",p);
kill(getppid(),SIGUSR2);
}
}
shmdt(p);
shmctl(shmid,IPC_RMID,NULL);
system("ipcs -m ");
return 0;
}
接下来是通过两个共享内存实现两个无亲缘关系的两个进程间的通信。
创建key值。(可以实现无亲缘关系的进程间的通信)但是通过IPC_PRIVATE创建的,只能实现亲缘关系的通信。
server.c
#include "sys/types.h"
#include "sys/shm.h"
#include "signal.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
struct mybuf
{
int pid;
char buf[124];
};
void myfun(int signum)
{
return ;
}
int main()
{
int shmid;
int key;
struct mybuf *p;
int pid;
key=ftok("./a.c",'a');
if(key < 0)
{
printf("creat key failure\n");
return -1;
}
printf("creat key sucess\n");
shmid=shmget(key,128,IPC_CREAT | 0777);
if(shmid <0)
{
printf("creat share memory failure\n");
return -1;
}
printf("creat share memory sucess shmid=%d\n",shmid);
signal(SIGUSR1,myfun);
p=(struct mybuf *)shmat(shmid,NULL,0);
if(p == NULL)
{
printf("parent process:shmat function failure\n");
return -3;
}
//get server pid
//read share memory
pid=p->pid;
//write client pid to share memory
p->pid=getpid();
//kill signal
kill(pid,SIGUSR2);
//client start read data from share memory
while(1)
{
pause();//wait server write data to share memory;
printf("client process receve data from share memory:%s",p->buf);//read data
kill(pid,SIGUSR2);//server may write share memory
}
shmdt(p);
shmctl(shmid,IPC_RMID,NULL);
system("ipcs -m ");
return 0;
}
client.c
#include "sys/types.h"
#include "sys/shm.h"
#include "signal.h"
#include "unistd.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
struct mybuf
{
int pid;
char buf[124];
};
void myfun(int signum)
{
return ;
}
int main()
{
int shmid;
int key;
struct mybuf *p;
int pid;
key=ftok("./a.c",'a');
if(key < 0)
{
printf("creat key failure\n");
return -1;
}
printf("creat key sucess\n");
shmid=shmget(key,128,IPC_CREAT | 0777);
if(shmid <0)
{
printf("creat share memory failure\n");
return -1;
}
printf("creat share memory sucess shmid=%d\n",shmid);
signal(SIGUSR1,myfun);
p=(struct mybuf *)shmat(shmid,NULL,0);
if(p == NULL)
{
printf("parent process:shmat function failure\n");
return -3;
}
//get server pid
//read share memory
pid=p->pid;
//write client pid to share memory
p->pid=getpid();
//kill signal
kill(pid,SIGUSR2);
//client start read data from share memory
while(1)
{
pause();//wait server write data to share memory;
printf("client process receve data from share memory:%s",p->buf);//read data
kill(pid,SIGUSR2);//server may write share memory
}
shmdt(p);
shmctl(shmid,IPC_RMID,NULL);
system("ipcs -m ");
return 0;
}