管道通信分为无名管道和有名管道。
无名管道用于父进程和子进程间的通信,有名管道可用于运行于同一系统中的任意两个进程间的通信。
管道的特点:
①半双工,数据只能向一个方向流动;需要双方通信时,要建立两个管道。
②只能用于具有亲缘关系的进程。
③单独构成一种独立的文件系统,且只存在于内存中。
④数据的读写:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,且总是从缓冲区的头部读出数据。
一、建立无名管道
1. pipe
函数的作用:建立管道
函数的原型:int pipe(int filedes[2]);
头文件:#include <unistd.h>
返回值:成功——返回0
失败——返回-1
函数的说明:pipe()会建立管道,并将文件描述词由参数filedes数组返回。filedes[0]为管道里的读取端,filedes[1]则为管道的写入端。
管道用于不同进程间通信。通常先创建一个管道,再通过fork函数创建一个子进程,该子进程会继承父进程所创建的管道。必须在系统调用fork( )前调用pipe( ),否则子进程将不会继承文件描述符。
示例1:
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int pipe_fd[2];
if(pipe(pipe_fd) < 0)
{
printf("pipe create error!\n");
return -1;
}
else
{
printf("pipe create success!\n");
}
close(pipe_fd[0]);
close(pipe_fd[1]);
}
运行结果:
pipe create success!
示例2:
/*父进程借管道将字符串"hello!\n"传给子进程并显示*/
#include <stdio.h>
#include <unistd.h>
int main()
{
int filedes[2];
char buffer[80];
pipe(filedes);
if(fork() > 0)
{
/*父进程*/
char s[] = "hello!\n";
write(filedes[1],s,sizeof(s));
}
else
{
/*子进程*/
read(filedes[0],buffer,80);
printf("%s",buffer);
}
return 0;
}
运行结果:
hello!
二、读写无名管道
管道两端可分别用描述符fd[0]和fd[1]来描述,管道的两端固定了任务,即fd[0]只能读,fd[1]只能写。
示例:
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int pipe_fd[2];
pid_t pid;
char buf_r[100];
int r_num;
memset(buf_r,0,sizeof(buf_r));
/*创建管道*/
if(pipe(pipe_fd) < 0)
{
printf("pipe creat error.\n");
return -1;
}
/*创建子进程*/
if(0 == (pid = fork())) //子进程
{
printf("\n");
close(pipe_fd[1]); //关闭写
sleep(2); //确保关闭写端
if((r_num = read(pipe_fd[0],buf_r,100)) > 0)
{
printf("%d numbers read from the pipe is %s.\n",r_num,buf_r);
}
close(pipe_fd[0]);
exit(0);
}
else if(pid > 0)
{
close(pipe_fd[0]);
if(write(pipe_fd[1],"hello",5) != -1)
{
printf("parent write1 hello!\n");
}
if(write(pipe_fd[1],"pipe",5) != -1)
{
printf("parent write2 pipe!\n");
}
close(pipe_fd[1]); //关闭写
sleep(3); //确保关闭写
waitpid(pid,NULL,0); //等待子进程结束
exit(0);
}
return 0;
}
运行结果:
parent write1 hello!
parent write2 pipe!
10 numbers read from the pipe is hellopipe.
三、创建有名管道
1. mkfifo
函数的作用:创建有名管道
函数的原型:int mkfifo(const char * pathname, mode_t mode);
参数:filename:有名管道的名称、路径
mode:打开方式
头文件:#include<sys/types.h>
#include<sys/stat.h>
mode:
O_NONBLOCK:FIFO打开的时候
O_RDONLY:只读
O_WRONLY:只写
O_RDWR:可读写
返回值:成功——返回0;
失败——返回-1
示例(有名管道的创建):
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FIFO "/tmp/2"
int main()
{
char buffer[80];
int fd;
unlink(FIFO);
mkfifo(FIFO,0666);
if(fork() > 0)
{
char s[] = "hello!\n";
fd = open(FIFO,O_WRONLY);
write(fd,s,sizeof(s));
close(fd);
}
else
{
fd = open(FIFO,O_RDONLY);
read(fd,buffer,80);
printf("%s",buffer);
close(fd);
}
return 0;
}
运行结果:
hello!
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FIFO "myfifo"
int main(int argc, char **argv)
{
char buf_r[100];
int fd;
int nread;
/*创建管道*/
if((mkfifo(FIFO, O_CREAT | O_EXCL) < 0) && (errno != EEXIST))
{
printf("cannot cret fifoserver.\n");
}
printf("Preparing for reading bytes...\n");
memset(buf_r,0,sizeof(buf_r));
/*打开管道*/
fd = open(FIFO, O_RDONLY | O_NONBLOCK, 0);
if(-1 == fd)
{
perror("open");
exit(1);
}
while(1)
{
memset(buf_r,0,sizeof(buf_r));
if(-1 == (nread = read(fd,buf_r,100)))
{
if(errno == EAGAIN)
{
printf("no data yet\n");
}
}
printf("read %s from FIFO\n",buf_r);
sleep(1);
}
puse();
unlink(FIFO);
}
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FIFO_SERVER "myfifo"
int main(int argc, char **argv)
{
char w_buf[100];
int fd;
int nwrite;
/*打开管道*/
fd = open(FIFO_SERVER, O_WRONLY | O_NONBLOCK, 0);
if(1 == argc)
{
printf("Please send something.\n");
exit(-1);
}
strcpy(w_buf,argv[1]);
if((nwrite = write(fd,w_buf,100)) == -1)
{
printf("The FIFO has not been read yet.Please try later.\n");
}
else
{
printf("write %s to the FIFO.\n",w_buf);
}
}
/*命令行参数hello*/
./write_mkfifo hello
运行结果:
write hello to the FIFO.
Preparing for reading bytes...
read from FIFO
read from FIFO
read from FIFO
read hello from FIFO
read from FIFO
read from FIFO
read from FIFO
read from FIFO
read from FIFO
read from FIFO
管道的应用场景:
1. 在shell中时常用到管道(作为输入输出的重定向)
2. 用于具有亲缘关系的进程间通信,用户自己创建管道,并完成读写操作。