什么是进程间通信?
- 进程间通信(简称IPC,是指在不同进程之间传播或交换信息
进程间通信有哪些方式?
- IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享内存、Socket、Streams等。其中Socket和Streams支持不同主机上两个进程IPC
管道(通常指无名管道,是UNIX系统IPC最古老的形式)
-
特点
-
- 头文件
#include <unistd.h>
- 它是半双工的,具有固定的读端和写端
- 他只能用于具有“亲缘关系”的进程间的通信(父子或兄弟进程之间)
- 它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存当中
- 当管道中的某个数据被读走后管道就没有此数据了
- 头文件
-
原型:int pipe(int fd[2]);
- 返回值:0=成功,-1=失败
- 参数:fd[0]从管道读数据,fd[1]往管道写数据
- 关闭管道:close(fd[0]),close(fd[1])
-
用法:写的时候关闭读,读的时候关闭写
- 读:close(fd[1]); pipe(fd[0])
- 写:close(fd[0]); pipe(fd[1])
#include <stdio.h> #include <unistd.h> #include <string.h> int main () { //无名管道,一旦确定父进程为读程序运行起来后就不能将父进程改为写 int fd[2]; int status=0; int num; char *writebuf="Hellow World!"; char readbuf[100]={0}; if(pipe(fd) == -1){//先开辟管道再fork printf("creat pipe faild!\n"); } num = fork(); if(num < -1){ printf("making course file\n"); } else if(num == 0){ close(fd[0]); write(fd[1],writebuf,strlen(writebuf)); } else if(num > 0){ close(fd[1]); read(fd[0],readbuf,strlen(writebuf)); printf("read:%s\n",readbuf); } else{ printf("Nothing doing\n"); } return 0; }
FIFO(称为命名管道,它是一种文件类型)
-
特点
- FIFO可以在无关的进程之间交换数据,与无名管道不同
- FIFO有路径与之相关联,它是一种特殊设备形式存在于文件系统中
-
原型:int mkfifo(const char *pathname, mode_t mode);
- 返回值:0=成功,-1=失败(失败原因返回erron中)
- mode和open函数中的mode相同,一旦创建了FIFO,就可以用一般文件I/O函数操作它。
-
使用案例
- read
#include <stdio.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <string.h> #include <fcntl.h> //int mkfifo(const char *pathname, mode_t mode); int main() { char readbuf[50]={0}; if(mkfifo("./File",0600) == -1 && (errno == EEXIST)){ perror("Why"); } int fp = open("./File",O_RDONLY); for(int i=0;i<5;i++){ read(fp,readbuf,50); printf("%s\n",readbuf); } close(fp); return 0; }
- write
#include <stdio.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> #include <string.h> #include <fcntl.h> #include <unistd.h> //int mkfifo(const char *pathname, mode_t mode); int main() { int fp = open("./File",O_WRONLY);//只写权限 char *buf="Hello world!"; for(int i=0;i<5;i++){ sleep(1); write(fp,buf,strlen(buf)); } close(fp); return 0; }
- 补充
- 没有指定O_NONBLOCK(默认未指定),只读open要阻塞到某个其他进程为写而打开FIFO(就像是需要有进程对FIFO进行写,才能使得读FIFO为非阻塞)
- 指定O_NONBLOCK,只读open立即返回。而只写open将出现错误返回-1如果没有进程已经为读而打开该FIFO,其errno置ENXIO(这种一般不用)
消息队列
-
消息队列,是消息的链接表。一个消息队列由一个标识符(队列ID)来标识
-
特点
- 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级
- 消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会删除
- 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取
-
函数原型
-
-
int msgget(key_t key,int flag)
参数 说明 key 两种情况创建一个新队列:1.如果没有与键值key相对应的消息队列,并且flag中包含了IPC_CREAT标志位 2.key参数为IPC_PRIVATE flag 打开队列的方式(1.只使用IPC_CREAT:如果消息队列不存在则创建 2.IPC_CREAT和IPC_EXCL两个同时使用,如果消息队列不存在则创建之,如果存在则出错返回)。IPC_EXCL不单独使用 返回值 成功返回队列ID,失败返回-1 -
int msgsend(int msqid,const void *prt,size_t size,int flag)
参数 说明 msqid 队列ID *prt 发送的消息 size 消息的大小 flag 默认为0 返回值 成功返回0,失败返回-1 -
int msgrcv(int msqid,const void *prt,size_t size,long type ,int flag)
参数 说明 msqid 队列ID *prt 接收的消息 size 消息的大小 type 消息类型(写与读要对应上) flag 默认为0 返回值 成功返回0,失败返回-1
-
-
使用案例(先运行read(会阻塞),再运行write)
- write
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <string.h> //int msgget(key_t key, int msgflg); //int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); //ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg); struct msgbuf { long mtype; /* message type, must be > 0 */ char mtext[1024]; /* message data */ }; int main() { key_t key; key = ftok(".",'z');//第一个"."参数是表示当前目录,'z'表示一个整数(1-255) struct msgbuf sendbuf = {888,"Hello world"}; int ID = msgget(key,IPC_CREAT|0777); if(ID == -1){ printf("Making queue Fiald\n"); } msgsnd(ID,&sendbuf,strlen(sendbuf.mtext),0); if(msgctl(ID,IPC_RMID,NULL) == 0){ printf("Delet queue success\n"); } return 0; }
- read
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> //int msgget(key_t key, int msgflg); //int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); //ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg); struct msgbuf { long mtype; /* message type, must be > 0 */ char mtext[1024]; /* message data */ }; int main() { struct msgbuf readbuf; key_t key; key = ftok(".",'z');//第一个"."参数是表示当前目录,'z'表示一个整数(1-255) printf("key=%d\n",key); int ID = msgget(key,IPC_CREAT|0777); if(ID == -1){ printf("Making queue Fiald\n"); } msgrcv(ID,&readbuf,sizeof(readbuf.mtext),888,0); printf("%s\n",readbuf.mtext); if(msgctl(ID,IPC_RMID,NULL) == 0){ printf("Delet queue success\n"); } return 0; }
共享内存
-
特点
- 共享内存是最快的一种IPC,因为进程是直接对内存进行存取
- 因为多个进程可以同时操作,所以需要进行同步
- 信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问
-
原型
- int shmget(key_t key ,szie_t size, int flag);作用:创建共享内存
参数 说明 key 键值,可以由ftok()获得 size 共享内存大小(以M进行对齐) flag 1.使用IPC_CREAT|权限:如果不存在key值的共享存储空间,且权限不为0,则创建共享存储空间并返回一个共享存储标识符。如果存在,则直接返回共享存储标识符。2.IPC_CREAT和IPC_EXCL两个同时使用:如果不存在key值的共享存储空间,且权限不为0,则创建共享存储空间,并返回一个共享存储标识符。如果存在,则产生错误。IPC_EXCL不单独使用 返回值 成功:返回共享内存ID 失败:返回-1 - void *shmat(int shm_id,const void *addr,int flag);作用:挂接共享内存
参数 说明 shm_id 由shmget()返回的ID void *addr 一般写0,让内核自己决定一个合适的地址位置 flag 一般写0,如果指定为SHM_RDONLY则以只读方式连接此段,其他值为读写方式连接此段。 返回值 成功:返回指向共享内存的地址;失败:返回-1 - int shmdt(void *addr)作用:断开与共享内存的连接
参数 说明 void *addr 由shmat()返回的地址 返回值 成功返回0,失败返回-1 - int shmctl(int shm_id,int cmd,struct shmid_ds *buf)作用:销毁共享内存
参数 说明 shm_id shmget()返回的ID cmd 设置为IPC_RMID时表示可以删除共享内存 *buf 一般为0 返回值 成功返回0,失败返回-1 -
补充
-
key_t ftok(const char *pathname, int proj_id);
-
key的值由pathname和proj_id共同决定,如果pathname和proj_id的值相同,就算在不同的程序执行所得到的key值也是一样的
-
ftok.c程序中:ftok(“/home/Desktop”,1);
-
sharer.c程序中:ftok(“/home/Desktop”,1);
-
执行所得到的key值相同
-
pathname可以指定到目录也可以指定到特定的文件,但是路径一定要正确
-
proj_id的取值为1-255
-
-
key值相同时由shmget返回的ID值也相同
- 但是由shmget创建共享内存时,当key相同下只能创建一种内存大小的内存,如果再创建相同key值下不同内存大小的共享内存,会出错
-
查看系统中有哪些共享内存:ipcs -m
-
删除共享内存:ipcrm -m ID
-
-
使用案例(先写再读)
- write
#include<stdio.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/types.h> #include <sys/ipc.h> #include <string.h> int main() { int ID; char *maddr=NULL; key_t key; key = ftok(".",1); ID = shmget(key,1024*2,IPC_CREAT|0666); if(ID == -1){ printf("faild\n"); } else{ maddr = shmat(ID,0,0); strcpy(maddr,"123"); printf("ID = %d\n" ,ID); printf("key = %d\n",key); } return 0; }
- read
#include<stdio.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/types.h> #include <sys/ipc.h> #include <string.h> int main() { int ID; char *maddr=NULL; key_t key; key = ftok(".",1); ID = shmget(key,1024*2,0); if(ID == -1){ perror("Faild"); } else{ maddr = shmat(ID,0,0); printf("read:%s\n",maddr); printf("ID = %d\n",key); } shmdt(maddr); shmctl(ID,IPC_RMID,0); return 0; }