• IPC之---管道


    进程间通信———管道

    什么是进程间通信

    进程间通信(IPC,Interprocess communication)是一组编程接口,让程序员能够协调不同的进程,使之能在一个操作系统里同时运行,并相互传递、交换信息。这使得一个程序能够在同一时间里处理许多用户的要求。因为即使只有一个用户发出要求,也可能导致一个操作系统中多个进程的运行,进程之间必须互相通话。举个例子来说,也就是说用一些方法让两个本来不能见面的人见面。IPC接口就提供了这种可能性。那么不同进程之间存在着什么双方都可以访问的介质呢?进程的用户空间是互相独立的,一般而言是不能互相访问的,唯一的例外是共享内存区。另外,系统空间是“公共场所”,各进程均可以访问,所以内核也可以提供这样的条件。下面一副图来说明进程间的通信。
    这里写图片描述

    进程间通信的方法:主要包括管道, 系统IPC(包括消息队列,信号,共享存储), 套接字(SOCKET).(下面主要介绍管道,匿名管道和命名管道)

    管道包括以下三种:

    <1>普通管道PIPE,特点就是:

    1.单向通信。
    2.只有在具有亲缘关系的进程间通信(父子,兄弟)。
    3.具有同步机制。(写什么,读什么)
    4.他是一种面向字节流的通信服务。
    5.生命周期随进程(也就是说,当与之相关的进程退出时,管道内申请的空间不管有没有释放系统都会释放它;就是说只要进程退出了,管道也就随之退出。比如,在管道中申请了堆空间,只申请不释放,当管道关闭,也就是进程退出了,系统就会自动是放那些空间。)

    <2>流管道s_pipe: 特点:可以双向传输.其它与普通管道相同。
    <3>命名管道:name_pipe,可以在许多并不相关的进程之间进行通讯.其它与普通管道相同。

    匿名管道

    由pipe函数创建

    #include<stdio.h>
    int pipe(int fileds[2]);

    调用pipe函数时在内存中开辟一块缓冲区(就称为管道),用于通信,它有一个读端和一个写端,通过函数参数传给用户程序两个文件描述符,fileds[0]指向管道的读端,fileds[1]指向管道的写端。(方便记忆就可以理解为,标准输入0,标准输出1)看起来管道就像是一个打开的文件,
    通过read(fileds[0]),和write(fileds[1])往里面写和读, 从上面的图我们可以看出,也就是在读写内核缓冲区。
    pipe函数创建管道成功返回0,失败返回-1。

    那么管道创建好了,它到底是怎么进行通信的呢?下面先用图片来说明一下。
    这里写图片描述

    #include<stdio.h>
     #include<unistd.h>
     #include<errno.h>
     #include<string.h>
     int main()
     {
         int _pipe[2];//定义参数
         int ret = pipe(_pipe);
         if(ret==-1)//创建管道失败
         {
             printf("creat pipe error!errno code is :%d
    ",errno);//错误码
             return 1;//返回值,这样你就会知道到底是哪里出现了错误
         }
         pid_t id = fork();
         if(id<0)//创建子进程失败
         {
             printf("fork error!");
             return 2;
         }
         else if(id==0)
         {
             //child
             close(_pipe[0]);//关闭读端
             int i = 0;
             char *_mesg = NULL;
             while(i<100)
             {
                 _mesg = "I am child!";
                 write(_pipe[1],_mesg,strlen(_mesg)+1);//xie
                 sleep(1);
                 i++;
             }
         }
         else
         {
             //father
             close(_pipe[1]);//关闭写端
             char _mesg_c[100];
             int j = 0;
             while(j<100)
             {
                 memset(_mesg_c,'',sizeof(_mesg_c));
                 read(_pipe[0],_mesg_c,sizeof(_mesg_c));
                 printf("%s
    ",_mesg_c);
                 j++;
             }
         }
     }

    运行结果如下图
    这里写图片描述

    这样就实现了进程间通信。(单向通信)父进程读,子进程写。
    使用管道需要注意以下四种特殊情况(假设都是阻塞I/O操作,没有设置O_NONBLOCK标志)
    1. 如果所有指向管道写端的文件描述符都关闭了,(管道写端的引用计数为0),而仍然有进程从管道的读端读取数据,那么管道中剩余的数据都被读取之后,再次read将会返回0,就像读到文件结尾一样。也就是说,写端不会写,读端读完之后就会再等着写端去写,但是写端关闭了啊,不会写了,所以就出现上面说的情况。这就体现出了管道的同步机制。
    2. 如果有指向管道写端的文件描述符没有关闭,(管道写端的引用计数大于0)而持有管道写端的进程也没有向管道中写数据,这时有进程管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。通俗讲就是,读端读数据,一直读,但是写端不写了,而且写端并没有关闭,所以这时读端就会一直等着写端去写。这就造成了阻塞式等待。
    3. 如果所有指向管道读端的文件描述符都关闭了(管道读端的引用计数为0),这时有进程向管道的写端写数据,那么该进程会收到SIGPIPE,通常会导致进程异常终止。所以进程就会异常退出了。
    4. 如果有指向管道读端的文件描述符没关闭(管道读端的引用计数大于0)而持有管道读端的进程也没有从管道中读取数据,这时有进程向管道写端写数据,那么在管道写满时再写将会阻塞,直到管道中有了空位置才写入并返回,也就是管道的同步机制。

    命名管道(FIFO)

    命名管道(NamedPipe)是服务器进程和一个或多个客户进程之间通信的单向或双向管道。不同于匿名管道的是:命名管道可以在不相关的进程之间和不同计算机之间使用,服务器建立命名管道时给它指定一个名字,任何进程都可以通过该名字打开管道的另一端,根据给定的权限和服务器进程通信。而且,FIFO总是按照先进先出的原则工作,第一个被写入的数据首先从管道中读出。
    命名管道的创建:

    #include<sys/types.h>
    #include<sys/stat.h>
    int mknod(const char*path,mode_t mod,dev_t dev);
    int mkfifo(const char*path,mode_t mode);

    函数参数中的path为创建的命名管道的路径名,mod为创建命名管道的模式,指明其存取权限,dev为设备值,该值文件创建的种类,它只在创建设备文件时才会用到。这两个函数带哦用成功返回0,失败都返回-1.线面用mknod函数创建一个命名管道

    umask(0);//重置管道的存取权限
    if(mknod("/tmp/fifo",S_IFIFO|0666)==-1)
    {
        perror("mknod error");
        exit(1);
    }
    //函数mkfifo的使用代码
    umask(0);
    if(mkfifo("/tmp/fifo",S_IFIFO|0666)==-1 )
    {
        perror("mkfifo error");
        exit(1);
    }
    //"S_IFIFO|0666"致命创建一个管道的存取权限为0666
    

    命名管道的使用和匿名管道基本相同,只是在使用命名管道之前首先要使用open函数打开,因为命名管道是存在于硬盘上的文件,而管道是存在于内存中的特殊文件。
    需要注意,使用open的几点:
    1. 调用open()打开命名管道可能会被阻塞,但是如果同时用读写方式(O_RDWR)打开,则一定不会造成阻塞。
    2. 如果以制度方式(O_RDONLY)打开,则调用open()函数的进程将会被阻塞直到有写才能打开管道。
    3. 同样,以写方式(O_WRONLY)打开也会阻塞直到有读方式打开管道。

    命名管道可以在不同的进程间通信,形象点讲就像我们平时使用的聊天工具一样,一个写一个读。下面用代码实现。

    //client.c 也就是 管道的写端
    #include<stdio.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<unistd.h>
    #include<fcntl.h>
    #include<string.h>
    int main()
    {
        umask(0);
        if(mkfifo("./mypipe",0666 | S_IFIFO)<0)
        {
            perror("mkfifo error");
            return 1;
        }
        int fd = open("./mypipe",O_RDONLY);
        if(fd<0)
        {
            printf("open file error!
    ");
            return 2;
        }
        char buf[1024];
        while(1)
        {
            ssize_t ret = read(fd,buf,sizeof(buf)-1);
             if(ret>0)//error or end of file
                 {
                     buf[ret] = 0;
                     printf("client say# %s
    ",buf);
                 }
             else if(ret==0)
             {
                 printf("client quit !server begin quit!
    ");
                 break;
         }
    
       }
        close(fd);
        return 0;
    }
    
    //server.c 也就是管道的读端
    #include<stdio.h>
     #include<sys/types.h>
     #include<sys/stat.h>
     #include<unistd.h>
     #include<fcntl.h>
     #include<string.h>
     int main()
     {
         int fd = open("./mypipe",O_WRONLY);
         if(fd<0)
         {
             printf("open file error!
    ");
             return 2;
         }
         char buf[1024];
         while(1)
         {
             printf("please enter # ");
             fflush(stdout);
             ssize_t ret = read(0,buf,sizeof(buf)-1);
              if(ret>0)
                  {
                      buf[ret-1] = 0;
                      write(fd,buf,strlen(buf));
                  }
    
        }
         close(fd);
         return 0;
     }
    

    下面是运行结果
    这里写图片描述
    运行就可以看到实现了进程间的通信。

  • 相关阅读:
    『笔记』数学数论(八)
    『笔记』BSGS
    『笔记』组合数学(六)
    01 分数规划
    高斯消元
    拉格朗日插值法
    洛谷网课数论
    [IOI2013]robots 机器人
    P3530 [POI2012]FES-Festival
    NOIP 2015 day1
  • 原文地址:https://www.cnblogs.com/chan0311/p/9427347.html
Copyright © 2020-2023  润新知