IPC学习(1)--Pipes and FIFOS
时间:2006-03-25 来源:rwen2012
(1) Pipes
管道是最简单但也是应用最广的一种进程间通讯方式,它一般有于相互关联的进程之间,如父子进程之间。
管道一般是半双工的,即可以将一个进程的输出通过管道写向另一个进程的输入,从而将本来相互“独立”的进程组合起来完成更加复杂的事情。如,SHELL的实现。
因为管道是半双工的,若要实现进程之间的双向通讯,就要建立两个管道。
下面是父子进程间双向通讯的一个简单的例子:
figure 1.1
/*
* -------------------------------------------------------------
*/
#include <unistd.h>
#include <sys/types.h>
char childbuf[] = "I am a child\n";
char parentbuf[] = "I am the parent\n";
char cf[256];
char pf[256];
int main(void)
{
int pipefd1[2], pipefd2[2];
pid_t childpid;
int m,n;
//create two pipes
pipe(pipefd1);
pipe(pipefd2);
if ( (childpid = fork()) < 0)
printf("fork error\n");
else if (childpid == 0)
{
//close the unneed pipes
close(pipefd1[1]);
close(pipefd2[0]);
write(pipefd2[1], childbuf, sizeof(childbuf));
n = read(pipefd1[0], cf, 256);
write(0, cf, n);
_exit(0);
}
// sleep(5);
close(pipefd1[0]);
close(pipefd2[1]);
m = read(pipefd2[0], pf, 256);
write(0, pf, m);
write(pipefd1[1], parentbuf, sizeof(parentbuf));
waitpid(childpid, NULL, 0);
exit(0);
}
/*
* ------------------------ end -------------------------------
*/
(2) FIFOS:
FIFOS与管道相似,它的一个特征是,它建立时用一个路径名作为参数,这样就使得它可以通过这个路径名将不相关的进程联系起来,从而实现无关联的进程之间的通讯。
为了简单起见,现在用FIFOS重新实现上述例子:
figure 1.2
/*
* -------------------------------------------------------------
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
#define FIFO1 "fifo.1"
#define FIFO2 "fifo.2"
char childbuf[] = "I am a child\n";
char parentbuf[] = "I am the parent\n";
char cf[256];
char pf[256];
int main(void)
{
pid_t childpid;
int m,n;
int readfd, writefd;
if ( (mkfifo(FIFO1, FILE_MODE) < 0) && (errno != EEXIST))
printf("mkfifo error\n");
if ( (mkfifo(FIFO2, FILE_MODE) < 0) && (errno != EEXIST))
printf("mkfifo error\n");
if ( (childpid = fork()) < 0)
printf("fork error\n");
else if (childpid == 0)
{
readfd = open(FIFO1, O_RDONLY, 0);
writefd = open(FIFO2, O_WRONLY, 0);
if (readfd < 0 || writefd < 0)
printf("open error\n");
if ( (m = write(writefd, childbuf, sizeof(childbuf))) < 0)
printf("write error\n");
n = read(readfd, cf, 256);
write(1, cf, n);
exit(0);
}
writefd = open(FIFO1, O_WRONLY, 0);
readfd = open(FIFO2, O_RDONLY, 0);
if (readfd < 0 || writefd < 0)
printf("open error\n");
m = read(readfd, pf, 256);
write(1, pf, m);
write(writefd, parentbuf, sizeof(parentbuf));
waitpid(childpid, NULL, 0);
//close the fd
close(readfd);
close(writefd);
//delete the fifos files
unlink(FIFO1);
unlink(FIFO2);
exit(0);
}
/*
* ------------------------ end -------------------------------
*/
在FIFOS中需要特别注意的时,两个进程中管道打开的顺序,上例中,子进程为:
readfd = open(FIFO1, O_RDONLY, 0);
writefd = open(FIFO2, O_WRONLY, 0);
而在父进程中为:
writefd = open(FIFO1, O_WRONLY, 0);
readfd = open(FIFO2, O_RDONLY, 0);
若调换为:
writefd = open(FIFO1, O_WRONLY, 0);
readfd = open(FIFO2, O_RDONLY, 0);
将会导致两个进程死锁。
(3).管道之间的消息传递相当于经典的生产者-消费者问题,通过有名管道(FIFOS),也可以实现一个生产者-多个消费者之间的通讯。
实现的思想如下;
生产者进程,打开一个知名的有名管道,然后各个消费者进程各自建立自己私用的有名管道。然后消费者向生产者的知名有名管道发送自己的私用有名管道的路径名,和请求信息,生产者进程从知名管道读取信息,然后再将应答信息发到相应的私用有名管道,最后各个消费者进程从自己的私用有名管道读取自己请求所得到的信息。
上述方法是实现的是迭代服务,而不是并发的服务。
常用的并发服务器的技术有4种:
a,传统的一个客户一个子进程,父进程用于等待拉入
b,进程池,创建子进程池,当客户进入时,如果有空闲子进程,则服务之,否则让其等待
c,线程
d,线程池,类似于上面进程的情况。
(4). 前面所说的两个例子中都是通过流来通讯的,若要实现报文方式的通讯,有两个方法:
a. 在报文之间加上分隔符,这样每次读取信息时都检查是否是一个报文的结束,但开销较大。
b. 在报文内加上报文长度的消息,这样当读取信息时就可以先读报头,再根据长度信息读取所需的长度。这个实现相对比较高效。
管道是最简单但也是应用最广的一种进程间通讯方式,它一般有于相互关联的进程之间,如父子进程之间。
管道一般是半双工的,即可以将一个进程的输出通过管道写向另一个进程的输入,从而将本来相互“独立”的进程组合起来完成更加复杂的事情。如,SHELL的实现。
因为管道是半双工的,若要实现进程之间的双向通讯,就要建立两个管道。
下面是父子进程间双向通讯的一个简单的例子:
figure 1.1
/*
* -------------------------------------------------------------
*/
#include <unistd.h>
#include <sys/types.h>
char childbuf[] = "I am a child\n";
char parentbuf[] = "I am the parent\n";
char cf[256];
char pf[256];
int main(void)
{
int pipefd1[2], pipefd2[2];
pid_t childpid;
int m,n;
//create two pipes
pipe(pipefd1);
pipe(pipefd2);
if ( (childpid = fork()) < 0)
printf("fork error\n");
else if (childpid == 0)
{
//close the unneed pipes
close(pipefd1[1]);
close(pipefd2[0]);
write(pipefd2[1], childbuf, sizeof(childbuf));
n = read(pipefd1[0], cf, 256);
write(0, cf, n);
_exit(0);
}
// sleep(5);
close(pipefd1[0]);
close(pipefd2[1]);
m = read(pipefd2[0], pf, 256);
write(0, pf, m);
write(pipefd1[1], parentbuf, sizeof(parentbuf));
waitpid(childpid, NULL, 0);
exit(0);
}
/*
* ------------------------ end -------------------------------
*/
(2) FIFOS:
FIFOS与管道相似,它的一个特征是,它建立时用一个路径名作为参数,这样就使得它可以通过这个路径名将不相关的进程联系起来,从而实现无关联的进程之间的通讯。
为了简单起见,现在用FIFOS重新实现上述例子:
figure 1.2
/*
* -------------------------------------------------------------
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
#define FIFO1 "fifo.1"
#define FIFO2 "fifo.2"
char childbuf[] = "I am a child\n";
char parentbuf[] = "I am the parent\n";
char cf[256];
char pf[256];
int main(void)
{
pid_t childpid;
int m,n;
int readfd, writefd;
if ( (mkfifo(FIFO1, FILE_MODE) < 0) && (errno != EEXIST))
printf("mkfifo error\n");
if ( (mkfifo(FIFO2, FILE_MODE) < 0) && (errno != EEXIST))
printf("mkfifo error\n");
if ( (childpid = fork()) < 0)
printf("fork error\n");
else if (childpid == 0)
{
readfd = open(FIFO1, O_RDONLY, 0);
writefd = open(FIFO2, O_WRONLY, 0);
if (readfd < 0 || writefd < 0)
printf("open error\n");
if ( (m = write(writefd, childbuf, sizeof(childbuf))) < 0)
printf("write error\n");
n = read(readfd, cf, 256);
write(1, cf, n);
exit(0);
}
writefd = open(FIFO1, O_WRONLY, 0);
readfd = open(FIFO2, O_RDONLY, 0);
if (readfd < 0 || writefd < 0)
printf("open error\n");
m = read(readfd, pf, 256);
write(1, pf, m);
write(writefd, parentbuf, sizeof(parentbuf));
waitpid(childpid, NULL, 0);
//close the fd
close(readfd);
close(writefd);
//delete the fifos files
unlink(FIFO1);
unlink(FIFO2);
exit(0);
}
/*
* ------------------------ end -------------------------------
*/
在FIFOS中需要特别注意的时,两个进程中管道打开的顺序,上例中,子进程为:
readfd = open(FIFO1, O_RDONLY, 0);
writefd = open(FIFO2, O_WRONLY, 0);
而在父进程中为:
writefd = open(FIFO1, O_WRONLY, 0);
readfd = open(FIFO2, O_RDONLY, 0);
若调换为:
writefd = open(FIFO1, O_WRONLY, 0);
readfd = open(FIFO2, O_RDONLY, 0);
将会导致两个进程死锁。
(3).管道之间的消息传递相当于经典的生产者-消费者问题,通过有名管道(FIFOS),也可以实现一个生产者-多个消费者之间的通讯。
实现的思想如下;
生产者进程,打开一个知名的有名管道,然后各个消费者进程各自建立自己私用的有名管道。然后消费者向生产者的知名有名管道发送自己的私用有名管道的路径名,和请求信息,生产者进程从知名管道读取信息,然后再将应答信息发到相应的私用有名管道,最后各个消费者进程从自己的私用有名管道读取自己请求所得到的信息。
上述方法是实现的是迭代服务,而不是并发的服务。
常用的并发服务器的技术有4种:
a,传统的一个客户一个子进程,父进程用于等待拉入
b,进程池,创建子进程池,当客户进入时,如果有空闲子进程,则服务之,否则让其等待
c,线程
d,线程池,类似于上面进程的情况。
(4). 前面所说的两个例子中都是通过流来通讯的,若要实现报文方式的通讯,有两个方法:
a. 在报文之间加上分隔符,这样每次读取信息时都检查是否是一个报文的结束,但开销较大。
b. 在报文内加上报文长度的消息,这样当读取信息时就可以先读报头,再根据长度信息读取所需的长度。这个实现相对比较高效。
相关阅读 更多 +