1.知识
(1)管道的局限性
管道有两个局限性:
【1】半双功
【2】只能在具有公共祖先进程之间使用
(2)建立管道
int pipe(int filedes[2]);
filedes返回两个文件描述符,filedes[0] 为读而打开,filedes[1]为写而打开,filedes[1]的输出是filedes[0]的输入
单个进程的管道没有任何用处,但由于管道存在于内核,fork后会复制文件描述符,于是父子进程可以通过管道进行通信。
当管道一端被关闭后,下列两条规则起作用:
【1】读一个写端关闭的管道,在所有数据都被读取后,read返回0,以指示到达文件结束。
【2】写一个读端关闭的管道,会产生 SIGPIPE。SIGPIPE的默认动作是abort,如果捕捉或忽略,write返回-1,errno设置为EPIPE。
管道的缓存大小由 PIPE_BUF 决定,使用 pathconf 和 fpathconf 函数可以确定 PIPE_BUF 的值。
如果对管道 write,并且写的字节数小于 PIPE_BUF,则此操作不会与其他进程对同一管道的write操作穿插。
如果有多个进程对管道进行write,并且写的字节书大于PIPE_BUF,则写操作的数据可能穿插进行。
2.应用
(1)父子进程普通数据通信
#include <unistd.h>
#include <stdio.h>
int main()
{
int pid = -1;
int fd[2];
char buf[BUFSIZ] = {0};
int n;
pipe(fd);
if (pipe(fd) < 0)
perror("pipe");
pid = fork();
if (pid < 0) {
perror("fork");
goto __end__;
} else if (pid > 0) {
close(fd[0]);
fd[0] = -1;
write(fd[1], "hello world\n", 12);
} else {
close(fd[1]);
fd[1] = -1;
n = read(fd[0], buf, sizeof(buf));
write(STDOUT_FILENO, buf, n);
}
__end__:
if (fd[0] != -1)
close(fd[0]);
if (fd[1] != -1)
close(fd[1]);
return 0;
}
(2)与exec连用
(3)父子进程同步
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
struct pipe_sync {
int pfd1[2];
int pfd2[2];
void (*wait_child)(struct pipe_sync *);
void (*wait_parent)(struct pipe_sync *);
void (*tell_child)(struct pipe_sync *);
void (*tell_parent)(struct pipe_sync *);
void (*init_child)(struct pipe_sync *);
void (*init_parent)(struct pipe_sync *);
};
void wait_child(struct pipe_sync *);
void wait_parent(struct pipe_sync *);
void tell_child(struct pipe_sync *);
void tell_parent(struct pipe_sync *);
void init_child(struct pipe_sync *);
void init_parent(struct pipe_sync *);
struct pipe_sync *pipe_sync_new()
{
struct pipe_sync * psync = (struct pipe_sync *)malloc(sizeof(struct pipe_sync));
if (psync == NULL) {
perror("malloc");
goto __end__;
}
if (pipe(psync->pfd1) < 0) {
perror("pipe pfd1");
psync->pfd1[0] = -1;
psync->pfd1[1] = -1;
}
if (pipe(psync->pfd2) < 0) {
perror("pipe pfd2");
psync->pfd2[0] = -1;
psync->pfd2[1] = -1;
}
psync->wait_child = wait_child;
psync->wait_parent = wait_parent;
psync->tell_child = tell_child;
psync->tell_parent = tell_parent;
psync->init_child = init_child;
psync->init_parent = init_parent;
__end__:
return psync;
}
void pipe_sync_free(struct pipe_sync **psync)
{
struct pipe_sync *tmp = *psync;
if (tmp == NULL)
return;
if (tmp->pfd1[0] != -1)
close(tmp->pfd1[0]);
if (tmp->pfd1[1] != -1)
close(tmp->pfd1[1]);
if (tmp->pfd2[0] != -1)
close(tmp->pfd2[0]);
if (tmp->pfd2[1] != -1)
close(tmp->pfd2[1]);
free(tmp);
*psync = NULL;
}
int main()
{
struct pipe_sync *psync = NULL;
int pid = -1;
int ret = 0;
psync = pipe_sync_new();
if (psync == NULL) {
printf("Failed to init psync\n");
goto __end__;
}
pid = fork();
if (pid < 0) {
perror("fork");
ret = -1;
goto __end__;
}
else if (pid == 0) {
psync->init_child(psync);
printf("child wait\n");
psync->wait_parent(psync);
printf("child get ack\n");
sleep(2);
printf("child ok\n");
printf("child tell\n");
psync->tell_parent(psync);
}
else {
psync->init_parent(psync);
sleep(2);
printf("parent ok\n");
printf("parent tell\n");
psync->tell_child(psync);
psync->wait_child(psync);
printf("parent get ack\n");
}
__end__:
pipe_sync_free(&psync);
return ret;
}
void wait_child(struct pipe_sync *psync)
{
char c;
if (psync == NULL) {
printf("wait_child : psync == NULL\n");
return;
}
if (read(psync->pfd2[0], &c, 1) != 1)
printf("wait_child : read err\n");
if (c != 'c')
printf("wait_child : incorrect data\n");
}
void wait_parent(struct pipe_sync *psync)
{
char c;
if (psync == NULL) {
printf("wait_parent : psync == NULL\n");
return;
}
if (read(psync->pfd1[0], &c, 1) != 1)
printf("wait_parent : read err\n");
if (c != 'p')
printf("wait_parent : incorrent data\n");
}
void tell_child(struct pipe_sync *psync)
{
if (psync == NULL) {
printf("tell_child : psync == NULL\n");
return;
}
if (write(psync->pfd1[1], "p", 1) != 1)
printf("tell_child : write err\n");
}
void tell_parent(struct pipe_sync *psync)
{
if (psync == NULL) {
printf("tell_parent : psync == NULL\n");
return;
}
if (write(psync->pfd2[1], "c", 1) != 1)
printf("tell_parent : write err\n");
}
void init_parent(struct pipe_sync *psync)
{
if (psync == NULL) {
printf("init_parent : psync == NULL\n");
return;
}
close(psync->pfd1[0]);
close(psync->pfd2[1]);
}
void init_child(struct pipe_sync *psync)
{
if (psync == NULL) {
printf("init_child : psync == NULL\n");
return;
}
close(psync->pfd1[1]);
close(psync->pfd2[0]);
}