Linux--进程间通信--管道及管道demo--问答--1119--1126

1.进程间通信

进程间通信的方法有:管道System V进程间通信POSIX进程间通信

管道:匿名管道、命名管道。

System V进程间通信: System V消息队列、System V共享内存、System V信号量。

POSIX进程间通信:消息队列、共享内存、信号量、互斥量、条件变量、读写锁。

进程间通信可以通过1.内核中的缓冲区 2.文件 3.网络通信的方式实现。

1.1进程间通信的本质理解

进程之间具有独立性,拥有自己的地址空间,因此无法通过各自的虚拟地址进行通信。

  • 前提:让不同的进程看到同一块“内存”(特定的结构组织)。
  • 所谓的进程看到同一块“内存”,这个“内存”不能隶属于任何一个进程,而应该更强调共享。

1.2管道

计算机领域设计者,设计了一种单向通信的方式——管道。

1.2.1管道的原理

  • 分别以读写的方式打开同一个文件
  • fork()创建子进程
  • 双方进程各自关闭自己不需要的文件描述符。

 

 父进程创建子进程时,将自己的pcb结构体拷贝一份给子进程,从而让父子进程都能看到同一份资源(内存),因为files_struct结构体中的fd_arraryp[],所以打开的文件无需拷贝一份。管道也是文件,这整个过程属于内存级别的操作,不需要向磁盘进行操作。

1.2.2匿名管道通信demo代码演示

int pipe(int pipefd[2]);

pipefd[2] 是输出型参数 会将传入的数组赋值成文件描述符

#include <assert.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <string>

using namespace std;
int main()
{
    //1、创建管道
    int pipefd[2]={0};//默认是 pipefd[0]为 读入端 pipefd[1]为写入端 放的是文件描述符
    int n=pipe(pipefd);
    assert(n!=-1);
    (void)n;//assert在release端下没用 为了防止n只定义未使用而在release下报错
    //cout<<"pipefd[0]:"<<pipe[0]<<endl<<"pipefd[1]:"<<pipefd[1]<<endl;  文件描述符为 3 和 4

    //2、创建子进程
    pid_t id=fork();
    assert(id!=-1)
    if(id==0)
    {
        //子进程 --- 读
        //3、构建单向通信的通道,父进程写入,子进程读取。
        //3.1 关闭子进程不需要的fd
        close(pipefd[1]);

        //
        char buffer[1024];
        while(true)
        {
            ssize_t s=read(pipefd[0],buffer,sizeof(buffer)-1);
            if(s>0)
            {
                buffer[s]=0;//因为系统调用的Read不添加\0 我们手动添加
                cout<<"Father#"<<buffer<<endl;
            }
        }

        exit();
    }
    //父进程
    close(pipefd[0]);
    string message="现在是父进程,正在发送消息";
    int count=0;
    char sendbuffer[1024];
    while(true)
    {
        //构建一个变化的字符串
        snprintf(sendbuffer,sizeof(sendbuffer),"%s:%d",message.c_str(),count);

        //写入
        write(pipefd[1],sendbuffer,strlen(sendbuffer));
        sleep(1);
    }
    pid_t ret=waitpid(id,nullptr,0);
    assert(ret<0);
    (void)ret;
    close(pipefd[1]);

    return 0;
}

 1.2.3管道的读写规则

当没有数据可读时,read调用阻塞,进程跟着停止,一直等到有数据来到为止。

当管道写满时(管道也是文件,有大小),write调用阻塞,直到有进程读走数据。

如果管道写入端对应的文件描述符被关闭,则read会把数据全部读完,最后返回0。

如果管道读入端对应的文件描述符被关闭,则操作系统会终止写入端的程序。

1.3管道的特点

  • 管道是用来进行具有血缘关系的进程间进程通信——父子进程
  • 管道具有通过让进程间协同的作用,提供了访问控制——比如读写规则。管道自带同步(没有数据读阻塞,缓冲区写满写阻塞)与互斥。(显示器也是一个文件,但父子进程同时向其进行打印操作时,不会有阻塞——缺乏访问控制)
  • 管道提供的是面向流式的通信服务——面向字节流——协议
  • 管道是基于文件的,文件的生命周期是随进程的,管道的生命周期是随进程的
  • 管道是单向通信的,是半双工通信的一种特殊情况。(半双工:“要么读,要么写”)

2.命名管道文件

int mkfifo(const char *filename,mode_t mode);

 使用mkfifo系统调用接口可以创建一个管道文件,参数(文件路径,管道文件的权限)

创建失败返回-1

 删除管道文件

unlink(const char* filename);

2.1命名管道的原理

 我们程序一般可能是用于向磁盘读取或写入,如果我们每次操作都进行一次IO操作将临时文件刷新到磁盘上,过程会非常慢效率低,于是操作系统就在磁盘上创建了一种文件,叫做管道文件。

管道文件:可以被打开,但是不会将内存数据进行刷新到磁盘,称为命名管道文件

特点:有名字,该文件一定在系统路径中。

路径具有唯一性。所以双方进程就可以通过路径看到同一份资源。

2.2匿名管道与命名管道的区别

让不同进程看到同一份文件的手段不同

2.3文件管道通信demo

comm.hpp

中定义了几个宏常量,定义了管道文件的filename

server.cc

#include "comm.hpp"
int main()
{
    //1.创建管道文件
    if(mkfifo(ipcPath.c_str(),MODE)<0)
    {
        perror("mkfifo");
        exit(1);
    }
    Log("创建管道文件成功",Debug)<<"step 1"<<endl;
    //2.正常的文件操作
    int fd=open(ipcPath.c_str(),O_RDONLY);
    if(fd<0)
    {
        perror("open");
        exit(2);
    }
    Log("打开管道文件成功",Debug)<<"step 2"<<endl;
    //3.编写正常的通信代码
    char buffer[SIZE];
    while(true)
    {
        memset(buffer,'\0',sizeof(buffer));
        ssize_t s=read(fd,buffer,sizeof(buffer)-1);//读取到内存要注意最后一个是/0
        if(s>0)
        {
            cout<<"client say:  "<<buffer<<endl;
        }
        else if(s==0)
        {
            //读到了文件结尾
            cerr<<"read end of file, client quit ,server quit too"<<endl;
            break;
        }
        else
        {
            //读取失败
            perror("read");
            break;
        }

    } 
    //4.关闭文件
    close(fd);
    Log("关闭管道文件成功",Debug)<<"step 3"<<endl;
    unlink(ipcPath.c_str());//通信完毕  就删除管道文件
    Log("删除管道文件成功",Debug)<<"step 4"<<endl;
    return 0;
}

client.cc

#include "comm.hpp"
int main()
{
    //1、读取管道文件
    int fd=open(ipcPath.c_str(),O_WRONLY);
    if(fd<0)
    {
        //打开失败
        perror("open");
        exit(1);
    }
    //2.ipc过程
    string buffer;
    while(true)
    {
        cout<<"Please Enter Message Line :>";
        getline(cin,buffer);//命名写到了buffer里面
        write(fd,buffer.c_str(),sizeof(buffer));//写到文件里 这里不用-1 因为文件里面不看/0
    }
    //3.关闭
    close(fd);
    return 0;
}

运行结果

3.问答

1.匿名管道只能用于具有亲缘关系的进程间通信,命名管道可以用于同一主机上的任意进程间通信

2.使用int pipe(int pipefd[2])接口创建匿名管道,pipefd[0]用于从管道读取数据,pipefd[1]用于向管道写入数据。

3.匿名管道需要在创建子进程之前创建,因为只有这样才能复制到管道的操作句柄,与具有亲缘关系的进程实现访问同一个管道通信。

4.管道的本质是内核中的缓冲区,通过内核缓冲区实现通信,命名管道的文件虽然可见于文件系统,但是只是标识符,并非通信介质。

5.管道特性:半双工通信,自带同步与互斥,生命周期随进程,提供字节流传输服务。

6.多个进程只要能够访问同一管道就可以实现通信,不限于读写个数

7.在同步的体现中,若管道所有写段关闭,则从管道中读取完所有数据后,继续read会返回0,不再阻塞;若所有读端关闭,则继续write写入会触发异常导致进程退出。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值