LINUX下进程间通信的方式主要有:管道、有名管道、消息队列、信号量、共享内存,此外还有信号和套接字。
管道只能用于亲缘进程(如父子进程)的通信。有名管道可用于非亲缘进程通信。信号量主要用于同步(线程同步与进程同步分别用两组函数操作信号量)。消息队列克服了信号量传递信息少、管道只能传送无类型字节流的弱点。信号类似于软件中断,用于事件通知。共享内存是最快的IPC方式,配合其他方式使用(一般是配合信号量)。
1、管道
管道是单向的传输方式,创立管道后系统分配一定大小的缓冲区,数据从输入管道进入缓冲区的头位置,输出管道从管道的未位置取出数据。
fd[0]为读管道,fd[1]为写管道,读前关闭写管道,写前关闭读管道。fork前必须先创建管道,否则子进程不能继承父进程的文件描述符。
void read_pipe(int fd){ char message[100]; read(fd,message,100); printf("from pipe:%s",message); } void write_pipe(int fd){ char message[]="hello pipe "; write(fd,message,sizeof(message)); } int main(int argc,char* argv[],char **environ ) { int fd[2]; if(pipe(fd)){ printf("cannot create pipe "); } pid_t id; id=fork(); //id=vfork(); switch(id){ case 0: close(fd[1]); read_pipe(fd[0]); printf("read pipe finished "); break; case -1: printf("fork failed "); break; default: close(fd[0]); sleep(5); write_pipe(fd[1]); printf("write pipe finished "); wait(); }return 0; }
使用默认的方式创建的管道,read()默认是阻塞的,也就是说,当read()时如管道没数据,则进程阻塞直到管道中有数据。
2、有名管道
有名管道提供一个路径名来访问管道,因此可以解决管道只能由亲缘进程访问的缺点。有名管道保证先进先出,又称FIFO。
3、消息队列
#include "sys/msg.h" #define BUF_SIZE 256 #define PRO_ID 32 #define PATH "." int main(int argc,char* argv[],char **environ ) { struct msgbuf{ long msgtype; char string_buf[BUF_SIZE]; }mymsgbuf; int qid; int msglen; key_t msgkey; //get key for msgqueue if((msgkey=ftok(PATH,PRO_ID))==-1){ printf("error "); } //build msg queue if((qid=msgget(msgkey,IPC_CREAT|0660))==-1){ printf("error "); } mymsgbuf.msgtype=3; strcpy(mymsgbuf.string_buf,"hello thsi is my msg hehe "); msglen=sizeof(struct msgbuf)-4; //send msg /*if(msgsnd(qid,&mymsgbuf,msglen,0)==-1){ printf("error "); }*/ //send msg if(msgrcv(qid,&mymsgbuf,msglen,3,0)==-1){ printf("error "); } printf("%s ",mymsgbuf.string_buf); return 0; }
如消息队列中没有要读的消息,则读进程阻塞直到队列中有要读消息。一旦消息被读取,队列将消除已读消息,也就是说同一消息只能读取一次。