文章目录

  • ​​1.管道​​
  • ​​2.匿名管道​​
  • ​​3.管道示例程序​​

1.管道

  • 管道是Unix中最古老的进程间通信的形式
  • 我们把从一个进程连接到另一个进程的一个数据流称为一个管道
  • eg:ls进程将数据写入管道,wc进程从管道读取数据,达到进程间通信的目的
    连接2个进程的数据流
    本质:有固定大小的内存缓冲区
ls|wc -w
  • 管道限制
    (1)管道是半双工的,数据只能向一个方向流动;
    所以需要双方通信时,需要建立起两个管道;
    (2)只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;
    通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可以应用该管道了

2.匿名管道

  • 匿名管道pipe
包含头文件<unistd.h>
功能:创建一个无名管道

原型:
int pipe(int fd[2])

参数:
fd:文件描述符数组,其中fd[0]表示读端,fd[1]表示写端

返回值:
成功返回0,失败返回错误代码
  • 创建管道后示意图
    (1)当创建了一个管道,就相当于创建了一块内存缓冲区。用户进程获得2个文件描述符,分别对应管道的读端和写端
    (2)fork之后,子进程的fd[0]和fd[1]共享了父进程管道,也指向了管道的读端和写端
    子进程可以往管道的写端去写入数据,
    父进程可以从管道的读端获取数据

3.管道示例程序

  • eg1:P23pipe.c
#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;
}
  • 测试:
  • (P23)管道:匿名管道pipe_#include

  • 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;
}
  • 测试:
  • eg:P23cat.c
    相当于实现拷贝操作
#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不带参数表示:从标准输入获取一行数据,然后将数据输出到标准输出
  • (P23)管道:匿名管道pipe_数据_02

  • 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)