复制文件描述符
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函数实现文件的重定向