赞
踩
摘要:管道是如何让进程间通信的,管道的特点有哪些,编程如何实现,管道的读写特性又是什么。
我们说进程与进程之间它们的内存空间是相互独立的,所以进程间通信不像线程间通信那样只需要定义全局变量就可以那般简单。它们需要用到的是管道,因为管道是在内核开辟的一片空间,而内核区是进程共有的,所以可以共同访问。
而管道又分为了无名管道和有名管道。众所周知Linux所有东西的呈现方式都是以文件的形式呈现的。而管道也属于文件,文件类型是"p",无名管道就是即使你用命令"ls -l"也查不到这个管道文件的,无名管道是用于具有亲缘关系的进程之间的通信。而有名管道是用命令"ls -l"可以查看得到的,有名管道可用于两个没有亲缘关系进程之间的通信也可用于有亲缘关系进程之间的通信。
无名管道有固定的输入端和输出端
无名管道的创建函数:
- #include <unistd.h>
-
- int pipe(int pipefd[2]);
可以看到 参数是一个 数组,所以调用时要先定义数组,然后将数组名传递进来。
我们来写一个子进程间与父进程间通信代码:
- int main(int argc, char *argv[])
- {
- int pipefd[2]={0}; //定义数组,用于装无名管道的写入端和输出端的文件描述符。
- pipe(pipefd); //创建无名管道
- pid_t pid = fork(); //创建子进程
- if(pid<0)
- {
- perror("fork");
- return -1;
- }
- if(pid>0) //父进程
- {
- char buf[64]={0}; //定义一个缓冲区装读到的数据
- while(1) //循环读取
- {
- read(pipefd[0],buf,64); //将管道输出端 数据读取到buf缓冲区。
- puts(buf); //输出读取到的数据
- memset(buf,0,64);
- }
- }
- else //子进程
- {
- char buf[64]={0}; //定义一个缓冲区装要写入的数据
- while(1) //循环写入
- {
- fgets(buf,64,stdin); //从标准输入流中输入数据到buf缓冲区。
- buf[strlen(buf)-1]='\0'; //将换行符吃掉
- write(pipefd[1],buf,strlen(buf)); //把写入进缓冲区的数据写入无名管道的输入端。
- }
- }
- return 0;
- }

运行后,就可以达到子进程输入父进程输出了 ,直接看结果:
无名管道的读写特性:当不读取数据,一直往输入端输入数据时,数据满了的话,写入函数wirte转为阻塞状态。我们可以利用这个特性去打印看看无名管道的存储上限是多少:
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/wait.h>
-
- int main(int argc, char *argv[]) //同上代码就不多做赘述了
- {
- int pipefd[2]={0};
- pipe(pipefd);
- pid_t pid = fork();
- if(pid<0)
- {
- perror("fork");
- exit(-1);
- }
- if(pid>0)
- {
- wait(NULL); //只做等待 ,不做打印
- }
- else
- {
- long int i=0;
- int ret;
- char buf[64]={"dfsdgsdfgsdfsd"};
- while(1) // 一直循环写入数据
- {
- ret=(write(pipefd[1],buf,64));
- i=i+ret; //记录写入大小
- printf("%ld\n",i); //打印写入大小
- }
- exit(-1);
- }
- return 0;
- }

看看运行结果:
可以看到当写入字节到达65536时 函数进入了阻塞状态。说明无名管道大小为65535个字节(操作系统而定)。
无名管道第二个特性,当管道输出端口被关闭时,在写入管道的第一瞬间,程序结束(不管后面还有多少程序),我们成之为管道破裂。看个例子:
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/wait.h>
-
- int main(int argc, char *argv[]) //同上代码就不多做赘述
- {
- int pipefd[2]={0};
- pipe(pipefd);
- close(pipefd[0]); //关闭输出端
- pid_t pid = fork();
- if(pid<0)
- {
- perror("fork");
- exit(-1);
- }
- if(pid>0)
- {
- wait(NULL);
- }
- else
- {
- long int i=0;
- int ret;
- char buf[64]={"dfsdgsdfgsdfsd"};
- while(1)
- {
- write(pipefd[1],buf,64); // 在写入的一瞬间 程序结束
- sleep(5); //测试是否会等待5秒 (其实不会)
- }
- exit(-1);
- }
- return 0;
- }

结果:当运行程序后毫无反应,便是结束了,说明管道已破裂。
而有名管道没有固定的输入输出端口
创建有名管道的函数:
- #include <sys/types.h>
- #include <sys/stat.h>
-
- int mkfifo(const char *pathname,mod_t mode);
参数
①:pathname 创建的文件管道名,不加路径默认创建在当前路径。
②: mode 管道文件权限,我们只需要进行读写操作就行,所以填0664;
创建成功返回0,失败返回-1.
接下来我们直接写两个经常,然后让它们通信
进程1(有名管道创建,写入管道):
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <stdlib.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <string.h>
- int main(int argc, char *argv[])
- {
- /*若第二次运行 此期间代码注释掉 ,因为管道文件第一次运行时就已创建完毕。
- int ret = mkfifo("fifo",0664); //创建有名管道 名字为fifo,权限0664。
- if(ret<0) //判断是否创建成功。
- {
- perror("mkfifo");
- return -1;
- }
- */
- int fd =open("fifo",O_RDWR); //打开管道文件
- char buf[64]={0}; //定义缓冲区
- while(1) //循环写入
- {
- fgets(buf,64,stdin); //从终端输入数据 进缓冲区
- buf[strlen(buf)-1]='\0'; //吃掉换行
- write(fd,buf,strlen(buf)); //将缓冲区里的数据写入有名管道。
- }
- return 0;
- }

进程2(读取管道里的信息):
- #include <stdio.h>
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <stdlib.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <string.h>
- int main(int argc, char *argv[])
- {
- int fd = open("fifo",O_RDWR); //打开管道文件
-
- char buf[64]={0}; 定义buf缓冲区
- while(1) //循环读取并打印
- {
- read(fd,buf,64); //读取管道里的数据到缓冲区
- puts(buf); //输出管道里的数据
- memset(buf,0,64); //清空缓冲区的数据 不然会一直打印上一次发送的数据
- }
- return 0;
- }

我们将进程1作为输入进程,进程2作为输出进程。注意!需要同时运行两个进程!!
效果如下:
左边时输入进程,右边是输出进程。可以看到两个不相关的进程已经可实现通信了。注意,这里的两个进程是本机之间的通信,与其他设备上不可通信,除非是网络编程,当然,后面我们也会讲到网络编程,一起去探究设备与另一个设备间是如何通信的。
今天的分享就到这里了,哪里写的不对的地方希望大家多多指正,毕竟作者也还是个小菜鸡。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。