天天看点

Linux - 管道 进程间通信-管道 一、管道简述二、在shell中使用管道三、pipe管道四、fifo有名管道

进程间通信-管道

一、管道简述

  • 管道是Unix中最古老的

    进程间通信

    的形式。
  • 我们把从一个进程连接到另一个进程的一个数据流称为一个“

    管道

  • 我们通常把是把一个进程的

    输出

    连接或“

    管接

    ”(经过管道来连接)到另一个进程的

    输入

  • 管道实现

    通信原理

    • 每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不 到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)。
    • Linux - 管道 进程间通信-管道 一、管道简述二、在shell中使用管道三、pipe管道四、fifo有名管道

二、在shell中使用管道

  • 链接shell命令

    :把一个进程的输出直接馈入另一个的输入,命令格式如下
command1 | command2 | command3
           

操作符是:”

|

”,它只能处理经由前面一个指令传出的

正确输出信息

,对

错误信息

没有直接处理能力。然后,

传递

给下一个命令,作为

标准的输入

.

管理命令的输出说明:

Linux - 管道 进程间通信-管道 一、管道简述二、在shell中使用管道三、pipe管道四、fifo有名管道

【指令1】 正确输出,作为 【指令2】的输入 然后**【指令2】的输出作为【指令3】的输入 ,【指令3】输出就会直接显示在屏幕**上面了。

通过管道之后【指令1】和【指令2】的正确输出不显示在屏幕上面

【提醒注意】:

  1. 管道命令只处理前一个命令正确输出,不处理错误输出;
  2. 管道命令右边命令,必须能够接收标准输入流命令才行;

举例应用:

  1. 读出logcat.log文件的内容,通过管道转发给grep作为输入内容
cat logcat.log | grep –n ‘ActivitManager’
           
  1. who|grep tty|wc –l 显示从控制台登录的人数
who|grep tty|wc –l 
           
Linux - 管道 进程间通信-管道 一、管道简述二、在shell中使用管道三、pipe管道四、fifo有名管道

三、pipe管道

3.1- pipe函数说明

管道是一种最基本的IPC机制,可由pipe函数创建:

#include <unistd.h>
int pipe(int filedes[2]);
           
  • pipe管道

    作用于

    有血缘关系

    的进程之间,通过

    fork

    来传递
  • 调用

    pipe函数

    时在

    内核

    中开辟一块

    缓冲区(称为管道)

    用于通信,它有一个读端一个

    写端,然后通过

    filedes参数

    传出给用户程序两个

    文件描述符

    ,filedes[0]指向管道的读

    端,filedes[1]指向管道的写端(很好记,就像0是标准输入1是标准输出一样)。所以管道 在用户程序看起来就像一个打开的文件,通过

    read(filedes[0]);

    或者

    write(filedes[1]);

    向这个文件读写数据其实是在读写内核缓冲区。
  • pipe函数

    调用成功返回 ,调用失败返回

    -1

3.2- pipe函数实现通信的原理

Linux - 管道 进程间通信-管道 一、管道简述二、在shell中使用管道三、pipe管道四、fifo有名管道
  • 1.

    父进程

    调用pipe开辟管道,得到两个

    文件描述符

    指向

    管道的两端

  • 2.父进程调用

    fork

    创建

    子进程

    ,那么子进程也有两个文件描述符指向同一

    管道

  • 3.

    父进程关闭管道读端,子进程关闭管道写端

    。父进程可以往管道里写,子进程可以从管道里读,管道是用

    环形队列

    实现的,数据从写端流入从读端流出,这样就实现了

    进程间通信

3.3-应用示例: 父进程写,子进程读

#include <unistd.h>
#include <stdio.h>
int main()
{
	int fd[2];
	pid_t pid;
	char str_write[1024] = "hello, I am content.";
	char str_read[1024] = {0};

	//创建管道,参数fd作为要传出去的两个文件描述符 
	if(pipe(fd) < 0)
	{
		//创建管道失败
		perror("pipe");
		exit(1);	
	}
	
	//创建子进程 
	pid = fork();
	
	//fd[0] 读端 , fd[1] 写端
	
	//父进程 
	if(pid > 0)
{
		close(fd[0]);//父进程 关闭读端 
		write(fd[1],str_write,strlen(str_write));
		wait(NULL);//立即阻塞父进程,等待子进程退出 
	}
	//子进程 
	else if (pid == 0)
	{
		int len;
		close(fd[1]);//子进程 关闭写端 
		len = read(fd[0],str_read,sizeof(str_read));
		write(STDOUT_FILENO,str_read,len);//把str_read输出到终端 
	 } 
	
	return 0;
}
           

3.4- 管道读写规则

  • 如果试图

    从管道写端读取数据

    ,或者向管道读端写入数据都将导致错误发生
  • 没有数据可读时

    ,read调用就会阻塞,即进程暂停执行,一直等到有数据来到为止。

    如果管道的

    另一端已经被关闭

    ,也就是没有进程打开这个管道并向它写数据时,read调用就会阻塞
  • 如果管道的

    写端不存在

    ,则认为已经读到了数据的末尾,读函数返回的读出字节数为0;
  • 当管道的

    写端存在

    时,如果请求的字节数目大于PIPE_BUF,则返回管道中现有的数据字节数,如果请求的字节数目不大于PIPE_BUF,则返回管道中现有数据字节数(此时,管道中数据量小于请求的数据量);或者返回请求的字节数(此时,管道中数据量不小于请求的数据量)。
  • 向管道中

    写入数据

    时,linux将不保证写入的

    原子性

    ,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞。

四、fifo有名管道

4.1-fifo有名管道简述

  • 匿名管道

    应用的一个限制就是只能在具有

    共同祖先

    (具有

    亲缘关系

    )的进程间通信。

    如果我们想在

    不相关

    的进程之间交换数据,可以使用

    FIFO文件

    来做这项工作,它经常被称为

    命名管道

  • 命名管道可以从命令行上创建,命令行方法是使用下面这个命令:
    • $ mkfifo filename
                 
  • 命名管道也可以从程序里创建,相关函数有:
  • int mkfifo(const char *filename,mode_t mode);
     	```
               
  • mkfifo

    创建的

    FIFO文件

    的文件属性的第一位是

    p

Linux - 管道 进程间通信-管道 一、管道简述二、在shell中使用管道三、pipe管道四、fifo有名管道

4.2- FIFO的打开规则:

  • 如果当前打开操作是为

    而打开FIFO时,若已经有相应进程为

    而打开该FIFO,则当前打开操作将成功返回;否则,可能

    阻塞直到有相应进程为写而打开该FIFO

    (当前打开操作设置了阻塞标志);或者,成功返回(当前打开操作没有设置阻塞标志)。
  • 如果当前打开操作是为

    而打开FIFO时,如果已经有相应进程为

    而打开该FIFO,则当前打开操作将成功返回;否则,

    可能阻塞直到有相应进程为读而打开该FIFO

    (当前打开操作设置了阻塞标志);或者,返回ENXIO错误(当前打开操作没有设置阻塞标志)。

4.3-应用示例

用来写FIFO文件:fifo_w.c

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
// write
void sys_err(char *str,int exitno)
{
	perror(str);
	exit(exitno);
}

int main(int argc, char*argv[])
{
	int fd;
	char buf[1024] = "hello world\n";
	if(argc < 2)
	{
		printf("enter fifoname\n");
		exit(1);
	}
	fd = open(argv[1],O_WRONLY);
	if(fd<0)
	{
		sys_err("open",1);
	}
	write(fd,buf,strlen(buf));
	close(fd);
	return 0;
}
           

用来读FIFO文件:fifo_r.c

#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<stdio.h>
#include<unistd.h>

//read

void sys_err(char *str,int exitno)
{
        perror(str);
        exit(exitno);
}

int main(int argc, char*argv[])
{
        int fd;
        char buf[1024];
        int len;
	 if(argc < 2)
         {
                printf("enter fifoname\n");
                exit(1);
         }
        fd = open(argv[1],O_RDONLY);
        if(fd<0)
         {
                sys_err("open",1);
         }
        len= read(fd,buf,sizeof(buf));
        write(STDOUT_FILENO,buf,len);

	 close(fd);
        return 0;
}

           

终端上测试:

1.创建FIOF文件

Linux - 管道 进程间通信-管道 一、管道简述二、在shell中使用管道三、pipe管道四、fifo有名管道

2.编译文件

Linux - 管道 进程间通信-管道 一、管道简述二、在shell中使用管道三、pipe管道四、fifo有名管道

3.在

终端1

上执行

fifo_r

(用来读 创建的 fifo1文件中的数据)

但是现在没有进程为

而打开该fifo1,所以终端1

被阻塞

Linux - 管道 进程间通信-管道 一、管道简述二、在shell中使用管道三、pipe管道四、fifo有名管道

4.打开终端2,执行

fifo_w

(用来向 创建的 fifo1文件中写入数据)

Linux - 管道 进程间通信-管道 一、管道简述二、在shell中使用管道三、pipe管道四、fifo有名管道

5.此时切换回

终端1

,可以看到已经读到数据

Linux - 管道 进程间通信-管道 一、管道简述二、在shell中使用管道三、pipe管道四、fifo有名管道

继续阅读