进程通信方式管道文件


一、管道文件原理

管道文件是一个基于文件级别的通信方式,内存级文件。管道是Unix中最古老的通信方式,就是一个进程链接到另一个进程,一个进程从管道中读一个进程写数据进管道中!
在这里插入图片描述

进程在打开一个文件时
在这里插入图片描述
文件,在磁盘上的无论读写都先将数据加载到内存中。而让一个文件成为真正的内存及文件,不会刷新到磁盘中,他不在磁盘中真正存在,只存在于内存当中,这是一个内存级文件,也就是管道文件!
一个进程打开文件时并创建子进程,那么打开的文件会不会给子进程拷贝,会的,不过不是将文件内容都拷贝他只是对于文件描述符表会拷贝一份,里面的文件对象的地址存在
在这里插入图片描述
子进程和父进程看到同一份资源。文件描述符表会被子进程继承下去,但是文件对象并不会继承,因为创建子进程和文件无关,文件是文件系统模块的!所有打开的文件不会给子进程拷贝,文件描述符是拷贝过去的,所以两个进程看到的资源是一样的,同一个内存级文件!进程间通信本质就是让不同的进程看见同一份资源,所有父子进程之间看到了同一内存级文件资源,他们在通信也要保证通信数据的准确性,不要这个进程一边要读取数据,一边又要给另外一个进程写数据,进程之间通信是在第三方通信信道之间完成,若是一个进程即要写又要读,当进程向一个通信信道中写入时,然后进程又向这个管道通信中读取可能是读到自己写入的数据!
所有要保证一个进程读同时将它的写端关闭,然后另一个进程写同时将它的读端关闭!
父子进程看到通过一份资源(文件),子进程对于父进程的文件描述符表继承下去了,而又要保证数据可靠性,所有一个文件它首先是以两种方式打开的,以读和写打开,然后在文件描述符表中占据两个下标位,然后创建子进程子进程会将文件描述符表中的内容拷贝一份,之后让一方读一方写,把其他端口给关掉!就像一个文件有多个指向,同一个内容多个指针指向,在文件中不用怕关掉之后这个文件就不存在了,有引用计数存在,有多个文件描述符表中指向这个文件时它的引用计数就会为指向个数,然后当文件描述符指向0时,这个文件才会关掉!建立通信信道—>管道文件(让进程看到同一份资源):父进程在打开文件的时候以读写方式打开两次,然后创建子进程之后结合对应的场景关掉自己对应的一端保证一端读一端写!
站在内核角度
内核级文件,一个文件打开两次,则操作系统会创建文件两次,以读和写方式打开文件,存在于不同的文件描述符表中对应下标不一样,只不过这两个文件指向同样的inode和文件缓冲区,不过避免两方同时读写需一方关闭一端,让父子进程进行单向通信,不过谁读谁写由用户自己决定。一个进程对于文件读取一个进程对于管道文件写也就是管道文件通信原理,而在内存级文件中只能定义为单向通信的为管道文件,若是要双向通信则建立多个管道实现通信,建立多个管道通信是基于它们是父子进行通信!若是没有任何关系就不能使用这样管道通信的方式,因为它们不能看见同样一张文件描述符表,它们必须是父子进程或者有血缘关系的进程可以做到,因为它们近乎都可以看见同一张文件描述符表!
这样进行通信的管道文件是没有名字的也不需要为一个匿名管道!

二、通信

建立管道文件方式,通过系统接口建立pipe
在这里插入图片描述
pipe它的参数是一个数组,里面有两个元素,它是一个输出型参数,它会将文件描述符带出来!pipefd[0]为文件描述符为只读的方式打开文件,pipefd[1]为以写方式打开文件,这个系统接口是打开的是内存级文件不用open打开文件而是用pipe.0号下标内容代表读段,1号下标内容代表写端!而要让一个进程读一个进程写。让父进程读,子进程写,则在创建子进程成功之后子进程要将管道文件对应的0号下标给关掉,父进程将1号下标关掉!

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

#define N 2
#define NUM 1024

using namespace std;

// 子进程写
void Writer(int wfd)
{
    string s = "hello, I am child";
    pid_t self = getpid();
    int number = 0;

    char buffer[NUM];
    while (true)
    {
        sleep(1);

        // 构建发送字符串
        //buffer[0] = 0; // 字符串清空, 只是为了提醒阅读代码的人,我把这个数组当做字符串了
        //snprintf(buffer, sizeof(buffer), "%s-%d-%d", s.c_str(), self, number++);
        // cout << buffer << endl;
        // 发送/写入给父进程, system call
        //write(wfd, buffer, strlen(buffer)); // strlen(buffer) + 1???
        char c = 'c';
        write(wfd, &c, 1); // strlen(buffer) + 1???
        number++;
        cout << number << endl;

        if(number >= 5) break;
    }
}

// father
void Reader(int rfd)
{
    char buffer[NUM];

    while(true)
    {
        buffer[0] = 0; 
        // system call
        //父进程从这个文件中读取数据到buffer缓冲区中
        ssize_t n = read(rfd, buffer, sizeof(buffer)); //sizeof != strlen
        if(n > 0)
        {
            buffer[n] = 0; // 0 == '\0'
            cout << "father get a message[" << getpid() << "]# " << buffer << endl;
        }
        else if(n == 0) 
        {
            printf("father read file done!\n");
            break;
        }
        else break;
        // cout << "n: " << n << endl;
    }
}

int main()
{
    int pipefd[N] = {0};
    //建立通信信道
    int n = pipe(pipefd);
    if (n < 0)
        return 1;

    // cout << "pipefd[0]: " << pipefd[0] << " , pipefd[1]: " << pipefd[1] << endl;

    // child -> w, father->r
    //创建子进程
    pid_t id = fork();
    if (id < 0)
        return 2;
    if (id == 0)
    {
        // child
        //子进程关闭读端
        close(pipefd[0]);

        // IPC code
        Writer(pipefd[1]);

        close(pipefd[1]);
        exit(0);
    }
    // father
    close(pipefd[1]);

    // IPC code
    Reader(pipefd[0]);

	//最后父进程要等待子进程退出
    pid_t rid = waitpid(id, nullptr, 0);
    if(rid < 0) return 3;

    close(pipefd[0]);


    sleep(5);
    return 0;
}

子进程
若是父进程启动建立信道之后不断读写,通信发送变化的数据,读写文件时调用系统资源资源是每隔一秒写入一次,为何父进程也要每隔一秒读取一次?因为父进程在读取数据时要等待资源就绪才去读取,若是资源不就绪则会去等待!然后夫进程在读取时也不是一直在读取,而是一次把写的数据读取完毕,不会一直读取所有父子进程会进行协同!完全读取则说明管道文件存在有固定大小,那么管道文件是多大?通过ulimit -a查看系统资源,管道文件大小为512字节。
在这里插入图片描述
固定大小是内定的,但是在内核中有许多不同分配规则,写端一直写,读端不读,将管道写满,指导不能再写之后就是管道文件所能写的极限了。验证为64KB大小这是centos下,而在不同的内核中,管道大小有差别!
资源多执行共享出现访问冲突问题,临界资源竞争问题,所有对于资源分配有固定大小!

操作系统对资源有限制!系统提供的!资源拷贝的过程发生了两次拷贝(用户拷贝到内核,内核拷贝给用户)!

管道特征
1.具有血缘关系的进程进行通信
2.管道只进行单向通信
3.父子进程会进行协同,同步与互斥—保护管道文件的数据安全
4.管道是面向字节流的(按照约定大小读取)
5.管道是基于文件的,而文件的生命周期是随进程的。

管道的4种情况
1,管道读写端正常,如果管道为空,那么读端就会发生阻塞(父进程要等待子进程写入,等待资源就绪)
2.读端不读,写端一直写,当将管道写满之后写端很快写入,写满之后就不再写了,就像是吃饭吃饱了就吃不下了,此时要等待读的那一方来读,而读取是一次性把管道文件中的所有数据全部读取,这也是防止写入时资源被覆盖,父进程不管子进程向管道中写了多少次,他只管一次就读取完毕!怎么确定写端写好了,读方才能去读,写入时要规定一个写入大小,当写到一个固定大小时,读方才能去读,不然若是提前去读了,可能会导致读到的数据不完整。
3.读端正常读,写端关闭。父进程把数据读完了,此时子进程不会写入,子进程把写端都关闭了不能再写入了,那么读端就会读到0,表明读到了文件结尾父进程就不会再读了,它会退出将这个文件给关闭了!
4.写端正常写,读端关闭。读写不会一直进行下去,不会一直写下去,因为它在写是无意义的都没有观众,一人独舞也没意思!操作系统不会做低效的事情,所有不会再写入!操作系统会叉掉正在写入的进程因为写入时要调度cpu资源,会占用内存所有操作系统通过向进程发送信号然后杀掉这个进程。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值