Day05 linux高级系统设计 - 管道
复制文件描述符
dup函数
作用:
文件描述符复制
语法:
#include <unistd.h>
int dup (int oldfd);
参数:
所需复制得文件描述符
返回值:
复制到的文件描述符
功能:
从文件描述符表中,找一个最小可能的文件描述符(通过返回值返回)作为oldfd复制
示例1:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{int newpd = dup(1);write(newpd,"hello world\n",12);return 0;
}
dup2函数(推荐)
函数:
#include <unistd.h>
int dup2(int oldfd,int newfd);
参数:
oldfd:原文件描述符
newfd:指定复制到的文件描述符,如果该文件描述符已存在,那将已存在的关闭
示例:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{int fd = open("huange.txt",O_WRONLY | O_CREAT | O_APPEND,0666);int pd = dup2(fd,1);printf("娶你明年\n");return 0;
}
无名管道
概述
pipe函数
作用:用于创建无名管道
语法:
#include <unistd.h>
int fd[] ;
pipe(fd);
参数:
fd为int型数据的首元素地址,其存放了管道的文件描述符反对fd[0],fd[1]
f[0]为读而打开管道,f[1]为写而打开
返回值:
成功:0
失败:-1
int fd[2];
pipe(fd);
示例:
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
int main(int argc, char const *argv[])
{int fd[2];pipe(fd);int i = 0;for(i = 0;i < 2;i++){int pid = fork();if(pid == 0){break;}}if(i == 0){close (fd[0]);char buf[] = "hello to2";write(fd[1],buf,sizeof(buf));close(fd[1]);_exit(0);}else if(i == 1){close (fd[1]);char buf[60] = {0};read(fd[0],buf,sizeof(buf));close(fd[0]);printf("%s\n",buf);_exit(0);}else if(i == 2){while(1){int pid = waitpid(-1,NULL,WNOHANG);if(pid == 0){break;}}}return 0;
}
读写特点
1.默认用read函数从管道中读取数据时是阻塞的
2.调用write函数写入时,当管道的缓冲区已满时,也会发生阻塞,管道的缓冲区大小时65kb
3.通信过程中,读端口全部关闭后,写进程向管道内写数据时,写进程会(收到SIGPIPE 信号)退出。
4.从管道中读取数据的特点 编程时可通过fcntl函数设置文件的阻塞特性。设置为阻塞:fcntl(fd,FSETFL,O_NONBLOCK);
示例一:缓冲区已满时 write 也会阻塞。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{int fd[2] ;pipe(fd);int pid = fork();if(pid == 0){close(fd[1]);sleep(2);close(fd[0]);_exit(0);}else if(pid > 0){close(fd[0]);//关闭读int count = 0;for(int i = 0;i < 10000;i++){int buf[1024] = {0};printf("buf=%ld\n",sizeof(buf)/sizeof(buf[0]));write(fd[1],buf,1024);count += 1024;printf("i=%d\ncount%d\n",i,count);} close(fd[1]);wait(NULL);}return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{//证明:调用 write 函数向管道里写数据,当缓冲区已满时 write 也会阻塞int fd[2];pipe(fd);for (int i = 0; i < 1000; i++){char buf[1024] = {0};write(fd[1],buf,1024);printf("i = %d\n",i);}printf("OVER\n");return 0;
}
示例二:通信过程中,写端关闭,读端将解阻塞
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char const *argv[])
{int fd[2];pipe(fd);int pid = fork();if(pid == 0){close(fd[1]);int buf[100] = {0};printf("开始读取\n");read(fd[0],buf,100);close(fd[0]);printf("读取结束\n");_exit(0);}else if(pid > 0){close(fd[0]);sleep(2);close(fd[1]);sleep(5);_exit(0);}return 0;
}
示例三:通信过程中 读端关闭 写端将收到SIGPIPE信号 退出写端进程
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{int fd[2];pipe(fd);int pid = fork();if (pid == 0){close(fd[1]);while (1){int i = 0;char buf[128] = "";int len = read(fd[0], buf, sizeof(buf) / sizeof(buf[0]));i++;printf("len=%d\n", len);if (i == 5){break;}}}else if (pid > 0){close(fd[0]);while(1){printf("给父进程%u写数据\n",getpid());write(fd[1],"hello pipe",10);sleep(1);}close(fd[1]);wait(NULL);}return 0;
}
综合案例
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{int pd[2];pipe(pd);int pid = fork();if(pid < 0){printf("输入有误\n");return 0;}else if(pid == 0){close(pd[1]);dup2(pd[0],0);execl("/bin/grep","/bin/grep","bash",NULL);_exit(-1);}else if(pid > 0){close(pd[0]);dup2(pd[1],1);execl("/bin/ps","ps","-A",NULL);wait(NULL);}return 0;
}
有名管道
概述:
又名命名管道(FIFO)
概述
mkfifo函数
作用:创建有名管道
语法:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname,mode_t mode);
参数:
pathname:文件名
mode:文件操作模式,一般使用0666(所有用户可读可写)
返回值
成功:0
失败:-1 一般失败是因为有与pathname名相同的文件
读写特点
综合案例
09_demoA.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char const *argv[])
{int i = 0;for(i = 0;i < 2;i++){int pid = fork();if(pid == 0){break;}}if(i == 0){mkfifo("./zjrtozl",0666);//张晋若发送消息int fd = open("./zjrtozl",O_WRONLY);while(1){char buf[100] = {0};fgets(buf,100,stdin); printf("张晋若:%s\n",buf);write(fd,buf,strlen(buf));buf[strlen(buf)-1] = 0;if(strcmp("886",buf) == 0){break;}}close(fd);_exit(0);}else if(i == 1){mkfifo("./zltozjr",0666);//张立接收消息int fd = open("./zltozjr",O_RDONLY);while(1){char buf[100] = {0};read(fd,buf,sizeof(buf));printf("张立:%s\n",buf);buf[strlen(buf)-1] = 0;if(strcmp("886",buf) == 0){break;}}close(fd);_exit(0);}else if(i == 2){while(1){int pid = waitpid(-1,NULL,WNOHANG);if(pid == -1){break;//子进程已经回收完毕}}}return 0;
}
09_demoB.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char const *argv[])
{int i = 0;for(i=0;i < 2;i++){int pid = fork();if(pid == 0){break;}}if(i == 0){mkfifo("./zltozjr",0666);//张立发消息int fd = open("./zltozjr",O_WRONLY);while(1){char buf[120] = {0};fgets(buf,120,stdin);printf("张立:%s\n",buf);write(fd,buf,strlen(buf));buf[strlen(buf)-1] = 0;if(strcmp("886",buf) ==0){break;}}close(fd);_exit(0);}else if(i == 1){mkfifo("./zjrtozl",0666);//张晋若接收消息int fd =open("./zjrtozl",O_RDONLY);while(1){char buf[] = {0};read(fd,buf,sizeof(buf));printf("张晋若:%s\n",buf);buf[strlen(buf)-1] = 0;if(strcmp("886",buf)==0){break;}}close(fd);_exit(0);}else if(i == 2){while(1){int pid = waitpid(-1,NULL,WNOHANG);if(pid == -1){break;//子进程回收完毕}}}return 0;
}
总结
无名管道与有名管道的使用场景
1.无名管道应用于有血缘关系的进程中
2.有名管道应用于没有血缘关系的进程中
有名管道与无名管道的区别
1.无名管道是基于内存的,无需文件管理系统
2.有名管道是基于文件和内存的,需要文件管理系统
dup
作用;复制文件描述符
意义:可以通过dup函数实现文件的重定向