目录
一、文件描述符复制
1、dup函数(复制文件描述符)
编辑 2、dup2函数(复制文件描述符)
编辑 二、无名管道pipe
1、概述
2、无名管道的创建
3、无名管道读写的特点
4、无名管道ps -A | grep bash实现
三、有名管道FIFO(命名管道)
1、概述
2、创建有名管道 mkfifo
3、有名管道读写的特点
一、文件描述符复制
让新的文件描述符 指向 旧的文件描述符。(新旧文件描述符指向同一个文件) 使用的函数dup、dup2。
1、dup函数(复制文件描述符)
#include<unistd.h>
int dup(int oldfd);
dup函数的功能:从系统中寻找最小可用的文件描述符 作为oldfd的副本。 新文件描述符 通过dup的返回值返回。
2、dup2函数(复制文件描述符)
#include<unistd.h>
int dup2(int oldfd, int newfd);
dup2的功能:将newfd作为oldfd的副本。 如果newfd事先存在 dup2会先close(newfd),然后将newfd作为oldfd的副本。
二、无名管道pipe
1、概述
管道(pipe)又称无名管道。 无名管道是一种特殊类型的文件,在应用层体现为 两个打开的文件描述符(读端和写端)。
管道的特点:
- 半双工,数据在同一时刻只能在一个方向上流动。
- 数据只能从管道的一端写入,从另一端读出。
- 写入管道中的数据遵循先入先出的规则。
- 管道所传送的数据是无格式的,这要求管道的读出方与写入方必须事先约定好数据 的格式, 如多少字节算一个消息等。
- 管道不是普通的文件,不属于某个文件系统,其只存在于内存中。
- 管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。
- 从管道读数据是一次性操作,数据一旦被读走,它就从管道中被抛弃,释放空间以便写更多的数据。
- 管道没有名字,只能在具有公共祖先的进程之间使用
2、无名管道的创建
#include<unistd.h>
int pipe(int filedes[2]);
功能:经由参数filedes返回两个文件描述符
参数: filedes为int型数组的首地址,其存放了管道的文件描述符fd[0]、fd[1]。 filedes[0]为读而打开,filedes[1]为写而打开管道,filedes[0]的输出是filedes[1]的输 入。
返回值: 成功:返回 0 失败:返回-1
注意:在使用无名管道的时候 必须事先确定,谁发,谁收的问题。一旦确定不可更改。
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{//创建一个无名管道int fd[2];pipe(fd);//创建一个子进程//父进程发 子进程收pid_t pid=fork();if(pid==0)//子进程{//子进程的写端无意义(可以事先关闭)close(fd[1]);//子进程接收父进程消息unsigned char buf[128]="";printf("子进程%d正在等待父进程的消息\n", getpid());read(fd[0],buf,sizeof(buf));printf("子进程%d读到的消息为:%s\n", getpid(), buf);//子进程读完数据 应该关闭读端close(fd[0]);//显示退出_exit(-1);}if(pid>0) //父进程{//父进程的读端无意义(可以事先关闭)close(fd[0]);//写端写入数据printf("3秒后父进程%d写入数据hello pipe\n",getpid());sleep(3);write(fd[1],"hello pipe",strlen("hello pipe"));printf("父进程:%d完成写入\n", getpid());//通信完成 应该关闭写端close(fd[0]);//等待子进程退出wait(NULL);}return 0;
}
3、无名管道读写的特点
1、默认用read函数从管道中读数据是阻塞的。
2、调用write函数向管道里写数据,当缓冲区已满时write也会阻塞。
3、通信过程中,读端口全部关闭后,写进程向管道内写数据时,写进程会(收 到SIGPIPE信号)退出。
4、无名管道ps -A | grep bash实现
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
int main(int argc, char const *argv[])
{//创建一个无名管道int fd[2];pipe(fd);//创建两个子进程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]);//1作为fd[1]的副本dup2(fd[1],1);//执行ps ‐Aexeclp("ps","ps","-A",NULL);_exit(-1);}else if(i==1)//子进程2{//grep bash 读端close(fd[1]);//0作为fd[0]的副本dup2(fd[0],0);//执行grep bashexeclp("grep","grep","bash",NULL);}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;
}
三、有名管道FIFO(命名管道)
1、概述
主要用于没有血缘关系的进程间通信。
特点:
- 半双工,数据在同一时刻只能在一个方向上流动。
- 写入FIFO中的数据遵循先入先出的规则。
- FIFO所传送的数据是无格式的,这要求FIFO的读出方与写入方必须事先 约定好数据的格式,如多少字节算一个消息等。
- FIFO在文件系统中作为一个特殊的文件而存在,但FIFO中的内容却存放在内存中。
- 管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。
- 从FIFO读数据是一次性操作,数据一旦被读,它就从FIFO中被抛弃,释 放空间以便写更多的数据。
- 当使用FIFO的进程退出后,FIFO文件将继续保存在文件系统中以便以后使用。
- FIFO有名字,不相关的进程可以通过打开命名管道进 行通信(重要)
2、创建有名管道 mkfifo
FIFO文件的创建
#include<sys/type.h>
#include<sys/stat.h>
int mkfifo( const char *pathname, mode_t mode);
参数:
pathname:FIFO的路径名+文件名。
mode:mode_t类型的权限描述符。
返回值: 成功:返回 0 失败:如果文件已经存在,则会出错且返回-1
fifo_write.c:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{//创建有名管道(确保两个进程识别相同目录)mkfifo("my_fifo",0666);//open以写的方式的打开 有名管道(阻塞 到 对方 以读的方式打开)int fp=open("my_fifo",O_WRONLY);if(fp<0){perror("open");return 0;}printf("写端open成功\n");while(1){//获取键盘输入unsigned char buf[128]="";printf("请输入需要发送的数据:");fgets(buf,sizeof(buf),stdin);buf[strlen(buf)-1]=0;//发送数据write(fp,buf,sizeof(buf));//退出循环if(strcmp(buf,"Bye")==0)break;}close(fp);
}
fifo.read.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{//创建有名管道(确保两个进程识别相同目录)mkfifo("my_fifo",0666);//open以读的方式的打开 有名管道(阻塞 到 对方 以写的方式打开)int fp=open("my_fifo",O_RDONLY);if(fp<0){perror("open");return 0;}printf("读端open成功\n");//循环的读取数据while(1){//接收数据unsigned char buf[128]="";read(fp,buf,sizeof(buf));printf("收到数据:%s\n", buf);//退出循环if(strcmp(buf,"Bye")==0)break;}close(fp);
}
3、有名管道读写的特点
阻塞方式打开管道:
1、open以只读方式打开FIFO时,要阻塞到某个进程为写而打开此FIFO
2、open以只写方式打开FIFO时,要阻塞到某个进程为读而打开此FIFO。
3、open以只读、只写方式打开FIFO时会阻塞,调用read函数从FIFO里读数据时read也会阻塞。
4、通信过程中若写进程先退出了,则调用read函数从FIFO里读数据时不阻塞;若写进程又 重新运行,则调用read函数从FIFO里读数据时又恢复阻塞。
5、通信过程中,读进程退出后,写进程向命名管道内写数据时,写进程也会(收到 SIGPIPE信号)退出。
6、调用write函数向FIFO里写数据,当缓冲区已满时write也会阻塞。
非阻塞方式打开管道:
1、先以只读方式打开:如果没有进程已经为写而打开一个FIFO, 只读open成功,并且 open不阻塞。
2、先以只写方式打开:如果没有进程已经为读而打开一个FIFO,只写open 将出错返回-1。
3、read、write读写命名管道中读数据时不阻塞。
4、通信过程中,读进程退出后,写进程向命名管道内写数据时,写进程也会(收到 SIGPIPE信号)退出。
注意: open函数以可读可写方式打开FIFO文件时的特点:
1、open不阻塞。
2、调用read函数从FIFO里读数据时read会阻塞。
3、调用write函数向FIFO里写数据,当缓冲区已满时write也会阻塞