Linux——进程间通信

本文详细介绍了Linux系统中进程间通信的各种方式,包括管道(匿名管道和命名管道)、System V共享内存、信号量以及进程互斥等。通过实例代码演示了如何使用这些通信方式,强调了进程间通信的重要性以及其在多进程协同工作中的作用。
摘要由CSDN通过智能技术生成

前言

进程间通信的必要性:
之前写的代码都是单进程的。是无法使用并发能力,并且无法实现多进程协同
传输数据,同步执行流,消息通知等
进程间通信不是目的,而是一种手段。

 

进程间通信的技术背景
进程是具有独立性的。虚拟地址空间+页表。保证进程运行的独立性(进程内核数据结构+进程的代码和数据)
通信成本比较高。

 

如何理解进程间通信?
进程运行具有独立性!——进程想通信,难度比较大——进程间通信的本质:需要中间媒介,先让不同的进程看到同一份资源(内存空间)
所谓的进程看到同一块“内存”,属于哪一个进程?
不能隶属于任何一个进程,而应该强调共享。

 

为什么要进行进程间通信?——需要交互数据、控制、通知等目标


 

1. 进程间通信方式的一些标准:

进程间通信发展

  • 管道——linux原生能提供
  • System V进程间通信——多进程——单机通信
  • POSIX进程间通信——多线程——网络通信

 
进程间通信分类

  • 管道
    匿名管道pipe 命名管道

  • System V IPC
    System V 消息队列
    System V 共享内存
    System V 信号量

  • POSIX IPC
    消息队列
    共享内存
    信号量
    互斥量
    条件变量
    读写锁

标准更多是在我们使用者看来,在接口上具有一定的规律


2. 管道

2.1 什么是管道

管道是Unix中最古老的进程间通信的形式。
我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”
竖画线就是管道“|”

只能单向通信,传输的都是资源(数据)
有入口,有出口
计算机通信领域的设计者,设计了一种单向通信的方式——管道

 

2.2 管道的原理

管道通信背后是进程之间通过管道进行通信
纯内存级通信方式——没必须要写到磁盘中

管道的底层原理就是通过文件实现的
文件是属于内核的(OS)
1.分别溢读写方式打开同一个文件
2.fork()创建子进程——进程具有独立性,此时子进程也应自己创建一个文件描述符表,然后父进程相关的数据会拷贝给子进程——拷贝只是第一次拷贝,之后不会存在父子进程相互影响的情况
3.双方进程各自关闭自己不需要的文件描述符

让不同的进程看到了同一份资源

 

2.3 匿名管道

#include <unistd.h>
功能:创建一无名管道
原型
int pipe(int fd[2]);
参数
fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端
返回值:成功返回0,失败返回错误代码

 

2.3.1 实例代码

1. demo代码

如何做到让不同的进程看到同一份资源呢?
fork让子进程继承的——能够让具有血缘关系的进程进行进程间通信——常用父子进程

int pipe(int pipefd[2]);

int pipefd[2]是输出型参数,期望通过调用它,得到被打开的文件fd
int pipe创建成功返回值为0,失败就是-1

 
 

Makefile

 mypipe:mypipe.cc
     g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
    rm -f mypipe 

条件编译:
#ifdef DEBUG
//…
#endif
取消注释,宏定义就开始编译。
g++ -o $@ $^ -std=c++11 #-DDEBUG#调试

 
mypipe.cc

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

 using namespace std;

 int main()
 {
   
    //1.创建管道
    int pipefd[2] = {
   0};//保存管道的一个数组,这个数组父子进程都能看到
    //pipefd[0]:读端, pipefd[1]:写端

    int n = pipe(pipefd);
    assert(n != -1);//debug下断言才有效
    (void)n;//release下要加这句代码才有效,有使用有定义

#ifdef DEBUG
    cout << "pipefd[0]: " << pipefd[0] << endl;//3
    cout << "pipefd[1]: " << pipefd[1] << endl;//4
#endif

    //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;//通信时传的是字符串
                cout << "Father# " << buffer << endl;
            }
        }
 
        exit(0);//终止

    }
    //父进程——写
    //3.构建单向通信的信道,父进程写入,子进程读取
    //3.1关闭父进程不需要的fd
        close(pipefd[0]);
        string message = "我是父进程,我正在给你发消息";
        int count = 0;
        char send_buffer[1024];
        while(true)
        {
   
            //3.2构建一个变化的字符串
            //snprintf:安全的进行格式化显示
            snprintf(send_buffer,sizeof(send_buffer),"%s[%d] : %d", 
            message.c_str(), getpid(), count++);
            //3.3写入
            write(pipefd[1],send_buffer,strlen(send_buffer));//文件不需要+1

            //3.4故意sleep
            sleep(1);
        }
        pid_t ret = waitpid(id,nullptr,0);
        assert(ret>0);
        (void)ret;
    
    return 0;

 } 

在这里插入图片描述

这就叫做管道。

为什么不定义全局buffer来进行通信呢?
因为有写时拷贝的存在,无法更改通信!

 
 

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

 using namespace std;

 int main()
 {
   
    //1.创建管道
    int pipefd[2] = {
   0};//保存管道的一个数组,这个数组父子进程都能看到
    //pipefd[0]:读端, pipefd[1]:写端

    int n = pipe(pipefd);
    assert(n != -1);//debug下断言才有效
    (void)n;//release下要加这句代码才有效,有使用有定义

#ifdef DEBUG
    cout << "pipefd[0]: " << pipefd[0] << endl;//3 默认从3开始,因为012被占用,标准输出...
    cout << "pipefd[1]: " << pipefd[1] << endl;//4
#endif

    //2.创建子进程
    pid_t id = fork();
    assert(id != -1);
    if(id ==0)
    {
   
        //子进程——读
        //3.构建单向通信的信道,父进程写入,子进程读取
        //3.1关闭子进程不需要的fd
        close(pipefd[1]);
        char buffer[1024 * 8];
        while(true)
        {
   
            //写入的一方,fd没有关闭,如果有数据就读,没有数据就等
            //写入的一方,fd关闭,读取的一方read会返回0,表示读到了文件的结尾
            ssize_t s = read(pipefd[0],buffer,sizeof(buffer)-1);
            if(s>0)
            {
   
                buffer[s] = 0;//通信时传的是字符串
                cout <<"child get a message["<<getpid()<< "] Father# " << buffer << endl;
            }
            else if(s == 0)
            {
   
                cout << "write quit(father), me quit!!!" << endl;
                break;
            }
        }
         
         //close(pipefd[0]);
 
        exit(0);//终止

    }
    //父进程——写
    //3.构建单向通信的信道,父进程写入,子进程读取
    //3.1关闭父进程不需要的fd
        close(pipefd[0]);
        string message = "我是父进程,我正在给你发消息";
        int count = 0;
        char send_buffer[1024*8];
        while(true)
        {
   
            //3.2构建一个变化的字符串
            //snprintf:安全的进行格式化显示
            snprintf(send_buffer,sizeof(send_buffer),"%s[%d] : %d", 
            message.c_str(), getpid(), count++);
            //3.3写入
            write(pipefd[1],send_buffer,strlen(send_buffer));//文件不需要+1

            //3.4故意sleep
            sleep(1);
            cout<<count<<endl;
            if(count == 5)
            {
   
                cout<<"write quit(father)"<<endl;
                break;
            }
        }
        <
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hey pear!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值