进程通信(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);
}