一:匿名管道(pipe)。
是一种最基本的IPC机制,由pipe函数创建。
调用pipe函数时在内核中开辟一块缓冲区用于通信。而我们把这块缓冲区叫做管道。
#include<unistd.h>
int pipe(int fd[2]);
其参数:为一个输出型的参数。其中fd[0]中保存读端口的文件描述符,fd[1]中保存写端口的文件描述符。
其返回值:成功返回0,失败返回-1。
其特征是:
1>、只能进行单向通信。
2>、只能用于具有血缘关系的进程之间的通信(常用于父子进程之间的通信)。
3>、进程的读写操作是以字节流方式来完成。(可以以任意大小读写)
4>、它是依赖于文件系统的,其生命周期随进程(随进程的结束而结束)。
5>、进程之间有默认的同步关系。
1. 验证:(eg:创建一个父子进程,假设子进程用来写数据,父进程用来读数据)(正常的读写,边读边写)
调用pipe后,fd[0]会指向管道的读端,而fd[1]会指向管道的写端。
由于子进程会复制父进程的进程地址空间,因此在fork后,会得到上图。
由于该管道是只能用于单向通信,所以需要吧其他不用端口对应的文件描述符关闭。以保证安全性。
代码实现:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fd[2]={0,0};
if(pipe(fd)==0)
{
pid_t id = fork();
if(id == 0)
{//child
close(fd[0]);//关闭读
const char* str="i am a student";
while(1)
{
write(fd[1],str,strlen(str));
sleep(1); //1s写一次
}
}
else
{//father
close(fd[1]);//关闭写
while(1)
{
char buf[1024];
int s = read(fd[0],buf,sizeof(buf));
if(s>0)
{
buf[s]=0;
printf("child -> father:%s\n",buf);
}
}
}
}
else
{
perror("pipe");
return 1;
}
return 0;
}
执行程序后,发现没间隔1s就会输出一次。
2.当父进程不关闭读端口,而每间隔500s读取一次数据时,并且子进程不断的每次以一个字节的数据不停的写:会发现,管道会被写满,子进程阻塞等待。经过测试发现其管道底层缓冲区大小为65536字节。(没有关闭读端口,但是没有读取数据,可能会将管道写满,导致写进程的阻塞)
代码实现:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fd[2]={0,0};
if(pipe(fd)==0)
{
pid_t id = fork();
if(id == 0)
{//child
close(fd[0]);
const char* str="i";
int n=0;
while(1)
{
write(fd[1],str,strlen(str));
++n;
printf("%d %d\n",n,n*strlen(str));
}
}
else
{//father
close(fd[1]);
while(1)
{
sleep(500);
char buf[1024];
int s = read(fd[0],buf,sizeof(buf));
if(s>0)
{
buf[s]=0;
printf("child -> father:%s\n",buf);
}
}
}
}
else
{
perror("pipe");
return 1;
}
return 0;
}
执行结果:
总共写入65536个字节后,子进程阻塞等待父进程读数据。
3.当子进程不停的写数据,而父进程读5次数据后,关闭读端口时。我们发现,子进程会立即收到13号信号,从而也被终止。(这么做是因为:管道本身是用来通信的,既然没有读的进程,那也没有通信,那将数据写进管道又有什么用呢?)(如果关闭所有读端口,写端口对应的进程会收到信号异常终止)
代码:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fd[2]={0,0};
if(pipe(fd)==0)
{
pid_t id = fork();
if(id == 0)
{//child
close(fd[0]);
const char* str="i am child!";
while(1)
{
write(fd[1],str,strlen(str));
sleep(1);
}
}
else
{//father
close(fd[1]);
int count=0;
while(1)
{
char buf[1024];
int s = read(fd[0],buf,sizeof(buf));
if(s>0)
{
buf[s]=0;
printf("child -> father:%s\n",buf);
}
if(++count >= 5)
{
close(fd[0]);
break;
}
}
int status;
waitpid(id,&status,0);
printf("father is quit! child quit! sig = %d exitCode = %d\n",status&0xff,(status>>8)&0xff);
}
}
else
{
perror("pipe");
return 1;
}
return 0;
}
结果:
4.当父进程不停的读数据,子进程写5条数据并且关闭写端口后,父进程在读数据的过程中,一旦读完管道中的数据,read就会返回一个0(相当于文件结尾)。并且不再读数据。(如果关闭所以写端口,读端口会一直读,直到文件尾)
代码测试:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
int fd[2]={0,0};
if(pipe(fd)==0)
{
pid_t id = fork();
if(id == 0)
{//child
close(fd[0]);
const char* str="i am child!";
int n=0;
while(1)
{
write(fd[1],str,strlen(str));
if(++n>=5)
{
close(fd[1]);
break;
}
sleep(1);
}
}
else
{//father
close(fd[1]);
int count=0;
while(1)
{
char buf[1024];
int s = read(fd[0],buf,sizeof(buf));
if(s>0)
{
buf[s]=0;
printf("child -> father:%s\n",buf);
}
else if(s == 0)
{
printf("write is quit! s=%d\n",s);
break;
}
}
int status;
waitpid(id,&status,0);
}
}
else
{
perror("pipe");
return 1;
}
return 0;
}
结果:
发现其read的返回值为0,代表已读取到文件尾(管道依赖于文件系统,因此可以将其看作为一个文件)。
eg:linux中的“|”(竖划线)就是一个匿名管道。(没有创建任何文件、并且进程之间具有血缘关系。(都属于bash的子进程))
二:命名管道(FIFO)
所谓命名管道:是一个设备文件(p)。
与匿名管道最大的区别是:会事先创建一个管道文件,来实现没有任何关系的两个进程直接的通信。
原理:创建一个管道文件,然后就可以讲问题转化为两个不同的进程对该管道文件的读写工作。
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char* pathname,mode_t mode);
参数:第一个为要创建的管道文件的名称,可以带路径,如果没有带路径,则默认为当前路径。
第二个为创建的该文件的权限大小。
eg:创建两个进程:server和client来实现这两个进程之间的通信。
代码:
server.c
#include <stdio.h> //读
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
umask(0);
if(mkfifo("./myfifo",S_IFIFO | 0666) == -1)
{
perror("fifo");
return 1;
}
else
{
int fd = open("myfifo",O_RDONLY);
if(open < 0)
{
perror("open");
return 2;
}
while(1)
{
char buf[1024];
int s = read(fd,buf,sizeof(buf));
if(s>0)
{
buf[s-1]=0;
printf("%s\n",buf);
}
else
{
perror("read");
return 3;
}
}
}
return 0;
}
cilent.c
#include <stdio.h> //写
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int fd = open("myfifo",O_WRONLY);
if(open < 0)
{
perror("open");
return 1;
}
while(1)
{
char buf[1024];
int s = read(0,buf,sizeof(buf));
if(s>0)
{
buf[s-1]=0;
write(fd,buf,sizeof(buf));
}
else
{
perror("read");
return 2;
}
}
return 0;
}
结果如下:
cilent:(输入)
server:(输出)