【Linux】零基础学习命名管道-共享内存

文章目录

背景

匿名管道有一个缺点,就是只能血缘间通信,而我们想要让两个毫不相干的进程进行通信,就要用到命名管道,

🚩存储

要两个毫不相干的进程通信,首先要看到同一个管道文件,命名管道如何做到的呢?同一路径下的同一文件名,文件路径具有唯一性

所以,命名管道存储在文件系统中,有自己的文件路径和inode,但是不会向磁盘中刷数据,数据传输发生在内存级缓冲区中,

所以,纯内存级缓冲区的通信方法有:
匿名管道,共享内存,消息队列

建立命名管道

🚩命令行创建

1,命名管道可以直接在命令行创建:

mkfifo "管道名"

可以看到我创建了一个管道,向里面写入!

可以看到卡住了,那是因为卡在文件缓冲区了,我没有读取

并且我们查看文件大小,发现还是0,说明没刷盘,读取之后结束

删除管道用unlink, 比如这里

unlink myfile

🚩mkfifo函数实现通信

函数:

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

用函数实现两个进程通信

头文件:封装类,完成命名管道初始化和清理

c 复制代码
#include<iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#define FIFO_FILE "./myfile"
#define MODE 0664

enum{
    FIFO_CREAT_FILE=1,
    FIFO_DELETE_FILE,
    FIFO_OPEN_FILE
};
class Init{
public:
    Init()
    {
    int n=mkfifo(FIFO_FILE,MODE);
    if(n==-1)
    {
        perror("mkfifo");
        exit(FIFO_CREAT_FILE);
    }
    }
    ~Init()
    {
    int m=unlink(FIFO_FILE);
    if(m==-1)
    {
        perror("unlink");
        exit(FIFO_DELETE_FILE);
    }
    }

};

==读端:打开文件即可,要是写端没准备好,我们还需要等写端打开,==不断写入,如果x=0,说明写端关闭了,那我也关闭

c 复制代码
#include "commen.h"
using namespace std;
int main()
{
    Init it;
    int fd=open(FIFO_FILE,O_RDONLY); //读端得等写端打开文件
    cout<<"server ready done"<<endl;
    if(fd==-1)
    {
        perror("open");
        exit(FIFO_OPEN_FILE);
    }
    while(true)
    {
        char buffer[1024]={0};
        int x=read(fd,buffer,sizeof(buffer));
        if(x>0)
        {
            buffer[x]=0;
            cout<<"client say:"<<buffer<<endl;
        }
        else if(x==0)
        {
            printf("client quit! me too! string errno %s,errno %d",strerror(errno),errno);
            break;
        }
        else{
            break;
        }
    }

    return 0;
}

写端:打开文件,从键盘上获取字符串,写入文件

c 复制代码
#include "commen.h"
using namespace std;
int main()
{
    int fd=open(FIFO_FILE,O_WRONLY);
    if(fd<0)
    {
        perror("open");
        exit(FIFO_CREAT_FILE);
    }
    cout<<"client ready done"<<endl;
    string line;
    while(true)
    {
        cout<<"please enter:"<<endl;
        getline(cin,line);
        write(fd,line.c_str(),line.size());
    }
    close(fd);
    return 0;
}

共享内存

🚩原理


🚩操作系统在物理内存中开辟一块物理内存,供两个进程交流,

操作系统中肯定有很多共享内存,操作系统肯定要管理,先描述再组织

要释放一个共享内存,先去关联再释放

🚩注意:

1,共享内存没有同步机制

2,共享内存通信速度最快(因为他拷贝最少)

3,共享内存数据都由用户自己控制

上代码:

shmget

🚩int shmget(key_y key , size_t size , int shmflg);

int 返回共享内存标识符

key:具有唯一性,保证进程能找到

size:共享内存大小

🚩shmflg:如何设置共享内存,O_CREAT没有就创建,有就打开

O_CREAT | O_EXIT,没有就创建,有返回错误
O_EXIT 不单独使用

🚩key 和 shmid(shmget返回值)区别
key:操作系统内标定唯一性
shmid:只在你的进程,用来表示资源唯一性

学习key

1,key 在内核中要具有唯一性,方便进程找到

2,第一个进程设置key,第二个进程拿着相同的key就能找到相同位置

3,key存在哪里?共享内存结构体中

🚩4,key其实类似路径 它怎么来的 ?函数

key_t ftok(const char* pathname, int proj_id id);

pathname是用户自己定义的路径/home/jib,id也是用户自己指定的

共享内存生命周期是随内核的,也就是用户不关闭,共享内存会一直存在,除非内核重启
🚩共享内存查看: ipcs -m
🚩共享内存删除:ipcrm -m shmid

nattch,表示哪个进程关联到此共享内存

shmat

🚩void *shmat(int shmid, const void *shmaddr, int shmflg);

🚩功能:将共享内存关联到进程中,,

shmid:共享内存的标识符

shmaddr:一般设置nullptr,核心自动选择地址

shmflg: 设置0 读写权限

shmdt

🚩 int shmdt(const void *shmaddr);

🚩功能:将进程去关联

shmaddr 共享内存起始地址

注意去关联,不代表释放共享内存

shmctl

功能:控制共享内存

🚩int shmctl(int shmid, int cmd, struct shmid_ds *buf);

cmd有3个动作

🚩实现共享内存

共享内存没有同步机制,也就是我们写入时,不会自动清理缓存区,就会一直打印重复数据

所以我们加了管道,通知读端

commen.hpp

c 复制代码
#include <iostream>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <sys/shm.h>

using namespace std;

//共享内存给的是4096整数倍字节大小,
//就算定义 4097,给你的其实是4096*2,但是你访问4098又会报错
const int size=4096;
const string pathname="/home/jib";
const int proj_id = 0x6666;

key_t GetKey()
{
    int k=ftok(pathname.c_str(),proj_id);
    if(k<0)
    {
        perror("ftok");
        exit(1);
    }
    printf("ftok success! key is 0x%x\n", k);
    return k;
}

int GetShareMemHelper(int flag)
{
    key_t k=GetKey();
    
    int shmid=shmget(k,size,flag);
    if(shmid<0)
    {
        perror("shmget");
        exit(2);
    }
    printf("shmget success : %d",k);
    return shmid;
}

int Createshm()
{
    return GetShareMemHelper(IPC_CREAT| 0666);
}
int Getshm()
{
    return GetShareMemHelper(IPC_CREAT|0666);
}
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#define FIFO_FILE "./myfile"
#define MODE 0664

enum{
    FIFO_CREAT_FILE=1,
    FIFO_DELETE_FILE,
    FIFO_OPEN_FILE
};
class Init{
public:
    Init()
    {
    int n=mkfifo(FIFO_FILE,MODE);
    if(n==-1)
    {
        perror("mkfifo");
        exit(FIFO_CREAT_FILE);
    }
    }
    ~Init()
    {
    int m=unlink(FIFO_FILE);
    if(m==-1)
    {
        perror("unlink");
        exit(FIFO_DELETE_FILE);
    }
    }
};

server.cc

c 复制代码
#include "commen.hpp"
#include "log.hpp"
using namespace std;
int main()
{
    Log log;
    Init init;
    int shmid=Createshm();
    char* shmaddr=(char*)shmat(shmid,nullptr,0);
    int fd=open(FIFO_FILE,O_CREAT|O_RDONLY);
    log(Info,"server open success");
    if(fd<0)
    {
        log(Fetal,"error string : %s,error :%d",strerror(errno),errno);
        exit(FIFO_CREAT_FILE);
    }
    struct shmid_ds shmds;
    while(true)
    {
        char c;
        size_t s=read(fd,&c,1);
        if(s<0)
        break;
        else if(s==0)
        break; 
        cout<<"Client Say:"<<shmaddr<<endl;
        //sleep(1);

        shmctl(shmid, IPC_STAT, &shmds);
        cout << "shm size: " << shmds.shm_segsz << endl;
        cout << "shm nattch: " << shmds.shm_nattch << endl;
        printf("shm key: 0x%x\n",  shmds.shm_perm.__key);
        cout << "shm mode: " << shmds.shm_perm.mode << endl;
    }
    shmdt(shmaddr);
    shmctl(shmid,IPC_RMID,nullptr);
    close(fd);
    return 0;

}

client.cc

c 复制代码
#include "commen.hpp"
#include "log.hpp"
using namespace std;
int main()
{
    Log log;
    int shmid=Getshm();
    char* shmaddr=(char*)shmat(shmid,nullptr,0);
    int fd=open(FIFO_FILE,O_CREAT| O_WRONLY,MODE);
    if(fd<0)
    {
        log(Fetal,"error string :%s,errno :%d",strerror(errno),errno);
        exit(FIFO_OPEN_FILE);
    }
    while(true)
    {
        cout<<"please enter :";
        fgets(shmaddr,4096,stdin);

        write(fd,"c",1);
    }
    shmdt(shmaddr);
    return 0;
}

实现两个进程通信

相关推荐
汤姆yu2 小时前
基于android的大学学校食堂点餐系统
android
数字化转型20252 小时前
SAP 实施项目乙方因甲方逾期付款单方面中途离场的风险处理方案
运维·人工智能·机器学习
陳10302 小时前
C++:继承
开发语言·c++
txinyu的博客2 小时前
解析muduo源码之 atomic.h
服务器·c++
数智工坊2 小时前
【操作系统-处理器调度】
java·linux·服务器·windows·ubuntu
济6172 小时前
Linux内核---vmlinux、zImage、uImage区别
linux·运维·服务器
阿拉伯柠檬2 小时前
网络层协议IP(二)
linux·网络·网络协议·tcp/ip·面试
简叙生活2 小时前
【CES直击:从“屏幕依赖”到“真实对话”,Lookee如何用声网技术重构英语学习?
学习·ces
静谧空间2 小时前
Linux自动备份Mysql数据
linux·运维·mysql