专栏首页程序员Linux进程间通信——匿名管道

Linux进程间通信——匿名管道

版权声明:本文为博主原创文章,转载请注明博客地址: https://blog.csdn.net/zy010101/article/details/83790850

进程如果不是独立进程,那么它就需要和别的进程进行通信。在进程协作时可以采用共享一个缓冲区的方式来实现。当然,OS的IPC提供了一种机制,以允许不必通过共享地址空间来通信和同步其动作。这就不得不提Linux的的前身Unix。因为Linux一开始就是从这儿借鉴的。加上Linux从一开始就遵守POSIX标准。

Unix最早是由AT&T的贝尔实验室开发的,值得一提的是,在Unix操作系统发展的过程中,产生了许多副产物(POSIX标准也是副产物之一),其中最著名的应当是C语言。是的,它仅仅是个副产物。那个时候Ken Thompson 与Dennis Ritchie感到用汇编语言做移植太过于头痛,他们想用高级语言来完成第三版。后来他们改造了B语言,就形成了今天大名鼎鼎的C语言。这个自发明到现在这个物联网时代仍占据编程语言榜前10的稳固位置。不得不感叹其生命力的强大以及适应性的强大。当然,Ken Thompson 与Dennis Ritchie也是图灵奖得主。

到了1980年,有两个最主要的Unix的版本线,一个是UC Berkeley的BSD UNIX,另一个是AT&T的Unix。至今为止UC Berkeley仍在维护Unix(这学校真牛逼)。

最初的Unix的IPC包括,管道,FIFO,信号。贝尔实验室对Unix早期的进程通信进行了改进,形成了system V这个操作系统的IPC。它包括:system V消息队列,system V信号灯,system V共享内存。当然POSIX IPC也有相应的一套。BSD Unix设计了socket(套接字)通信。这样将进程之间的通信不仅仅限制在单机内。Linux继承了这些。

进程间通信的目的:

  1. 数据传输:一个进程将数据发送给另一个进程
  2. 共享数据:多个进程操作共享数据(比如:售票系统),一个进程对共享数据进行了修改,另外一个进程应该立即看到,(否则票买完了,但是另一边不知道,还在卖)
  3. 通知:一个进程告诉另外一个进程发生了某些事件。
  4. 资源共享
  5. 进程控制:一个进程控制另外一个进程的执行(例如debug程序)。它希望知道另一个进程的实时状态。

Linux进程通信方式:

管道:管道(pipe)分为无名管道和有名管道。无名管道用于具有亲缘关系进程间的通信,有名管道则可以在任意的进程中间进行通信。

管道通信具有以下的特点:

  1. 管道是半双工的。(双向通信的,但是不能同时向双方传输)
  2. 只能用于父子进程或者是兄弟进程之间(就是要具有亲缘关系)
  3. 管道是一种文件(能读写),它只存在于内存之中。他是具有亲缘关系的进程共享的。
  4. 写入的内容每次都添加到管道缓冲区的末尾,并且每次都是从缓冲区的头部读取数据。

Linux建立无名管道函数是pipe函数。它需要的头文件是#include<unistd.h>.

函数原型:int pipe(int filedes[2]);

函数功能:pipe建立一个无名管道文件,若成功返回0,否则返回-1.错误原因由errno给出。管道文件的描述符由filedes数组返回。其中filedes[0]为管道的读取端,filedes[1]为写入端。

代码测试如下:

#include<unistd.h>
#include<errno.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<wait.h>
#include<linux/limits.h>        //这个头文件中有PIPE_BUF


int main()
{
    int filedes[2];         //保存管道文件的文件描述符
    char str[30] = {"Hello World!"};
    char temp[30] = {0};

    if(0 != pipe(filedes))      //创建管道失败
    {
        printf("errno=%d\n",errno);
        return 0;
    }
    if(0 < fork())          //父进程
    {
        close(filedes[0]);      //为避免不必要的错误,关闭读端
        write(filedes[1],str,strlen(str));
        close(filedes[1]);
        wait(NULL);         //回收子进程
        exit(0);
    }
    else
    {
        sleep(3);      //让父进程先执行
        close(filedes[1]);      //为避免不必要的错误,关闭写端
        read(filedes[0],temp,strlen(str));
        close(filedes[0]);
        printf("%s\n",temp);
        exit(0);
    }
    
    return 0;
}

在读写管道文件的时候,最好是严格遵守文件的读写规则,在使用完毕后一定要关闭文件。为了避免不必要的一些错误,在使用管道的文件的要先创建管道文件,然后创建新进程,这样所有的进程才能共享这个管道文件。代码中为了避免向读取端写入和从写入端读取而引发的错误,在读的时候关闭写端,在写的时候关闭读端。

代码中先让父进程向管道文件中写入了字符串“Hello World!”。然后子进程读取管道文件中的字符串,并向屏幕打印。程序执行结果如下:

如果子进程读取到的管道文件为空,那么read()函数将会使得进程阻塞,这时候父进程将会执行,然后完成对管道文件的写入。之后wait()将父进程挂起,子进程完成读取。同样,管道已经满时,进程再试图写管道,在其它进程从管道中移走数据之前,写进程将一直阻塞。(典型的生产者——消费者模型)管道是存在于内存中的文件(实际上内核实现的一个队列),他是进程的资源,会随着进程的销毁而销毁。还有一点是管道中的东西在读取后就会被删除。管道文件有大小限制的,在我现在的内核版本下他是4KB。管道文件的大小由PIPE_BUF描述。它在#include<linux/limits.h>这个头文件中给出。

#define PIPE_BUF        4096	/* # bytes in atomic write to a pipe */

向管道写入数据的时候Linux不保证写入的原子性,管道缓冲区一有空闲,写进程就会去写入。所以要及时读取管道文件

同时管道还要求写端对读端的依赖性,示例代码如下:

#include<unistd.h>
#include<errno.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<wait.h>
#include<linux/limits.h>        //这个头文件中有PIPE_BUF


int main()
{
    int filedes[2];         //保存管道文件的文件描述符
    char str[30] = {"Hello World!"};
    char temp[30] = {0};
    
    int num;
    if (0 < fork()) 
    {
        sleep(1);
        close(filedes[0]);      //关闭读端
       num =  write(filedes[1],str,strlen(str));
       if(-1 == num)
       {
           printf("error!\n");
       }
       else
       {
           printf("write to pipe is %d\n",num);
       }
        close(filedes[1]);
        wait(NULL);
        //exit(0);
    }
    else
    {
        //子进程不读,不写,直接将管道文件两端都关闭
        close(filedes[0]);
        close(filedes[1]);
        exit(0);
    }
    
    return 0;
}

输出结果如下:

这个时候,在父进程中将无法写入。所以管道这个描述还是很形象的,当你向一段水管里面装水的时候,需要将另一端堵上,否则装入的水全都流走了。因此在父进程写的时候,需要先关闭读;在子进程读的时候需要先关闭写。同时,不能在没有读的情况下将管子两头堵上。

当子进程结束的时候,父进程关闭读,调用write写数据,这时候父进程将会收到子进程SIGPIPE信号,当前进程将会中断,而不是阻塞。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Linux文件及文件I/O

    版权声明:本文为博主原创文章,转载请注明博客地址: https://blog.csdn.ne...

    zy010101
  • Linux内核中文件的数据结构和原子操作

    版权声明:本文为博主原创文章,转载请注明博客地址: https://blog.csdn.net/z...

    zy010101
  • Linux下搭建FTP服务器

    版权声明:本文为博主原创文章,转载请注明博客地址: ...

    zy010101
  • Iowait的成因、对系统影响及对策

    什么是iowait? 顾名思义,就是系统因为io导致的进程wait。再深一点讲就是:这时候系统在做io,导致没有进程在干活,cpu在执行idle进程空转,所以说...

    李海彬
  • Elasticsearch基础但非常有用的功能之一:别名

    本文是系列文章第一篇。介绍Elasticsearch的一些非常基础但实战开发确非常有用的技术点。了解这些技术点会帮助你设计更易于维护的数据索引,预先知道PB级大...

    用户1461999
  • Elasticsearch基础但非常有用的功能之一:别名

    本文是系列文章第一篇。介绍Elasticsearch的一些非常基础但实战开发确非常有用的技术点。了解这些技术点会帮助你设计更易于维护的数据索引,预先知道PB级大...

    用户1390885
  • 推荐 | 一款功能强大的子域收集工具

    在渗透测试中信息收集的重要性不言而喻,子域收集是信息收集中必不可少且非常重要的一环,目前网上也开源了许多子域收集的工具,但是总是存在以下部分问题:

    HACK学习
  • Python IDE安装

    今天我就来讲一下Python IDE的安装,Python IDE有很多,Python官网自带的IDE,pycharm,eclipse,sublime,anaco...

    不可言诉的深渊
  • 网络基础知识

    l IP地址 每个IP地址都分割成网络号和主机号两部分,便于IP地址的寻址操作。 那怎么区分网络号和主机号? 子网掩码 这个时候就需要子网掩码,子网掩码...

    苦咖啡
  • 人工智能的“性别”研究

    如果人工智能是人类智能的模拟,那么它模拟的是谁,它有性别吗?无论你认为性别是由一个人所处环境和所受文化构成的社会结构,还是从本质论角度来看的生物性决定因素,亦或...

    AiTechYun

扫码关注云+社区

领取腾讯云代金券

玩转腾讯云 有奖征文活动