我们将一个进程到另一个进程的数据流称为管道,它是进程间通信最古老的方式。
管道分为两种:匿名管道与命名管道。
匿名管道
顾名思义,所谓的匿名管道自然就是没有名字的管道,这种管道通常用于有亲缘关系的进程间的通信。匿名管道通常由pipe函数来创建,当一个进程创建了一个匿名管道后,它们的模型就是这样的:
可以看出,仅连接了一个进程的管道于这个进程而言是无意义的,所以通常还会在创建管道后调用fork函数创建子进程。当创建子进程后,就有了这样的模型:
当需要在两个进程间进行数据的传输时,可以在两个进程中关闭相应的读端或者写端,就可以进行通信了,从这种创建方式也可以看出匿名管道只能用于有亲缘关系的进程间的通信的原因了。
来看看匿名管道的特点:
1、仅能在有亲缘关系的进程间进行通信。
2、管道形式的进程间通信是基于字节流的。
3、管道是由进程创建的,因此当进程退出时管道也随之释放。
4、因为两个进程对同一个管道文件进行操作,因此同一时间仅有一个进程能访问管道,并且两个进程只能交替进行访问,而不能出现一个进程暂停而另一个进程无限制访问的情况,因此管道自带同步机制。
5、管道是半双工的,如果要实现两个进程的双向通信,可以创建两个管道。
如下代码利用匿名管道实现两个具有亲缘关系的进程间的通信:
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
int fds[2];
if(pipe(fds) == -1)
{
perror("pipe");
exit(EXIT_FAILURE);
}
pid_t id;
id = fork();
if(id == -1)
{
perror("fork");
exit(EXIT_FAILURE);
}
else if(id == 0)
{//child
close(fds[0]);
while(1)
{
if(write(fds[1], "Hello", 5) == -1)
exit(EXIT_FAILURE);
sleep(2);
}
close(fds[1]);
exit(EXIT_SUCCESS);
}
else
{//father
close(fds[1]);
char buf[10] = { 0 };
while(1)
{
int len = 0;
if((len = read(fds[0], buf, 10)) == -1)
exit(EXIT_FAILURE);
else if(len == 0)
{
printf("child quit");
exit(EXIT_SUCCESS);
}
else
printf("%s\n", buf);
}
close(fds[0]);
exit(EXIT_SUCCESS);
}
return 0;
}
命名管道
显然,命名管道就是有名字的管道,我们在程序中可以中函数mkfifo函数创建,匿名管道只能在具有亲缘关系的进程间通信,那么命名管道就是在无亲缘关系的进程间进行数据交流的通信方式了。
利用命名管道进行进程间通信的方式是在一个进程中创建管道文件,并对其以读或者写的方式打开,同时在另一个进程中以写或者读的方式打开这个文件,这样就建立了两个进程间的连接。
如下是利用命名管道在两个进程间进行数据传输的程序:
server.c:
#include <stdio.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#define ERR_EXIT(m) \
do{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0);
int main()
{
umask(0);
if(mkfifo("myfifo", 0644) == -1)
ERR_EXIT("mkfifo");
int rfd = open("myfifo", O_RDONLY);
if(rfd == -1)
ERR_EXIT("open");
char buf[1024] = { 0 };
int len;
while(1)
{
if((len = read(rfd, buf, 1023)) == -1)
ERR_EXIT("read");
if(len == 0)
{
printf("client quit\n");
break;
}
else
{
printf("%s", buf);
memset(buf, 0x00, 1024);
}
}
close(rfd);
return 0;
}
client.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#define ERR_EXIT(m) \
do{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0);
int main()
{
int wfd = open("myfifo", O_WRONLY);
if(wfd == -1)
ERR_EXIT("myfifo");
char buf[1024] = { 0 };
int len;
while(1)
{
if((len = read(0, buf, 1024)) == -1)
ERR_EXIT("read");
if(len == 0)
{
printf("client quit\n");
break;
}
else
{
if(write(wfd, buf, 1024) == -1)
ERR_EXIT("write");
memset(buf, 0x00, 1024);
}
}
close(wfd);
return 0;
}
Makefile:
.PHONY : all
all : server client
server : server.c
gcc server.c -o server
client : client.c
gcc client.c -o client
.PHONY : clean
clean :
rm -f server client