(P23)管道:匿名管道pipe
原创
©著作权归作者所有:来自51CTO博客作者喜欢打篮球的普通人的原创作品,请联系作者获取转载授权,否则将追究法律责任
文章目录
- 1.管道
- 2.匿名管道
- 3.管道示例程序
1.管道
- 管道是Unix中最古老的进程间通信的形式
- 我们把从一个进程连接到另一个进程的一个数据流称为一个管道
- eg:ls进程将数据写入管道,wc进程从管道读取数据,达到进程间通信的目的
连接2个进程的数据流
本质:有固定大小的内存缓冲区
- 管道限制
(1)管道是半双工的,数据只能向一个方向流动;
所以需要双方通信时,需要建立起两个管道;
(2)只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;
通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可以应用该管道了
2.匿名管道
包含头文件<unistd.h>
功能:创建一个无名管道
原型:
int pipe(int fd[2])
参数:
fd:文件描述符数组,其中fd[0]表示读端,fd[1]表示写端
返回值:
成功返回0,失败返回错误代码
- 创建管道后示意图
(1)当创建了一个管道,就相当于创建了一块内存缓冲区。用户进程获得2个文件描述符,分别对应管道的读端和写端
(2)fork之后,子进程的fd[0]和fd[1]共享了父进程管道,也指向了管道的读端和写端
子进程可以往管道的写端去写入数据,
父进程可以从管道的读端获取数据
3.管道示例程序
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
int main(int argc, char *argv[])
{
int pipefd[2];
if (pipe(pipefd) == -1)
ERR_EXIT("pipe error");
pid_t pid;
pid = fork();
if (pid == -1)
ERR_EXIT("fork error");
if (pid == 0)
{
close(pipefd[0];
write(pipefd[1], "hello", 5);
close(pipefd[1]);
ERR_EXIT(EXIT_SUCCESS);
}
close(pipefd[1]);
char buf[10] = {0};
read(pipefd[0], buf, 10);
close(pipefd[1]);
printf("buf = %s\n", buf);
return 0;
}
- 测试:
- eg2:P23pipe1.c,模拟ls | wc -w的实现
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
int main(int argc, char *argv[])
{
int pipefd[2];
if (pipe(pipefd) == -1)
ERR_EXIT("pipe error");
pid_t pid;
pid = fork();
if (pid == -1)
ERR_EXIT("fork error");
if (pid == 0)
{
//复制管道的写端,意味着1指向管道的写端
dup2(pipefd[1], STDIN_FILENO);
close(pipefd[1]);//因为0(标准输入)已经指向了管道的写端,所以可以close(pipefd[1])
close(pipefd[0]);//读端没啥用了,关闭即可
execlp("ls", "ls", NULL);
fprintf(stderr, "error execute ls\n");
ERR_EXIT(EXIT_SUCCESS);
}
//复制管道的都端,意味着0指向管道的读端
//1是标准输出
dup2(pipefd[0], STDOUT_FILENO);
close(pipefd[0]);
close(pipefd[1]);
execlp("wc", "wc", "-w", NULL);
fprintf(stderr, "error execute ls\n");
ERR_EXIT(EXIT_SUCCESS);
return 0;
}
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0)
int main(int argc, char *argv[])
{
//将0和标准输入断开,重新指向了Makefile文件
//也就是,标准输入指向了Makefile
close(0);
open("Makefile", O_RDONLY);
//同理。1指向了Makefile2
close(1);
open("Makefile2", O_WRONLY | O_CREAT | O_TRUNC, 0644);
//不带参数的cat表示:从标准输入获取命令,写入到标准输出
//从Makefile获取数据,写入到Makefile2中
execlp("cat", "cat", NULL);
return 0;
}
- 测试:
cat不带参数表示:从标准输入获取一行数据,然后将数据输出到标准输出 - Makefile
.PHONY:clean all
CC=gcc
CFLAGS=-Wall -g
BIN=01sigqueue_sen \
04sigaction_recv
all:$(BIN)
%.o:%.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f *.o $(BIN)