Linux 进程通信

1.什么是进程通信?

答:两个或多个进程实现数据层面的交互;但是因为进程的独立性,导致进程通信的成本较高;

2.为什么要通信?

答:多进程之间由协同的需求,所以通信;以下是通信要实现的目的:

2.1.基本数据:

2.2.发送命令:一个进程控制另一个进程

2.3.多进程之间实现协同

2.4.通知

2.6.通信是有成本的:因为是要打破进程的独立性

3.怎么通信?

3.1进程间通信的本质:必须让不同的进程看到同一份“资源”

3.2“资源”——特定形式的内存空间;

3.3这个资源谁提供?答:一般是操作系统提供;

3.4为什么不是我们两个进程中的一个提供呢?答:进程具有独立性;假设是由一个进程提供,这个资源应该属于谁?他不是共享资源,还是进程独有,因为破环了进程的独立性;OS是第三方空间,谁用谁申请

3.5进程访问这个空间,进行通信,本质就是访问操作系统;进程代表的就是用户;“资源”从创建、使用、释放——必须调用系统接口;

3.6操作系统提供资源,保障了进程通信,而且底层设计、接口设计、都要由操作系统独立设计;一般操作系统,会有一个独立的通信模块——隶属于文件系统——IPC通信模块(进程间通信的意思)

3.7进程间通信是由标准的;两套标准——system V  和 posix

3.8基于文件级别的通信方式——管道(此时的文件,只是代称,我可以往内存中写啊)

思想是正确的,但是效率并不高;管道是Linux(unix)最古老的进程通信方式;

3.9管道原理(自我理解):基础IO部分,缓冲区里的数据要写入磁盘文件,现在不写入磁盘,而是直接与子进程交互,把数据给子进程;并且通过引用计数原理(智能指针)控制着文件的打开和关闭,进行读写;(只能单向通信所以命名为管道)

3.10强烈建议如果单向通信,一定要关闭不必要的文件描述符,防止写错;

3.11 如果我要进行双向通信呢——答:建两个管道;

3.12 以上讲的是父子进程,如果没有任何关系,可以用以上讲的原理进行通信吗?答:不能,做不到;

3.13兄弟之间也可以用管道,进行通信;爷爷和孙子也可以进行通信——使用管道通信,必须得是具有血缘关系;常用于父子之间

3.14 以上所说的文件,是在缓冲区中进行的,并不是在真实的文件中交互的,所以这个管道叫做匿名管道!

4.接口

4.1open、close是磁盘级的接口,不能用他,而是专用的接口pipe(系统接口)

 int pipe(int pipefd[2]);

//pipdfd[2]  输出型参数
//pipefd[0]  只读下标 
//pipefd[1]  只写下标
 

4.2代码实现管道

// 实现系统间通信
#include <iostream>
#include <unistd.h>
#include <string>
#include <cstdlib> //stdlib.h C++风格的头文件写法
#include <sys/types.h>
#include <sys/wait.h>

using namespace std;

#defind N 2
#define NUM 1024
void Writer(int wfd)
{
    string s = "hello,i am child!";
    pid_t self = getpid();
    int number = 0;
    char buffer[NUM];
    while (true)
    {
        //构建字符串
        buffer[0] = 0; // 字符串清空;只是为了提醒阅读代码的人,我把这个数组当作字符串了
        snprintf(buffer, sizeof(buffer), "%s-%d-%d", s.c_str(), self, number);
        //cout<<buffer<<endl;       
        //发送给父进程
        write(wfd,buffer,strlen(buffer));
        sleep(1);
    }
}
void Reader(int rfd)
{
    char buffer[NUM];
    while(true)
    {
        buffer[0]=0;
        ssize_t n=read(rfd,buffer,sizeof(buffer));
        if(n>0)
        {
            buffer[n]=0; //0=='\0'
            cout<<"father get a message["<<getpid()<<"]#"<<buffer<<endl;
        }
    }
}
int main()
{
    int pipefd[N] = {0};
    int n = pipe(pipefd); // 0下标是读;1下标是写
    if (n < 0)
    {
        perror("pipe");
        return 1;
    }
    // cout<<"pipefd[0]:"<<pipefd[0]<<",pipefd[1]:"<<pipefd[1]<<endl;
    // 子进程写入,父进程读
    pid_t id = fork();
    if (id < 0)
        return 2;
    if (id == 0)
    {
        // 子进程
        close(pipefd[0]);
        Writer(pipefd[1]);

        close(pipefd[1]);
        exit(0);
    }
    close(pipefd[1]);
    Reader(pipefd[1]);
    pid_t rid = waitpid(id, nullptr, 0);
    if (rid < 0)
        return 3;

    close(pipefd[0]);
    return 0;
}

4.3根据以上代码可以得到管道的本质:将上层缓冲区的数据拷贝到系统缓冲区,再将数据拷贝给上层缓冲区;

4.4进程间通信的本质:需要让不同的进程,看到同一份资源——但是是多执行流共享的,难免会出现访问冲突的问题;

5.管道的特征:五点

5.1具有血缘关系的进程进行进程间通信;

5.2管道只能单向通信;

5.3父子进程是会进程协同的,同步与互斥的——保护管道文件的数据安全;

5.4管道是面向字节流的;

5.5管道是基于文件的,文件的生命周期是跟随进程的(父子进程退出,管道会被操作系统回收)

6.管道的4种情况:

6.0管道是有大小的,Linux版本2.6.11之前是4KB,之后是64KB;(为了保障传输数据的整体性,有序性,定义了一个pipe_buf 大小是4KB,当写入系统缓冲区的数据小于4KB时,必须要等子进程传输完数据,父进程才能读——这个工作就是读取的原子性问题)

6.1读写端正常,管道如果为空,读端就要阻塞;

6.2读写端正常,管道如果被写满,写端就要阻塞

6.3读端正常,写端关闭,读端就会读到0,表明读到了文件(pipe)结尾,不会被阻塞;一直在读;

6.4读端关闭,写端正常,操作系统就要杀掉这个正在写入的进程;如何干掉?答:操作系统通过信号(是几号信号? 答:13号)干掉(操作系统是不会做低效,浪费等类似的工作的,如果做了,那就是操作系统的bug)

ulimit -a //查看系统对一些非常重要的资源的限制

7.管道的应用场景

7.1这个管道和我们以前学的知识,哪些是有关系的?

Linux shell 指令 :cat test.txt | head -10 |tail -5  这个语句的实现,就是shell命令+进程替换+管道实现的;

7.2使用管道实现一个简易版本的进程池;(请看下一节)

  • 13
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值