pipe管道
实现原理: 内核借助环形队列机制,使用内核缓冲区实现。
特点: 1. 伪文件;2. 管道中的数据只能一次读取;3. 数据在管道中,只能单向流动。
局限性:1. 自己写,不能自己读;2. 数据不可以反复读;3. 半双工通信;4. 血缘关系进程间可用。
管道的读写行为:
读管道:1. 管道有数据,read返回实际读到的字节数。
2. 管道无数据: 1)无写端,read返回0 (类似读到文件尾);2)有写端,read阻塞等待。
写管道:1. 无读端, 异常终止。 (SIGPIPE导致的)。
3. 有读端: 1) 管道已满, 阻塞等待;2) 管道未满, 返回写出的字节个数。
管道的优劣:
优点:简单,相比信号,套接字实现进程通信,简单很多。
缺点:1.只能单向通信,双向通信需建立两个管道;2.只能用于有血缘关系的进程间通信。该问题后来使用fifo命名管道解决。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{
int fd[2] = { 0 };
pipe(fd);
pid_t pid = fork();
if (pid == 0)
{
sleep(3);
close(fd[0]); //关闭读端
write(fd[1], "hello", 5);
close(fd[1]); //关闭写端
while (1)
{
sleep(1);
}
}
else if(pid > 0)
{
close(fd[1]); //关闭写端
close(fd[0]);
int status = 0;
waitpid(pid, &status, 0);
if(WIFSIGNALED(status))
{
printf("killed by %d\n", WTERMSIG(status));
}
char buf[1024] = { 0 };
while (1)
{
int ret = read(fd[0], buf, sizeof(buf));
if (ret == 0)
{
printf("read over!\n");
break;
}
else if (ret > 0)
{
write(STDOUT_FILENO, buf, ret);
}
}
}
return 0;
}
利用管道实现ps -ef | grep bash命令:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
int fd[2] = { 0 };
pipe(fd);
pid_t p1, p2, ret1, ret2;
p1 = fork();
if (p1 < 0)
{
printf("fork child 1 error!\n");
return -1;
}
else if (p1 == 0)
{
//child 1
//关闭读端
close(fd[0]);
//标准输出重定向到写端
dup2(fd[1], STDOUT_FILENO);
//执行ps -ef命令
execlp("ps", "ps", "-ef", NULL);
}
else
{
//parent
p2 = fork();
if (p2 < 0)
{
printf("fork child2 error!\n");
return -1;
}
else if (p2 == 0)
{
//child 2
//关闭写端
close(fd[1]);
//标准输入重定向写端
dup2(fd[0], STDIN_FILENO);
//执行grep bash命令
execlp("grep", "grep", "bash", NULL);
}
else
{
//parent
close(fd[0]);
close(fd[1]);
//回收子进程
int status = 0;
do
{
ret1 = waitpid(p1, &status, 0);
ret2 = waitpid(p2, &status, 0);
} while (ret1 != p1 || ret2 != p2);
printf("p1 = %d, ret1 = %d, p2 = %d, ret2 = %d\n", p1, ret1, p2, ret2);
printf("child all die!\n");
}
}
return 0;
}
利用管道实现ls -l | wc -l命令:
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
int main(int argc, char const *argv[])
{
pid_t pid;
int status = 0;
int i = 0;
//建立管道,在fork之前,父子进程可以共享文件描述符
int fd[2];
int ret = pipe(fd);
if (ret == -1)
{
cout << "pipe error!" << endl;
exit(1);
}
for (; i < 2; ++i)
{
pid = fork();
if (pid < 0)
{
cout << "fork error" << endl;
exit(1);
}
else if (pid == 0)
{
break;
}
}
if (i == 0)
{
//子进程1写ls -l命令
close(fd[0]); //关闭管道的读端
dup2(fd[1], STDOUT_FILENO); //重定向stdout
execlp("ls", "ls", "-l", nullptr);
cout << "execlp error!" << endl;
exit(1);
}
else if (i == 1)
{
//子进程2读ls -l命令,写wc -l
close(fd[1]); //关闭管道的写端
dup2(fd[0], STDIN_FILENO); //重定向stdin
execlp("wc", "wc", "-l", nullptr);
cout << "execlp error!" << endl;
exit(1);
}
else if (i == 2)
{
//关闭管道读写端
close(fd[0]);
close(fd[1]);
//父进程回收子进程
wait(nullptr);
wait(nullptr);
}
return 0;
}
一个读端多个写端:
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
using namespace std;
int main(int argc, char const *argv[])
{
pid_t pid;
int status = 0;
int i = 0;
//建立管道
int fd[2];
int ret = pipe(fd);
if (ret == -1)
{
cout << "pipe error!" << endl;
exit(1);
}
for (; i < 2; ++i)
{
pid = fork();
if (pid < 0)
{
cout << "fork error" << endl;
exit(1);
}
else if (pid == 0)
{
break;
}
}
if (i == 0)
{
//子进程1写
close(fd[0]); //关闭管道的读端
write(fd[1], "1.hello\n", strlen("1.hello\n"));
close(fd[1]);
}
else if (i == 1)
{
//子进程2写
close(fd[0]); //关闭管道的读端
write(fd[1], "2.HEELO", strlen("2.HEELO"));
close(fd[1]);
}
else if (i == 2)
{
//关闭管道写端
close(fd[1]);
//父进程回收子进程
wait(nullptr);
wait(nullptr);
char buf[1024];
read(fd[0], buf, sizeof(buf));
write(STDOUT_FILENO, buf, strlen(buf));
close(fd[0]);
}
return 0;
}
fifo管道
实现无血缘关系的进程间通信。fifo操作起来像文件。
1.创建fifo
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
int main(int argc, char const *argv[])
{
int ret = mkfifo(argv[1], 0644);
if (ret == -1)
{
perror("mkfifo error!");
exit(1);
}
return 0;
}
2.一个或多个进程写fifo
//file write
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{
if (argc != 2)
{
printf("./a.out fifoname\n");
return -1;
}
//fifo文件
//打开fifo文件
printf("begin write...\n");
int fd = open(argv[1], O_WRONLY);
printf("end write...\n");
//写
char buf[256] = { 0 };
int num = 1;
while (1)
{
sprintf(buf, "N%04d", num++);
write(fd, buf, strlen(buf));
sleep(1);
}
//关闭描述符
close(fd);
return 0;
}
3.一个进程读fifo
//fifo read
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char const *argv[])
{
if (argc != 2)
{
printf("./a.out fifoname\n");
return -1;
}
printf("begin read...\n");
int fd = open(argv[1], O_RDONLY);
printf("end read...\n");
char buf[256] = { 0 };
int num = 1;
int ret = -1;
while(1)
{
ret = read(fd, buf, sizeof(buf));
if (ret > 0)
{
printf("read: %s\n", buf);
}
// sleep(1);
}
close(fd);
return 0;
}