进程间通信
首先,每个进程有各自不同的地址空间,任何一个进程都不能看到别的进程的系统资源。因此,进程间通信的主要方式是通过内核,首先在内核中开辟出一个缓冲区。把进程1的要沟通的数据写到缓冲区中去,然后通过进程2读走缓冲区的内容,这样才实现了进程间的通信(IPC)。
一、匿名管道
1.管道是一种简单的进程间通信方法,有IPC机制。通过调用pipe函数在内核中创建出缓冲区(管道)
#include < unistd.h >
int pipe(int filedes[2]);
通过输出型参数传出文件描述符(以前的文章介绍过)。其中filedes[0]指向读端,filedse[1]指向写端。
就如同打开一个文件一样。成功返回0,失败返回-1。
2.匿名管道的通信步骤:
通过父进程调用pipe创建管道,得到两个文件描述符指向管道,在fork()出子进程,子进程也有两个文件描述符共同指向管道。关闭相应的文件描述符读写端,就可以进行通讯。
#include<stdio.h>
#include<error.h>
#include<unistd.h>
#include<string.h>
int main()
{
int fd[2];
int ret=pipe(fd);
if(ret==-1)
{
perror("pipe");
return -1;
}
pid_t id=fork();
if(id<0)
{
perror("fork");
return 1;
}
else if(id==0)
{
//child
close(fd[0]);//close read
int i=0;
char *str=NULL;
while(i<10)
{
str="i am your child";
write(fd[1],str,strlen(str)+1);
sleep(1);
i++;
}
}
else{
//father
close(fd[1]);//close write
char msg[100];
int j=0;
printf("father:\n");
while(j<10)
{
memset(msg,'\0',sizeof(msg));
read(fd[0],msg,sizeof(msg));
printf("%s \n",msg);
j++;
}
}
return 0;
}
运行结果:
父进程打印出消息。如果要进行父进程写入,子进程读只需要关闭子进程的写端,父进程的读端。
3.匿名管道的特点:
①.这种管道通信只适用于单向通信。
②.常常用于父子间的通信,而且一定要具有血缘关系,比如父子进程,爷孙进程,兄弟进程等等。
③.管道的生命周期,随着进程结束而结束。
④.管道通信,面向字节流的通信。
⑤.管道通信自带同步机制。
4.匿名管道的几种特殊情况:
①.写端写满后停滞,等待读端读。
②.写端一直不写,读端处于阻塞状态等待。
③.读端关闭,写端会被操作系统终止,(收到来自进程的SIGPIPE)。
④.写端停止,等待读端读取,最后关闭,返回0。
二、命名管道(FIFO)
匿名管道的一个不足之处在于它没有名称。因此诞生了命名管道,命名管道某种程度上可以看做是匿名管道,但是他可以进行非血缘关系进程间的通信。之所以能够这样,是因为它可以通过同一个路径名而看到同一份资源,这份资源以FIFO的文件形式存在于文件系统中。
管道的创建:
#include< sys/types.h >
#include< sys/stat.h >
int mkfifo(const char * filename ,mode_t mod);
int mkfifo(const char * filename ,mode_t mod | S _IFIFO,(dev_t)0);
两个函数都是创建文件。
filename是文件名,mod是操作权限。因为创建文件在系统中,读写操作都是对文件的操作,对于匿名管道那种不可见的操作,这种做法更突出可见性。因为命名管道是一个文件,可以用于任何两个进程的通信。