无名管道
管道是Linux一种通信机制,是在进程之间建立的一种逻辑上的通道,一端为流入段一端为流出段,一个进程在流入端写入数据,其他进程在流出段按照流入顺序读出数据。
管道的特点:
-
单工单向通信,管道的数据只能向一个方向传送,发送接收不能同时进行,一旦确定 数据传输方向就不能更改,要实现双向通信需要创建两个管道。
-
以字节流形式传送,以字节为单位按照流入顺序传递数据(FIFO)
无名管道与有名管道的区别:
- 无名管道只能在父子进程间通信,有名管道可以在任意进程间通信
- 无名管道没有名字标识,有名管道有名称
无名管道的创建
#include <unistd.h>
int pipe(int pipe[2])
若成功,返回0;若出错,返回-1。 pipe[0]用于读管道,pipe[1]用于写管道。
例:利用无名管道实现字符串倒序:父进程通过管道向子进程发送字符串,子进程将其倒序发送给父进程。
// 无名管道
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<fcntl.h>
char sendbuf[]="taylor swift";
char recbuf[20];
char parrecbuf[20];
void reverse(char *str)//字符串倒序
{
if(str==NULL)
return;
char *p=str;
char *q=str;
while(*q)
++q;
q--;
while(q>p)
{
char t=*p;
*p++=*q;
*q--=t;
}
}
int main()
{
int mypipe[2],fd;
if(pipe(mypipe)<0)
{
perror("pipe failed");
exit(0);
}
if((fd=fork())<0)
{
perror("fock failed");
exit(0);
}
if(fd==0){
read(mypipe[0],recbuf,strlen(sendbuf));
printf("子进程字符串:%s\n",recbuf);
reverse(recbuf);
write(mypipe[1],recbuf,strlen(recbuf)); //向管道写入倒序后字符串
}
if(fd>0){
write(mypipe[1],sendbuf,strlen(sendbuf));
sleep(3); // 等待子进程
read(mypipe[0],parrecbuf,strlen(sendbuf));
printf("父进程字符串:%s\n",parrecbuf);
wait();
}
}
管道的重定向
无名管道可以用来实现进程的输入输出重定向,将某个进程的标准输出与另一个进程的标准输出相连接。实现该功能需要使用dup系统调用
#include<unistd.h>
newfd=dup(oldfd);
newfd=dup2(oldfd,newfd);
dup(oldfd)表示复制文件描述符old,返回值为新的文件描述符newfd。 dup2(oldfd,newfd)相当于" close(newfd);dup(oldfd)",将newfd文件描述符关闭,再将old描述符复制到newfd描述符。
Linux系统将所有设备都当作文件来处理,标准输入的Linux文件描述符是 0,标准输出是 1,标准错误是 2。程序刚刚启动的时候,0是标准输入,1是标准输出,2是标准错误。如果此时去打开一个新的文件,它的文件描述符会是3,依次递增。因此可以利用管道实现标准输入输出的重定向。
例:利用管道实现类似ps -aux|grep init。
源程序:
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
void main(){
int pid,mypipe[2];
pipe(mypipe);
pid=fork();
if(pid<0){
perror("create process failed\n");
exit(0);
}
if(pid==0){
close(mypipe[1]);
dup2(mypipe[0],0);
close(mypipe[0]);
sleep(1);
execlp("grep","grep","init",NULL);
}
else{
close(mypipe[0]);
dup2(mypipe[1],1);
close(mypipe[1]);
execlp("ps","ps","aux",NULL);
}
}
运行结果: