- 一 文件描述符复制
- 1.1 dup函数(复制文件描述符)
- 1.2 dup2函数(复制文件描述符)
- 二 无名管道的概述
- 三 无名管道的特点
- 四 无名管道的创建和使用
- 4.1 无名管道创建函数pipe
- 4.2 无名管道使用实例一
- 4.3 无名管道的读写特点
- 4.4 无名管道的默认大小
- 4.5 无名管道使用实例二:写一个程序实现 ps -A | grep bash
让新的文件描述符指向旧的文件描述符,新旧文件描述符指向同一文件;
使用函数dup,dup2;
- 需要的头文件和函数原型
#includeint dup(int oldfd); int dup2(int fildes, int fildes2);
-
函数功能
从系统中寻找最小的可用文件描述符,作为已有文件描述符oldfd的副本,新文件描述符和旧文件描述符指向同一文件,新文件描述符提供dup的返回值返回。 -
dup函数案例
#include1.2 dup2函数(复制文件描述符)#include #include #include int main(int arg,char *args[]) { int newfd = 0; newfd = dup(1); if(newfd == -1) { perror("dup"); return -1; } printf("新文件描述符是:%dn",newfd); printf("向文件描述符1输出n"); write(newfd,"向新文件描述符输出n",strlen("向新文件描述符输出n")); while(1) { sleep(1); } return 0; }
- 需要的头文件和函数原型
#includeint dup2(int oldfd, int newfd);
-
功能
将newfd作为oldfd的副本。
如果newfd事先存在,dup2会先close掉newfd,然后将newfd作为oldfd的副本。 -
dup2函数实例
#include二 无名管道的概述#include #include #include int main(int arg,char *args[]) { int newfd = 0; newfd = dup2(1,100); if(newfd == -1) { perror("dup"); return -1; } printf("新文件描述符是:%dn",newfd); printf("向文件描述符1输出n"); write(newfd,"向新文件描述符输出n",strlen("向新文件描述符输出n")); while(1) { sleep(1); } close(newfd); return 0; }
管道(pipe)又称无名管道。无名管道是一种特殊类型的文件,在应用层体现为两个打开的文件描述符,实际上它是一块逻辑内存。
三 无名管道的特点- 半双工,创建一个管道,这个管道上数据只能在一个方向上流动;
- 数据只能从管道的一端写入,从另一端读出;
- 写入管道中的数据遵循先入先出的规则;
- 管道所传送的数据是无格式的,这要求管道的读出方与写入方必须事先约定好数据的格式,如多少字节一个消息等;
- 管道不是普通文件,不属于某个文件系统,其只存在于内存中;
- 管道在内存中对应一个缓冲区,不同的系统其大小不一定相同;
- 从管道读取数据是一次性操作,数据一旦被读走,他就从管道中被抛弃,释放空间以便写更多的数据;
- 管道没有名字,只能在有血缘关系的进程之间使用。
- 需要的头文件和函数原型
#include
函数功能
经由参数filedes返回两个文件描述符参数
filedes为int型数组的首地址,其中存放了管道文件描述符fd[0],fd[1]; filedes[0]为读而打开;filedes[1]为写而打开管道。filedes[0]的输出是filedes[1]的输入。
- 返回值
成功:返回0 失败:返回-14.2 无名管道使用实例一
- 注意
使用无名管道时,必须事先确定谁发,谁收的问题。
#include4.3 无名管道的读写特点#include #include #include #include #include int main(int arg, char *argv[]) { //创建无名管道 int fd[2]; pipe(fd); //创建一个子进程 //父进程发;子进程收 pid_t pid = fork(); if(pid == 0) //子进程 { //子进程写端无意义(可以事先关闭) close(fd[1]); //子进程接收父进程消息 printf("子进程%d正在等待父进程的消息n",getpid()); unsigned char buf[128] = ""; read(fd[0],buf,sizeof(buf)); printf("子进程%d读到的消息为:%sn",getpid(),buf); //子进程读完数据,应该关闭读端 close(fd[0]); //显示退出 _exit(-1); } else if(pid > 0) //父进程 { //父进程的读端无意义(可以事先关闭) close(fd[0]); //写端写入数据 printf("父进程:%d 3秒后写入数据hello pipen",getpid()); sleep(3); write(fd[1],"hello pipe",strlen("hello pipe")); printf("父进程:%d完成写入n",getpid()); //通信完成,应该关闭写端 close(fd[1]); //等待子进程退出 wait(NULL); } return 0; }
- 默认用read函数从管道中读数据是阻塞的;
- 用write函数向管道中写数据,当缓冲区已满时也是阻塞的;
- 通信过程中如果读端口全部关闭后,写进程向管道写数据时,写进程会收到SIGPIPE信号而退出;
- 可以通过fcntl函数来设置管道的阻塞特性:
设置为非阻塞:fcntl(fd,FSETFL, O_NONBLOCK); 设置为阻塞:fcntl(fd,FSETFL, 0);4.4 无名管道的默认大小4.5 无名管道使用实例二:写一个程序实现 ps -A | grep bash
用命令ulimit -a 查看系统中无名管道的默认大小
验证无名管道有大小限制
#include#include #include #include #include #include int main(int arg, char *argv[]) { //创建无名管道 int fd[2]; int res = 0; res = pipe(fd); if(res == -1) { perror("pipe"); return 1; } //创建两个子进程 int i=0; for(i=0;i<2;i++) { pid_t pid = fork(); if(pid == 0) break; } if(i==0) //子进程1 { //ps -A 写端 //关闭管道的读端口 close(fd[0]); //将管道的读端口的文件描述符复制给文件描述符0 int res1 = dup2(fd[1],1); if(res1 == -1) { perror("dup2"); return 1; } int res2 = execl("/bin/ps","ps","-A",NULL); if(res2 == -1) { perror("execl"); } _exit(0); } else if(i == 1)//子进程2 { //子进程2负责执行grep bash //关闭管道的读端口 close(fd[1]); //将管道的写端口的文件描述符复制给文件描述符1 int res1 = dup2(fd[0],0); if(res1 == -1) { perror("dup2"); return 1; } int res2 = execl("/bin/grep","grep","bash",NULL); if(res2 == -1) perror("exec"); _exit(0); } else if(i == 2)//父进程 { //关闭管道读写端口 close(fd[0]); close(fd[1]); while(1) { pid_t pid = waitpid(-1,NULL,WNOHANG); if(pid>0) { printf("子进程%d被主进程回收n",pid); } else if(pid == 0) { continue; } else if(pid<0) { break; } } } return 0; }



