Linux进程间通信(1)管道

管道

  • 管道可以用来在两个进程之间传递数据,如: ps -ef | grep “bash”, 其中‘|’就是管道,其作用就是将 ps 命令的结果写入管道文件,然后 grep 再从管道文件中读出该数据进行过滤。

    你可以用管道操作符I来连接进程。Linux与MS-DOS不同,在Linux下通过管道连接的进程可以同时运行,并且随着数据流在它们之间的传递可以自动地进行协调。举一个简单的例子,你可以使用sort命令对ps命令的输出进行排序。

$ ps >psout.txt
$ sort psout.txt >pssort.out

一个更精巧的解决方案是用管道来连接进程,如下:

$ ps | sort>passort.out

如果想在屏幕上分页显示输出结果,可以再连接第三个进程more,将它们都放在同一个命令行,如下:

$ ps|sort|more

允许连接的进程数目是没有限制的。假设你想看看系统中运行的所有进程的名字,但不包括shell本身,可以使用下面的命令:

$ ps -xo comm |sort |uniq |grep -v sh |more

这个命令首先按字母顺序排序ps命令的输出,再用uniq命令去除名字相同的进程,然后用grep -v
sh命令删除名为sh的进程,最终将结果分页显示在屏幕上。与使用一系列单独的命令并且每个命令都带有自己的临时文件相比,这是一个更精巧的解决方案。但这里有一点 需要引起注意:如果你有一系列的命 令需要执行,相应的输出文件是在这组命令被创建的同时立刻被创建或写入的,所以决不要在命令流中重复使用相同的文件名。如果尝试执行以下命令:

cat mydata.txt |sort |uniq > mydata.txt

最终将得到一个空文件,因为你在读取文件mydata. txt之前就已经覆盖了这个文件的内容。

(1)有名管道

 有名管道可以在任意两个进程之间通信
 有名管道的创建:

◼ 命令创建: mkfifo FIFO
◼ 系统调用创建

在这里插入图片描述

#include <sys/types.h>
#include <sys/stat.h>
-int mkfifo( const char *filename, mode_t mode);
//filename 是管道名 mode 是创建的文件访问权限

-代码演示

  • 第一步:先创建一个有名管道文件——fifo,两个.c文件——write.c和read.c(分别对这个管道文件进行“读”和“写”)。
  • 第二步:在write.c中,以“只读”的形式打开有名管道文件“fifo”,打印打开fifo文件得到的文件描述符—-fd,然后不断地将从标准输入——stdin中的内容放入数组——buff中,将buff中的内容write写入与文件描述符fd有关的文件“fifo”中,并且当buff中的前三个字符为“end”时,停止从标准输入中读取内容,直接关闭写端。
  • 第三步:在read.c中,以“只读”的形式打开管道文件“fifo”,打印得到的文件描述符——fd,然后将从文件“fifo”中读取的内容放入数组——buff中,再打印到标准输出中,如果读“fifo”时再没有什么数据可读(即写端没有再往文件“fifo”中写入内容),则打印“对方(写端)已关闭”,再跳出关闭读端。

write.c写端:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <fcntl.h>
 int main()
 {
    
    
 int fd = open("FIFO", O_WRONLY);
 assert(fd != -1);
 printf("open FIFO success\n");
 while(1)
 {
    
    
 printf("please input: ");
 char buff[128] = {
    
    0};
 fgets(buff, 128, stdin);
 write(fd, buff, strlen(buff) - 1);
  if(strncmp(buff, "end", 3) == 0)
   {
    
    
      break;
   }
 }
 close(fd);
 exit(0);
 }

read.c读端:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <fcntl.h>
 int main()
 {
    
    
 int fd = open("FIFO", O_RDONLY);
 assert(fd != -1);
 printf("open FIFO success\n");
 while(1)
 {
    
    
 char buff[128] = {
    
    0};
 int n = read(fd, buff, 127);
 if(n <= 0 || 0 == strncmp(buff, "end", 3))
    {
    
    
       break;
    }
 printf("%s\n", buff);
  }
 close(fd);
 exit(0);
 

./write
在这里插入图片描述
./read
在这里插入图片描述
总结:

  • 1.管道有读端和写端之分,当写端关闭之后,读端read就会返回0,并且随后也关闭。
  • 2.阻塞:当读端没有数据可读时,read调用通常会阻塞,即它将暂停进程(读端进程)来等待直到有数据到达为止。但是,这种阻塞没有多大用处,所以read调用就会返回0,写端程序中的“if(n==0)及之后的语句”都是来应对这种阻塞的。

(2)无名管道

无名管道主要应用于父子进程间的通信。
无名管道的创建:
在这里插入图片描述

#include <unistd.h>
 /*
pipe()成功返回 0,失败返回-1
fds[0]是管道读端的描述符
 fds[1]是管道写端的描述符
*/
int pipe( int fds[2]);

代码演示:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>

int main()
{
    
    
   int fd[2];

 int res = pipe(fd);
 assert( res != -1 );
 pid_t pid = fork();
 assert( pid != -1 );

  if( pid == 0 )
 {
    
    
 char buff[128] = {
    
    0};
 read(fd[0], buff, 127);
 printf("child read: %s\n", buff);
 }
 else
 {
    
    
 write(fd[1], "hello", 5);
 }

 close(fd[0]);
 close(fd[1]);
 exit(0);
 }

在这里插入图片描述

3、 管道的特点
◼ 无论有名还是无名,写入管道的数据都在内存中
◼ 管道是一种半双工通信方式(通信方式有单工、半双工、全双工)
◼ 有名和无名管道的区别:有名可以在任意进程间使用,而无名主要在父子进程间
管道的实现:
在这里插入图片描述

Guess you like

Origin blog.csdn.net/weixin_48560325/article/details/121867384