目录

1. 认识无名管道

2. 无名管道大小测试

3. 无名管道练习

4. 无名管道_两个管道双向传输

5. 有名(命名)管道

总结


任务间的通信与同步(7种)

管道    信号  信号量    互斥锁  消息队列   共享内存   socket套接字

1. 认识无名管道

管道相关的关键概念

管道是Linux支持的最初Unix IPC形式之一,具有以下特点:

(1)管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;
(2)只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)
(3)单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。
(4)数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。

管道的创建:

#include <unistd.h>
int pipe(int fd[2])//Return 0 on success,or -1 on error

该函数创建的管道的两端处于一个进程中间,在实际应用中没有太大意义,因此,一个进程在由pipe()创建管道后,一般再fork一个子进程,然后通过管道实现父子进程间的通信(因此也不难推出,只要两个进程中存在亲缘关系,这里的亲缘关系指的是具有共同的祖先,都可以采用管道方式来进行通信)。

管道的读写规则:

管道两端可分别用描述字 fd[0] 以及 fd[1] 来描述,需要注意的是,管道的两端是固定了任务的。即一端只能用于读,由描述字 fd[0] 表示,称其为管道读端;另一端则只能用于写,由描述字fd[1] 来表示,称其为管道写端。如果试图从管道写端读取数据,或者向管道读端写入数据都将导致错误发生。 一般文件的I/O函数都可以用于管道,如close、read、write等等。

从管道中读取数据:

(1)如果管道的写端不存在,则认为已经读到了数据的末尾,读函数返回的读出字节数为0;
 (2)当管道的写端存在时,如果请求的字节数目大于PIPE_BUF,则返回管道中现有的数据字节数,如果请求的字节数目不大于PIPE_BUF,则返回管道中现有数据字节数(此时,管道中数据量小于请求的数据量);或者返回请求的字节数(此时,管道中数据量不小于请求的数据量)。
注:(PIPE_BUF在 include/linux/limits.h 中定义,不同的内核版本可能会有所不同。Posix.1要求PIPE_BUF至少为 512字节,red hat 7.2中为4096)。

向管道中写入数据:

(1)向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞。 
(2)管道中的读端关闭,此时向管道写入数据的进程将收到内核传来的SIFPIPE信号,应用程序可以处理该信号,也可以忽略(默认动作则是应用程序终止)。

当管道中父进程没有写入数据时,而子进程使用read读管道中的数据时,read将一直阻塞,等待管道中写入数据

NAME : pipe ,  pipe2   ----- creat pipe
pip1.c

/*parent process : write pipechild  process : read  pipe
*/
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>int main(void)
{int fd[2];int pid;if(pipe(fd) == -1)         //创建管道,创建错误返回-1,正确返回0perror("pipe");pid = fork();      // 创建进程if(pid > 0)  // 父进程         fd[0] 读 fd[1] 写{close(fd[0]);               //关闭管道读端sleep(5);write(fd[1], "ab", 2);      //写管道写数据(2为写入字符为两个字节大小)while(1);}else if(pid == 0)    // 子进程复制了管道{   char ch[2];   // 用于接收数据printf("child process is waiting for data: \n");close(fd[1]);         // 关闭管道写端read(fd[0], ch, 2);   // 存放到ch,2个字节大小printf("read from pipe: %s\n", ch);} return 0;
}

读函数read 
ssize_t read ( int  fd, void   * buf,  size_t nbyte) 
read函数是负责从fd中读取内容. 成功时,read返回实际所读的字节数,如果返回的值是0,表示已经读到文件的结束了.
小于0表示出现了错误. 如果错误为EINTR说明读是由中断引起的,  如果是ECONNREST表示网络连接出了问题.

写函数write 
ssize_t write ( int  fd, const   void   * buf, size_t nbytes) 
write函数将buf中的nbytes字节内容写入文件描述符fd.成功时返回写的字节数.失败时返回-1. 并设置errno变量. 在网络程序中,当我们向套接字文件描述符写时有俩种可能.  
1)write的返回值大于0,表示写了部分或者是全部的数据.  
2)返回的值小于0,此时出现了错误.我们要根据错误类型来处理.  如果错误为EINTR表示在写的时候出现了中断错误.  
如果为EPIPE表示网络连接出现了问题(对方已经关闭了连接).

2. 无名管道大小测试

管道的容量是有限的:管道的存储能力为65536字节

#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/wait.h>int main(void)
{pid_t pid;int fd[2];if(pipe(fd) == -1)perror("pipe");pid = fork();       // 创建子进程if(pid == 0)        // child process : write to the pipe{                   // 子进程不断写入管道*char ch = '*';int n = 0;close(fd[0]); // 关闭读端while(1){               // 地址write(fd[1], &ch, 1);printf("count =  %d\n", ++n);}}  else if(pid > 0)         //parent process : wait until child process is over{waitpid(pid, NULL, 0); // 父进程等待子进程结束}
}

结果:。。。count = 65536 (字节)

3. 无名管道练习

子进程把键盘输入的数据写入管道,父进程读取数据

pip_w&r_.c

#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/wait.h>int main(void)
{pid_t pid;int fd[2];if(pipe(fd) == -1)perror("pipe");pid = fork();       //创建子进程if(pid == 0)             //child process : write to the pipe{ char tmp[100];  // 用于存储数据close(fd[0]);while(1){printf("parent processs is waiting for you message\n");scanf("%s", tmp)                 // 等待键盘输入数据write(fd[1], tmp, sizeof(tmp));  // 将数据写入管道     }}  else if(pid > 0)         //parent process : 读取子进程的输入{char tmp[100];close(fd[1]);while(1)  // 只要管道有内容就会读取{read(fd[0], tmp, sizeof(tmp));            //读取printf("read from pipe : %s\n", tmp);    // 输出}}
}

4. 无名管道_两个管道双向传输

two_pip.c

#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/wait.h>
#include <string.h>
#include <ctype.h>
int main(void)
{pid_t pid;int fd[2];int fd2[2];    // 两个描述符// 判断是否成功if(pipe(fd) == -1)perror("pipe");if(pipe(fd2) == -1)perror("pipe");pid = fork();       //创建子进程if(pid == 0)        //child process :{ char tmp[100];close(fd[1]);    //关闭fd管道的写   close(fd2[0]);   //关闭fd管道的读while(1){memset(tmp, '\0', sizeof(tmp));     // 对tmp做清空处理read(fd[0], tmp, sizeof(tmp));      // 子进程等待去读管道for( i = 0; i < sizeof(tmp); i++)   // 有数据时,read由阻塞变为非阻塞tmp[i] = toupper(tmp[i]);  // 将所有的字符变为大写toupperwrite(fd2[1], tmp, sizeof(tmp));    // 写入管道fd2}}  else if(pid > 0)         //parent process : 读取子进程的输入{char tmp[100];close(fd[0]);close(fd[1]);while(1){ memset(tmp, '\0', sizeof(tmp));gets(tmp);                      //父进程由键盘输出write(fd[1], tmp, sizeof(tmp)); //向子进程写入小写memset(tmp, '\0', sizeof(tmp));read(fd2[0], tmp, sizeof(tmp));     //读取子进程传过来的大写printf("after change : %s \n", tmp)}}
}

memset 函数

 代码示例

#include <stdio.h>
#include <string.h>int main ()
{char str[50];strcpy(str,"This is string.h library function");puts(str);memset(str,'$',7);puts(str);return(0);
}//  This is string.h library function
//  $$$$$$$ string.h library function

5. 有名(命名)管道

  • 匿名管道,由于没有名字,只能用于亲缘关系的进程间通信。为了克服这个缺点,提出了有名管道(FIFO),也叫命名管道、FIFO 文件。
  • 有名管道(FIFO)不同于匿名管道之处在于它提供了一个路径名与之关联,以 FIFO 的文件形式存在于文件系统中,并且其打开方式与打开一个普通文件是一样的,这样即使与 FIFO 的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过 FIFO 相互通信,因此,通过 FIFO 不相关的进程也能交换数据。
  • 一旦打开了 FIFO,就能在它上面使用与操作匿名管道和其他文件的系统调用一样的 I/O 系统调用了(如read()、write() 和 close())。与管道一样,FIFO 也有一个写入端和读取端,并且从管道中读取数据的顺序与写入的顺序是一样的。FIFO 的名称也由此而来:先入先出
  • 有名管道(FIFO) 和匿名管道(pipe)有一些特点是相同的,不一样的地方在于:

1. FIFO 在文件系统中作为一个特殊文件存在,但 FIFO 中的内容却存放在内存中。

2. 当使用 FIFO 的进程退出后,FIFO 文件将继续保存在文件系统中以便以后使用。

3. FIFO 有名字,不相关的进程可以通过打开有名管道进行通信。

非亲缘关系进程之间进行管道通信

NAME : mkfifo ,  mkfifoat  -  make a FIFO(先入先出) special  file(a named pipe)

模式(model):写权限4,读权限2,可执行权限1  (主要读写)

函数功能:创建一个FIFO文件,用于进程之间的通信。pathname就是路径名,mode是该文件的权限。返回值表示是否成功,返回0表示成功,返回-1表示失败,失败原因在errno中。(建立FIFO的时候,要求不存在这样的FIFO)。

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);// 函数功能:创建一个FIFO文件,用于进程之间的通信。pathname就是路径名,mode是该文件的权限。返回值表示是否成功,返回0表示成功,返回-1表示失败,失败原因在errno中。(建立FIFO的时候,要求不存在这样的FIFO)。

read_named_pipe.c

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>int main(void)
{int ret;int fd;char buf[100];ret = mkfifo("my_fifo", 666);     // 创建文件,权限if(ret != 0)perror("mkfifo");printf("prepare reading from the pip :\n");fd = open("my_fifo",  0_RDWR);if(fd == -1)perror("open");while(1){memset(buf, '\0', sizeof(buf));read(fd, buf, sizeof(buf));printf("read from named pipe : %s\n", buf);sleep(1);}return 0; }

编译  :gcc read_named_pipe.c  -o  read

重新开一个终端:

write_named_pipe.c

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>int main(int argc, char *argv[])
{int fd;char buf[100];fd = open("my_fifo",  0_WRONLY);if(fd == -1)perror("open");if(argc == 1){printf("please send something to the named pipe :\n");exit(EXIT_FAILURE);}strcpy(buf, argv[1]);write(fd, buf, sizeof(buf));printf("write to the pipe : %s\n", buf);return 0; }

编译:gcc  write_named_pipe.c  -o  write

          ./write hello morning  

总结

有名管道  (可以在任意两个进程之间通信)
无名管道(只能在父子进程之间进行通信)(有一个读端和一个写端,缺一不可)
    管道写端关闭,则读端返回值为0;
    管道读端关闭,则写端会产生异常(会收到信号SIGPIPE)
    管道为空, 那么读会阻塞
    管道写满, 那么写会阻塞

1.有名管道和无名管道的区别:有名管道可以在任意两个进程之间通信;无名管道只能在父子进程之间进行通信
2.写入管道的数据在哪里? 在内存中(不是在磁盘上)
3.管道的通信方式: 半双工(数据可以从a到b,也可以从b到a,但是某一时刻只能选择其中一个)

Linux_2 管道(任务间的通信)相关推荐

  1. 【操作系统】进程间的通信——管道

    进程间的通信-管道 管道 进程间的通信(IPC-Inter-Process Communication)有多种方式,管道是其中最基本的方式. 管道是半双工的,即是单向的. 管道是FIFO(先进先出)的 ...

  2. 进程间的通信IPC(无名管道和命名管道)

    进程间的通信IPC介绍 进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息. IPC的方式通常有管道(包括无名管道和命名管道).消息队列.信号量 ...

  3. 进程间的通信——无名管道

    进程间的通信--无名管道 宗旨:技术的学习是有限的,分享的精神是无限的. 一.进程间的通信 (1)同主机进程间数据交互机制:无名管道(PIPE),有名管道(FIFO).消息队列和共享内存.无名管道多用 ...

  4. 网络编程之 进程间的通信之管道的使用

    如何使用管道是进程间通信的关键 博主先声明一下,关于处理进程创建以及销毁的方法.        "子进程究竟何时终止????调用waitpid函数后还要无休止的等待子进程终止吗???&quo ...

  5. 命名管道(FIFO) Linux进程进程间的通信之命名管道(FIFO)

    Linux进程进程间的通信之命名管道(FIFO) 命名管道(FIFO),它和一般的管道一样.都是作为中间的邮递员来实现两个进程间的通信交流. 命名管道(FIFO)有几个特点: 1.命名管道(FIFO) ...

  6. 2022.8.31 进程中无名管道的特点,无名管道的创建,为何无名管道只能能够实现具有亲缘关系的进程间的通信,以及实现利用无名管道父进程给子进程发送消息的完整代码。

    无名管道通信 无名管道特点: (1):只能用于具有亲缘关系的进程之间的通信.(父子进程或兄弟进程) (2):是一个半双工的通信模式,具有固定的读端和写端.(fd[0]固定为读端,fd[1]固定为写端) ...

  7. 高并发编程-线程通信_使用wait和notify进行线程间的通信

    文章目录 概述 场景 引子 synchronized wait/notify机制 synchronized wait/notify 改造 问题 概述 Java中线程通信协作的最常见的两种方式: syn ...

  8. Linux系统编程(三)进程间的通信

    Linux系统编程(三)进程间的通信 一.为什么需要进程之间的通信(IPC)? 二.管道 1.概念 2.特质 3.原理 4.局限性 5.代码 2.读入数据 三.共享存储映射 注意事项 父子进程通信 一 ...

  9. 进程间的通信之1-----管道

    进程间的通信之1-----管道 1.标准流管道 像文件操作有标准 io 流一样, 管道也支持文件流模式. 用来创建连接到另一进程的管道, 是通过函数 popen 和 pclose. 函数原型: #in ...

最新文章

  1. Ios文件连接dlna服务器,iOS播放多种视频格式,实现DLNA|AirPlay投射盒子总结
  2. python映射类型包括哪三种_python新手入门必备——映射类型相关函数
  3. flex 文字竖排_flex button字竖排展示
  4. ANSI,ASCII,Unicode的区别与联系!
  5. python输出数字方阵_在python里输出数字方阵
  6. 雨中的蚊子为啥不会被雨滴砸死?
  7. Tomcat7.0+的JNDI问题
  8. 学生成绩管理 php,php学生成绩管理系统(模板)
  9. [CSS] 详细解释 @media 属性与 (max-width:) and (min-width) 之间的关系及用法
  10. 暮光之城3蓝光BD高清下载
  11. 上海商业车险进平台验收通过
  12. 【商业】梳理你的商业模式
  13. D. Dirty Deeds Done Dirt Cheap
  14. 如何用ftp上传到服务器视频文件,ftp如何将文件上传到服务器上
  15. fiddler进行弱网测试
  16. 超时任务总结(tradingTask)
  17. matlab生成sinc函数,【 MATLAB 】sinc 函数简介
  18. MySql 循环执行语句,循环执行update,详细介绍【游标嵌套】
  19. 光纤收发器的6个指示灯说明
  20. 计算机如何永久删除文件无法找回,电脑文件永久性删除了怎么办?简单五招教你恢复...

热门文章

  1. 2013年10月高等教育国际金融全国统一命题考试
  2. IP地址和子网掩码的关系, 如何计算网络地址?
  3. easyExcel导出excel
  4. mipsel_24kc的linux内核,选择内核为4.14.195时的编译错误
  5. 基于Matlab模板匹配方法的车牌识别系统设计
  6. 定积分的计算(牛顿-莱布尼茨公式)
  7. 2020NYIST个人积分赛第五场-I
  8. frp使用反向代理实现https协议
  9. Linux 时间同步systemd-timesyncd介绍
  10. 教师计算机教程,中学化学教师计算机教学应用教程——中小学教师计算机教学应用教程...