管道的概念
管道是一种两个进程见进行单向通信的机制,因为管道只能对数据进行单向传递,所以管道又称为半双工管道。管道是Linux由Unix继承而来的IPC形式之一。
管道分为无名管道与命名管道。
管道的特点:
1:只能在有亲缘关系的进程之间进行通信(也就是在父子进程之间通信)
2:单向通信一个读端,一个写端,如果要双向通信就要建立两个管道
3:接收数据流,与数据格式无关
4:一般而言,进程退出,管道释放,因此管道的生命周期随进程
5:同步互斥原则,内核会对管道操作进行同步和互斥
**注意:**从管道中读数据是一次性操作,数据一但被读,他就从管道中被抛弃,释放空间以便写更多的数据。
无名管道的缺点:
1、没有名字,因此无法使用open()打开
2、只能用于亲缘进程间(如父子进程、兄弟进程、祖孙进程等)通信
3、半双工工作方式,读写端是分开的,pipefd[0]为读端,pipefd[1]为写端
4、写入操作不具有原子性,因此只能用于一对一的简单通信
5、不能用lseek()来定位
管道命令的用法:
command1|command2|command3
**注意:**管道命令只处理前一个命令的正确输出,并且管道命令的右边命令,必须能够接收标准输入流命令才行。
案例1:
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
#include<string.h>
#include<sys/wait.h>
int main()
{
int pipefd[2];
//管道读写端的描述符
//判断是否创建成功一个管道
if(pipe(pipefd) == -1)
{
perror("pipe");
exit(-1);
}
//用fork创建一个子进程
pid_t pid = fork();
if(-1 == pid)
{
perror("fork");
exit(-1);
}
if(pid == 0)
{//子进程
//关闭管道的写端
close(pipefd[1]);
char buffer[1024]={0};
//读数据
read(pipefd[0],buffer,sizeof(buffer));
printf("%s\n",buffer);
exit(0);
}
else if(pid > 0)
{//父进程
//关闭管道的读端
close(pipefd[0]);
char buffer[] = "Hello World!";
//写数据
write(pipefd[1],buffer,sizeof(buffer));
wait(NULL);
}
//关闭管道
close(pipefd[0]);
close(pipefd[1]);
return 0;
}
运行结果
案例2:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
int main()
{
int pipefd[2];
int pid;
if (pipe(pipefd) < 0)
{
return -1;
}
pid = fork();
if (pid < 0)
{
return -1;
}
else if (pid == 0)
{
//要从管道读取数据,则将管道数据写入端关闭
close(pipefd[1]);
//将管道读取端重定向到0,0本身指向标准输入重定向后指向管道
//dup2使得第二个参数的描述符,也指向第一个参数指向的文件
dup2(pipefd[0], 0);
execl("/usr/bin/wc", "wc", NULL);
}
//要写入数据,则把管道读取端关闭
close(pipefd[0]);
//将管道写入端重定向到1,1本身代表标准输出,重定向后指向管道
dup2(pipefd[1], 1);
close(pipefd[1]);
//ls结果本身输出到屏幕上,重定向后写入管道写入端
execl("/bin/ls", "ls", "-l", NULL);
close(1);
close(0);
return 0;
}
相当一ls -l | wc命令
命名管道
创建命名管道shell命令: mkfifo 名称
创建命名管道函数:
int mkfifo(const char *pathname, mode_t mode);
创建一个命名管道文件,通过管道文件的数据写入或者读取来实现进程间的通信
pathname 管道文件名称
mode 管道文件的创建权限
mkfifo仅仅是创建一个命名管道,如果要使用这个管道,那么需要open打开这个管道文件
通过命令使用命名管道:
这时打开另外一个终端
敲下回车以后
此时会在第一个终端里也就是输入cat my—namedpipe命令的终端里输出。
案例:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
void test_func()
{
printf("this is my call back function!\n");
return;
}
int main()
{
int fifo_fd;
char fifo_name[] = "./test.fifo";
char buff[1024] = { 0 };
int ret;
if (mkfifo(fifo_name, 0664) < 0) {
if (errno == EEXIST) {
unlink(fifo_name);
if (mkfifo(fifo_name, 0664) < 0) {
perror("mkfifo error");
return -1;
}
}
}
fifo_fd = open(fifo_name, O_RDONLY | O_NONBLOCK);
while (1) {
memset(buff, 0x00, 1024);
if ((ret = read(fifo_fd, buff, 1024)) <= 0) {
usleep(1000);
continue;
}
buff[ret - 1] = '\0';
if (!strcmp(buff, "function")) {
test_func();
}
printf("buf[%s]\n", buff);
}
close(fifo_fd);
return 0;
}
该代码主要功能就是,在一个终端运行该程序,它会进入阻塞状态等待管道test.fifo输入数据,如果输入的是function便调函数 test_func,如果不是便不会调用。
此时另开一个终端并向test.fifo中输入数据
由于我们输入的并不是function,所以不会调函数。
这时输入function便会调用test_fun函数。