进程通信(IPC)的几种方式及比较
撰文:黄显国080416
难得闲暇,抽空学习了一下进程通信的知识,现将这几天的所学做一下总结,以备遗忘时参考。
进程通信的方式:
Linux
系统继承了三种系统的进程通信模式:
1、
基于system V IPC
2、
基于UNIX IPC
3、
基于POSIX IPC
同时还包含一种socket
进程间通信,不过这种是不同处理器系统之间的一种网络通信方式,不是我所关心的。
方式一:管道(PIPE
)
管道分无名管道与有名管道两种
1、
无名管道。
无名管道用于具有亲缘关系的父子进程,子子进程之间的通讯。它的实现函数有
int pipe(int fd[2]);
int pipe(int fd[2]);
//fd[2]
为描述符数组,包含一个读描述符与一个写描述符,在使用管道通信时,关闭某些不需要的读或写描述符,建立起单向的读或写管道,然后用read
和write
像操作文件一样去操作它即可。
如图便是进程1
到进程2
的一个读管道。

以下是我写的一个pipe
的验证程序,分别在父进程和父子进程里向管道写数据,然后在子进程和子子进程里读数据,当尝试改变各子进程的sleep
时间以实现渴望的同步时,会发现结果很有趣。注意创建子进程时将复制父进程的管道。
/*******************************************************************************************/ //pipe.c //frome the example, we can see: //用pipe创建的无名管道,父子进程,子子进程之间都可以通信,由于read //或write默认为阻塞,而进程与进程之间又有某种意义上的同步方法; //故而可以从下面的程序中得到一些启示。 /*****************************************************************************************/ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> int main() { int pipe_fd[2]; char buf_r[100]; char buf_rr[100]; if(pipe(pipe_fd)<0) { printf("pipe create error\n"); return -1; } else printf("pipe create success\n"); printf("pipe_fd[0]=%d,pipe_fd[1]=%d\n",pipe_fd[0],pipe_fd[1]); if(fork()==0)//子进程 { close(pipe_fd[1]);//关闭子进程的写描述符 printf("fork()"); //sleep(2); if(fork()==0)//子子进程 { if(read(pipe_fd[0],buf_r,5)>0) printf("BUF_R: in child child process,read from the pipe is %s\n",buf_r); close(pipe_fd[0]); exit(0); } Else //子进程 { if(read(pipe_fd[0],buf_rr,3)>0) printf("BUF_RR: in child parent process,read from the pipe is %s\n",buf_rr); close(pipe_fd[0]); exit(0); } } else //父进程 { close(pipe_fd[0]); sleep(5); if(write(pipe_fd[1],"Hello ",5)!=-1) printf("write1 parent pipe success\n"); if(fork()==0) //父子进程 { if(write(pipe_fd[1],"PIPE",5)!=-1) printf("write parent child pipe success"); close(pipe_fd[1]); exit(0); } close(pipe_fd[1]); exit(0); } } 2、 有名管道 有名管道可用于两个无关的进程之间的通信。它的实现函数是: int mkfifo(const char *filename, mode_t mode) //创建一个名为filename的管道,模式可选为读或写方式,阻塞或非阻塞方式等。 下面一个实例演示了mkfifo的使用。Fifo_read.c不断从管道文件里读数据,fifo_write.c往管道文件里写数据。改变sleep的值也会产生类似上面进程同步的问题,而会发现一些缓冲区的特性。 两个程序用gcc编译后在两个终端里运行。 //--------------------------------------------------------------------------------------------------- //fifo_read.c //创建有名管道,演示两个不相关的进程之间的通信 //int mkfifo(const char *filename, mode_t mode) #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #define FIFO "/home/huang/myfifo" int main(int argc,char **argv) { int fd; int nread; char buf_r[100]; if(mkfifo("/home/huang/myfifo",O_CREAT|O_EXCL)<0)//&&(errno!=EEXIST)) { perror("mkfifo:"); printf("cann't create fifoserver\n"); return -1; } printf("Preparing for reading bytes...\n"); fd=open(FIFO,O_RDONLY|O_NONBLOCK,0); if(fd==-1) { perror("open!\n"); exit(1); } while(1) { memset(buf_r,0,sizeof(buf_r)); if((nread=read(fd,buf_r,sizeof(buf_r)))==-1) { if(errno==EAGAIN) printf("no data yet\n"); } printf("read %s from FIFO\n",buf_r); sleep(1); } pause(); unlink(FIFO); } //------------------------------------------------------------------------------------------------------------------ //fifo_write.c //创建有名管道,演示两个不相关的进程之间的通信 //int mkfifo(const char *filename, mode_t mode) #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #define FIFO "/home/huang/myfifo" int main(int argc, char **argv) { int fd; char w_buf[100]; int nwrite; if(argc==1) { printf("please send some message\n"); exit(1); } fd=open(FIFO,O_WRONLY|O_NONBLOCK,0); if(fd==-1) { if(errno==ENXIO) printf("open error;no reading process\n"); perror("open:"); return -1; } memset(w_buf,'a',sizeof(w_buf)); printf("sizeof(w_buf)=%d\n",sizeof(w_buf)); while(1) { if((nwrite=write(fd,w_buf,strlen(w_buf)))==-1) { if(errno==EAGAIN) printf("The FIFO has not been write yet.\n"); perror("write"); // else // printf("error in writting!\n"); } else printf("write %s to the FIFO\n",w_buf); sleep(2); } close(fd); }