linux操作系统之进程间通信 --管道

Linux环境下,进程地址空间相互独立,每个进程各自有不同的用户地址空间。任何一个进程的全局变量在另一个进程中都看不到,所以进程和进程之间不能相互访问要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)。

在进程间完成数据传递需要借助操作系统提供特殊的方法,如:文件、管道、信号、共享内存、消息队列、套接字、命名管道等。随着计算机的蓬勃发展,一些方法由于自身设计缺陷被淘汰或者弃用。现今常用的进程间通信方式有:
① 管道 (使用最简单)
② 信号 (开销最小)
③ 共享映射区 (无血缘关系)
④ 本地套接字 (最稳定)

管道
未名管道(匿名管道)
管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。有如下特质:
1. 其本质是一个伪文件(实为内核缓冲区)
linux文件类型:- 文件 d 目录 l 符号链接
s 套接字 b块设备 c 字符设备 p 管道 (这四种都是伪文件)

  1. 由两个文件描述符引用,一个表示读端,一个表示写端。
  2. 规定数据从管道的写端流入管道,从读端流出。
    管道的原理: 管道实为内核使用环形队列机制,借助内核缓冲区(4k)实现。
    管道的局限性:
    ① 数据自己读不能自己写。
    ② 数据一旦被读走,便不在管道中存在,不可反复读取。
    ③ 由于管道采用半双工通信方式。因此,数据只能在一个方向上流动。
    只能在有公共祖先的进程间使用管道。(有血缘关系的进程)
    常见的通信方式有,单工通信、半双工通信、全双工通信。

pipe函数
创建管道
int pipe(int pipefd[2]); 成功:0;失败:-1,设置errno
函数调用成功返回r/w两个文件描述符。无需open,但需手动close。规定:fd[0] → r; fd[1] → w,就像0对应标准输入,1对应标准输出一样。向管道文件读写数据其实是在读写内核缓冲区。

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

int main()
{
    int fd[2];
    int ret =0;
    ret = pipe(fd);
    pid_t pid;
    if(ret == -1)
    {
            perror("pipe error.\n")
            exit(1);
    }
    pid = fork();
    if(pid == -1)
    {
            perror("fork error.\n");
            exit(1);
    }
    else if(pid ==0)
    {
            close(fd[1]);//子进程负责读,所以关闭写的描述符
            char buf[1024];
            ret = read(fd[0],buf,sizeof(buf));//从管道读数据
            if(ret == 0)
            {
                    printf("end.\n");
            }
            write(STDOUT_FILENO,buf,ret);//写到屏幕
    }
    else
    {
            close(fd[0]);//父进程负责写,所以关闭读的描述符
            char *str ="hello ,pipe.";
            write(fd[1],str,strlen(str));
    }
    return  0;
}   

FIFO
命名管道
FIFO常被称为命名管道,以区分管道(pipe)。管道(pipe)只能用于“有血缘关系”的进程间。但通过FIFO,不相关的进程也能交换数据。
FIFO是Linux基础文件类型中的一种。但,FIFO文件在磁盘上没有数据块,仅仅用来标识内核中一条通道。各进程可以打开这个文件进行read/write,实际上是在读写内核通道,这样就实现了进程间通信。
创建方式:
1. 命令:mkfifo 管道名
2. 库函数:int mkfifo(const char *pathname, mode_t mode); 成功:0; 失败:-1
一旦使用mkfifo创建了一个FIFO,就可以使用open打开它,常见的文件I/O函数都可用于fifo。如:close、read、write、unlink等。

有名管道的打开规则:
有名管道比无名管道多了一个打开操作:open

FIFO的打开及读写规则:
一、对于FIFO,需要open去打开FIFO的读端或是写端的描述符。
1> 如果open的时候没有指定O_NONBLOCK标志,且open的是读端时
如果不存在此FIFO的已经打开的写端时,open会一直阻塞到有FIFO的写端打开;
如果已经存在此FIFO的打开的写端时,open会直接成功返回。

2> 如果open的时候没有指定O_NONBLOCK标志,且open的是写端时
如果不存在此FIFO的已经打开的读端时,open会一直阻塞到有FIFO的读端打开;
如果已经存在此FIFO的打开的读端时,open会直接成功返回。

二、从FIFO或者空管道读写
1> read时,读端fd没有指定O_NONBLOCK标志
如果存在此FIFO或管道的已经打开的写端时,阻塞到FIFO或管道中有数据或者FIFO或管道的已经打开的写端全部被关闭为止。
如果不存在此FIFO或管道的已经打开的写端时,read返回0;
2> write时, 同read差不多,就不详述了。

猜你喜欢

转载自blog.csdn.net/weixin_40878579/article/details/80045775