一、无名管道
无名管道用于有亲缘关系的进程间的通信,管道字如其名,它就像在两个进程之间铺设了一条管道,进程通过管道进行数据交互。无名管道是没有名字的,它由pipe或者pipe2函数创建,与之对应的是有名管道。
1、特点:
(1)创建之后再文件系统中不可见
(2)以半双工的方式进行通信
半双工(Half Duplex)数据传输指数据可以在一个信号载体的两 个方向上传输,但是不能同时传输。
(3)拥有固定的读端(0)和写端(1)
(4)只能用于具有亲缘关系的进程间通信
2、无名管道的创建–pipe
#include <unistd.h>//头文件
int pipe(int pipefd[2]);
参数:
pipefd:存放无名管道读端和写端的数组地址
pipefd[0]--读端
pipefd[1]--写端
返回值:
成功返回0,失败返回-1;
3、无名管道的读写性
(1)读–特性:
<1>写端存在:
管道有数据:返回读到的字节数
管道无数据:阻塞
<2>写端不存在:
管道有数据:返回读到的字节数
管道无数据:返回0
(2)写–特性:
<1>读端存在:
管道有空间:返回写入的字节数
管道无空间:阻塞,直到有空间为止
<2>读端不存在:
无论管道是否有空间,管道破裂
(3)无名管道的大小
我们可以通过一个while循环让它不断写入,当满了时它就会停止写入,这时也就是它的大小。
实例:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int pfd[2]={0};//定义无名管道数组,只有两个端口,所以大小为2
int ret=pipe(pfd);//pipe--创建无名管道
char buf[1]={0};//缓冲区大小,每次写入一个
int size,count=0;
while(1)
{
size=write(pfd[1],buf,1);
if(size<0)//write写入不成功就退出,并输出错误原因
{
perror("write");
exit(-1);
}
count=count+size;//循环直到写满
printf("size=%d\n",count);//输出空间大小
}
return 0;
}
可以看到,当size=65536时,就不动了,说明管道写满了,大小为65536,当然,可能不同系统大小有所差异。
(4)管道的破裂
为便于观察,我们先了解几个判断进程终止和异常的函数
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *wstatus);
参数:
wstatus:进程结束时,状态信息的首地址
返回值:
成功返回子进程结束的pid号,失败返回-1;
如果想得到进程结束的状态信息,可以用以下宏来的到:
WIFEXITED(wstatus)--判断一个子进程是否正常退出,正常退出为真(1),非正常退出为假(非正数)
WEXITSTATUS(wstatus)--返回子进程结束的返回值
WIFSIGNALED(wstatus)--判断是否被信号终止
WTERMSIG(wstatus)--打印进程信号的编号
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int pfd[2]={0};//管道的两个端口
int ret=pipe(pfd);//创建管道
if(ret<0)
{
perror("pipe");
exit(-1);
}
close(pfd[0]);//关闭读端
pid_t pid=fork();//创建自进程
if(pid<0)
{
perror("fork");
exit(-1);
}
if(pid==0)
{
write(pfd[1],"ccvv",4);//向管道写入
sleep(3);
}
else
{
int status;//定义状态信息首地址
wait(&status);
printf("进程编号:%d\n是否正常退出%d\n信号是否被终止: %d\n",WTERMSIG(status),WIFEXITED(status),WIFSIGNALED(status));
exit(0);//结束进程
}
return 0;
}
运行结果:
可以看到WIFEXITED(status)的结果是0,说明为非正常退出,而程序也正常运行,所有就是管道破裂了。
二、有名管道
特点:
有名管道的创建之后会在文件系统中以管道文件的形式存在;
有名管道可以用于任意两个进程间的通信,没有固定的读端和写端。
1、有名管道的创建–mkfifo
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
参数:
pathname:创建管道文件的文件名
mode:管道文件的权限
返回值:
成功返回0,失败返回-1
2、实例
创建一个有名管道,一个进程向管道中输入数据,另一个进程输出数据。
我们首先要创建一个fifo管道:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, char *argv[])
{
int ret=mkfifo("fifo",0664);//0664赋文件权限
//0664--> rw- rw- -w-,每一位依次对应<用户>、<用户组><其他>的权限,每个权限又对应读(r)写(w)执行(x)
if(ret<0)
{
perror("mkfifo");//ret<0,就输出创建错误原因
exit(-1);
}
return 0;
}
创建之后可以看到,多了一个颜色与其他不同的文件
然后写入fifo:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd=open("fifo",O_WRONLY);//以‘仅写’打开管道
if(fd<0)
{
perror("open fifo");
exit(-1);
}
printf("open fifo success\n");
char buf[64]={0};//定义缓冲区大小
while(1)
{
fgets(buf,64,stdin);//获取输入的字符串
write(fd,buf,strlen(buf));//写入
}
close(fd);
return 0;
}
读取fifo:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd=open("fifo",O_RDONLY);//以‘仅读’打开管道
if(fd<0)
{
perror("open fifo");
exit(-1);
}
printf("open fifo success\n");
char buf[64]={0};
while(1)
{
read(fd,buf,64);//读取缓冲区,注:这里的64不能写成strlen(buf),因为两个buf不一样,会读不进去
fputs(buf,stdout);//输出
}
close(fd);
return 0;
}
运行结果演示:
只运行write_fifo:
只运行read_fifo:
运行write_fifo 并同时运行read_fifo(两个终端):
可以发现,从write_fifo里输入的值,read_fifo那里也输出了,说明fifo管道将他们连通了,可以传输数据。