1、匿名管道
(1)确定通信方向:
1)父写子读,关闭父读,关闭子写
2)子写父读,关闭子读,关闭父写
(2)好处:确定,经典
(3)注意事项:
1)写端关闭,读端仍然在读,则读完管道中的内容后再次读时,读到EOF,返回0。
2)写端未写完数据,写端暂无数据但未关闭,则读端将数据读完,再次去读时,发生阻塞。
3)读端关闭,写端写数据到管道,进程产生SIGPIPE信号,默认写进程会终止进程
4)写端数据写满了,读端未将数据读完,再次写时,发生阻塞
//程序1 匿名管道
#include <stdio.h>
#include <sys/time.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(void)
{
int fd[2];//定义文件描述符
pid_t pid;//定义进程
char str[1024]="hello,myworld";
char buf[1024];
//开辟管道
if(pipe(fd)<0)
{
perror("pipe");
exit(1);
}
pid=fork();//创建父进程和子进程
//确定方向,父写子读
if(pid>0) //父进程,关闭父读
{
close(fd[0]);
write(fd[1],str,strlen(str));//写数据到文件
wait(NULL);
}
else if(pid==0)//子进程,关闭子写
{
int len;//读取到的数据字节数
close(fd[1]);
memset(buf,0,sizeof(buf));//清空缓存数组
len=read(fd[0],buf,sizeof(buf));//从写端读取数据
write(STDOUT_FILENO,buf,len);//将数据显示到终端
printf("\n");//读取完毕
}
else //创建进程失败
{
perror("fork\n");
exit(-1);
}
}
程序运行效果:
//程序2 匿名管道-非阻塞读管道文件
#include <stdio.h>
#include <sys/time.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
int main(void)
{
int fd[2];
pid_t pid;
char str[1024]="hello,myworld";
char buf[1024];
if(pipe(fd)<0)
{
perror("pipe");
exit(1);
}
pid=fork();//创建父进程和子进程
//确定方向,父写子读
if(pid>0) //父进程,关闭父读
{
close(fd[0]);
write(fd[1],str,strlen(str));//写数据到文件
wait(NULL);
}
else if(pid==0) //子进程,关闭子写
{
int len,flags;
close(fd[1]);
//先读后写
flags=fcntl(fd[0], F_GETFL);//非阻塞读终端
flags |= O_NONBLOCK;//非阻塞读终端
flags =fcntl(fd[0],F_SETFL,flags); //改变fd[0]的属性
memset(buf,0,sizeof(buf));//清空缓存数组
while(1)
{
len=read(fd[0],buf,sizeof(buf));//从写端读取数据
write(STDOUT_FILENO,buf,len);//将数据显示到终端
if(len>0)//读取到数据
{
break;
}
}
printf("\n");
}
else //创建进程失败
{
perror("fork\n");
exit(-1);
}
}
程序运行效果:
2、fifo有名管道--解决无血缘关系的进程通信
管道文件myfifo存放在磁盘中,标记为内核中的一条管道,管道文件只是一个文件名,没有真正大小
创建管道文件:
[root@localhost 808]# mkfifo xwp
[root@localhost 808]# ls -l xwp //查看管道文件信息
prw-r--r-- 1 root root 0 07-31 04:26 xwp
prw--代表管道文件
//程序3 有名管道 写文件
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char *argv[])
{
//通过文件的控制属性,访问管道文件
int fd;//定义文件描述符
char buf[1024]="hello everybody\n";
if(argc<2)//判断是否有输入
{
printf("enter your fifoname\n");
exit(1);
}
fd=open(argv[1],O_WRONLY);//只写方式打开文件
if(fd<0)
{
perror("open\n");
}
write(fd,buf,strlen(buf));//将数据写到终端并显示出来
close(fd);//关闭文件
return 0;
}
//程序4、有名管道 读文件
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char *argv[])
{
//通过文件的控制属性,访问管道文件
int fd;//定义文件描述符
int len;//读取数据字节数
char buf[1024];//读取数据缓存区
if(argc<2)//判断是否有输入
{
printf("enter your fifoname\n");
exit(1);
}
fd=open(argv[1],O_RDONLY);//只读方式打开文件
if(fd<0)
{
perror("open\n");
}
memset(buf,0,sizeof(buf));//清空缓冲区
len=read(fd,buf,sizeof(buf));//从文件读取数据
write(STDOUT_FILENO,buf,len);//将数据写到终端并显示出来
close(fd);//关闭文件
return 0;
}
程序3和程序4结合使用,程序运行效果:
3、复制文件描述符dup、dup2
//程序5 复制文件描述符dup
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(void)
{
int fd;//定义文件描述符
int newfd;//定义新文件描述符
fd=open("filea.c",O_RDWR | O_CREAT,0777);//只写方式打开文件
printf("fd=%d\n",fd);//输出文件描述符
if(fd<0)
{
perror("open\n");
}
//复制文件描述符,实质指向fd "abc" file*,所以操作newfd和操作fd是一样的
newfd=dup(fd);
printf("newfd=%d\n",newfd);//输出文件描述符
write(newfd,"hello",5);//写数据到新文件描述符所指的文件
close(fd);//关闭文件
close(newfd);//关闭文件
return 0;
}
程序执行效果:
//程序6 文件描述符重定向
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(void)
{
int fd,newfd;//定义文件描述符
//读写或创造方式打开文件
fd=open("fileb.c",O_RDWR | O_CREAT,0777);
printf("fd=%d\n",fd);//输出文件描述符
if(fd<0)
{
perror("open\n");
}
//重定向,跟dup区别,可以用newfd来指定新的文件描述符的值
//若文件已经被打开,先关闭,如果重定向文件等于原来的fd,则共用一份文件
newfd=dup2(fd,5);//重定向后依然向fd所指文件写入数据
printf("newfd=%d\n",newfd);//输出新文件描述符值
write(newfd,"hello",5);//向新文件写入数据
close(fd);//关闭文件
close(newfd);//关闭文件
return 0;
}
程序执行效果:
扩展:
使用命名管道创建一个FileTP的管道文件,使用该管道实现S.out到R.out的文件内容的传送。
//程序7 将数据从文件1写入管道文件
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char *argv[])
{
//通过文件的控制属性,访问管道文件
int fd,newfd;//定义文件描述符
char str[1024]="hello everybody\n";
char buf[1024];
int len;
//创建或打开一个文件
fd=open("file1.h",O_CREAT | O_RDWR,0644);
write(fd,str,strlen(str));//将数据写入文件
close(fd);//关闭文件
fd=open("file1.h",O_RDWR);//再次打开文件
//阻塞读
printf("fd=%d\n",fd);//输出文件描述符
printf("lensizeof(buf)=%d\n",sizeof(buf));//计算字符数组大小
memset(buf,0,sizeof(buf));//清空缓存区
len=read(fd,buf,sizeof(buf));//从文件1中读取数据并存放到缓存数组中
if(argc<2)//判断是否有输入
{
printf("enter your fifoname\n");
exit(1);
}
newfd=open(argv[1],O_WRONLY);//只写方式打开文件
if(newfd<0)
{
perror("open\n");
}
write(newfd,buf,strlen(buf));//将数据写到管道文件中
close(newfd);//关闭管道文件
close(fd);//关闭文件1
return 0;
}
//程序8 将数据从管道文件写入文件2中
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char *argv[])
{
//通过文件的控制属性,访问管道文件
int fd,newfd;
int len;
char buf[1024];
if(argc<2)//判断是否有输入
{
printf("enter your fifoname\n");
exit(1);
}
fd=open(argv[1],O_RDONLY);//只读方式打开文件
if(fd<0)
{
perror("open\n");
}
memset(buf,0,sizeof(buf));//清空缓冲区
len=read(fd,buf,sizeof(buf));//从管道文件中读取数据
write(STDOUT_FILENO,buf,len);//将数据写到终端并显示出来
//创建或打开一个文件
newfd=open("file2.h",O_CREAT | O_RDWR,0644);
write(newfd,buf,strlen(buf));//将数据写入文件2中
close(newfd);//关闭文件2
close(fd);//关闭管道文件
return 0;
}
程序7和程序8结合使用,程序执行效果: