OS53.【Linux】System V 共享内存(2)

🧨🧨🧨🧨🧨🧨🧨🧨🧨除夕篇🧨🧨🧨🧨🧨🧨🧨🧨🧨

🎉❤️🎉❤️🎉❤️🎉❤️🎉❤️祝各位程序员们马年大吉 万事如意!❤️🎉❤️🎉❤️🎉❤️🎉❤️🎉

目录

1.知识回顾

[2.接着System V 共享内存(1)继续讲](#2.接着System V 共享内存(1)继续讲)

通信代码

分析细节

结论

共享内存的特点

使用管道为共享内存添加同步机制

共享内存的属性


1.知识回顾

参见OS52.【Linux】System V 共享内存(1)文章复习共享内存的基础知识

2.接着System V 共享内存(1)继续讲

通信代码

上篇文章的最后实现了让两个进程使用共享内存进行通信

shm1.cpp写入:

cpp 复制代码
#include <stdio.h>
#include <iostream>
#include "header.hpp"
int main() 
{
    //为了减小代码行数,系统调用的错误执行结果都没有判断
    int shmid=get_new_shared_memory(); 
    char* start_addr=(char*)shmat(shmid,nullptr,0);
    for (;;) 
    {
        sleep(1);
        std::cout<<start_addr;
        fflush(stdout);
    }
    shmdt(start_addr);
    shmctl(shmid,IPC_RMID,nullptr);
    return 0;
}

shm2.cpp写入:

cpp 复制代码
#include <stdio.h>
#include <iostream>
#include "header.hpp"
int main() 
{
    //为了减小代码行数,系统调用的错误执行结果都没有判断  
    int shmid=get_old_shared_memory(); 
    char* start_addr=(char*)shmat(shmid,nullptr,0);
    for (;;)
    {
        std::cin>>start_addr;
    }
    shmdt(start_addr);
    return 0;
}

运行结果:

分析细节

注意到:

cpp 复制代码
for (;;)
{
    std::cin>>start_addr;
}

没有将输入暂存到缓冲区,然后将缓冲区数据拷贝到start_addr处

结论

结论: 1.一旦有了共享内存,可以将其挂接到进程自己的地址空间中,进程可以直接把他当成自己的内存空间来使用

2. 一旦有数据写入到共享内存,进程就能立马看到, 不需要经过系统调用

共享内存的特点

从以上结论可以得出共享内存的特点: 共享内存没有同步互斥值之类的保护机制

对比管道,管道中如果没有数据,那么进程读管道会被阻塞

其次,共享内存是所有的进程间通信中最快的,因为不需要经过系统调用

再之,共享内存内部的数据,由用户自己维护

使用管道为共享内存添加同步机制

共享内存无同步机制,可以借助管道

管道的复习:
OS43.【Linux】进程间通信 管道通信理论部分

OS44.【Linux】进程间通信 管道通信代码部分(1)

OS45.【Linux】进程间通信 管道通信代码部分(2)

OS46.【Linux】进程间通信 管道的应用场景和进程池项目

例如: shm2.out向共享内存写入数据,shm1.out读数据,可以为共享内存配上管道来保证同步

1.读写端正常,管道为空,读端阻塞,此时不能读共享内存数据

2.读写端正常,管道不为空,读端不阻塞,此时能读共享内存数据

header.cpp写入:

cpp 复制代码
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <stdlib.h>
#define SIZE 1024
#define PROJ_ID 0x1344 
key_t get_key()
{
    char buffer[SIZE];
    char* pathname=getcwd(buffer,sizeof(buffer));
    if (pathname==nullptr)
    {
        perror("getcwd failed");
        exit(1);
    }
    return ftok(pathname,PROJ_ID);
}

int get_new_shared_memory()
{
    key_t key=get_key();
    //申请新的共享内存
    int shmid=shmget(key,4097,IPC_CREAT|IPC_EXCL|0666);
    return shmid;
}

int get_old_shared_memory()
{
    key_t key=get_key();
    //申请新的共享内存
    int shmid=shmget(key,4097,IPC_CREAT|0666);
    return shmid;
}

shm1.cpp写入:

cpp 复制代码
#include <stdio.h>
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "header.hpp"
int main() 
{ 
    //为了减小代码行数,系统调用的错误执行结果都没有判断
    int shmid=get_new_shared_memory();  
    char* start_addr=(char*)shmat(shmid,nullptr,0);
    mkfifo("./tmp_fifo",0664);
    int fd=open("./tmp_fifo",O_RDONLY);

    for (;;) 
    {
        char ch;
        ssize_t n=read(fd,&ch,1);
        if (n==0||n<0)
            break;
        std::cout<<start_addr<<std::endl;
    }
    shmdt(start_addr);
    shmctl(shmid,IPC_RMID,nullptr);
    unlink("./tmp_fifo");
    return 0;
}

shm2.cpp写入:

cpp 复制代码
#include <stdio.h>
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "header.hpp" 
int main()  
{
    //为了减小代码行数,系统调用的错误执行结果都没有判断  
    int shmid=get_old_shared_memory(); 
    char* start_addr=(char*)shmat(shmid,nullptr,0);
    int fd=open("./tmp_fifo",O_WRONLY);
    
    for (;;)
    {
        std::cin>>start_addr;
        write(fd,"",1);
    }
    shmdt(start_addr);
    return 0;
}

运行结果:

我这个共享内存+管道代码最初版本写的有问题,下篇文章讲讲排错记录

共享内存的属性

man手册是这样说的:共享内存的属性存储在struct shmid_ds

cpp 复制代码
struct shmid_ds {
    struct ipc_perm shm_perm;    /* Ownership and permissions */
    size_t          shm_segsz;   /* Size of segment (bytes) */
    time_t          shm_atime;   /* Last attach time */
    time_t          shm_dtime;   /* Last detach time */
    time_t          shm_ctime;   /* Creation time/time of last
                                    modification via shmctl() */
    pid_t           shm_cpid;    /* PID of creator */
    pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */
    shmatt_t        shm_nattch;  /* No. of current attaches */
    ...
};
 
struct ipc_perm {
        key_t          __key;    /* Key supplied to shmget(2) */
        uid_t          uid;      /* Effective UID of owner */
        gid_t          gid;      /* Effective GID of owner */
        uid_t          cuid;     /* Effective UID of creator */
        gid_t          cgid;     /* Effective GID of creator */
        unsigned short mode;     /* Permissions + SHM_DEST and
                                    SHM_LOCKED flags */
        unsigned short __seq;    /* Sequence number */
    };

注: struct shmid_ds内含struct ipc_perm,结构体嵌套

对于一个已经创建好的共享内存,其key存储在struct ipc_perm中,这是很自然的,因为操作系统要想管理共享内存,必须先描述共享内存再组织共享内存,描述共享内存需要结构体

相关推荐
xlp666hub19 小时前
Leetcode第五题:用C++解决盛最多水的容器问题
linux·c++·leetcode
张宏23620 小时前
原子操作 (基于Linux 应用层 C 语言)
linux
kymjs张涛1 天前
OpenClaw 学习小组:初识
android·linux·人工智能
程序设计实验室2 天前
经历分享,发现挖矿木马后,服务器快速备份与重装(腾讯云平台)
linux
Miku162 天前
OpenClaw-Linux+飞书官方Plugin安装指南
linux·人工智能·agent
Miku162 天前
OpenClaw 接入 QQ Bot 完整实践指南
linux·人工智能·agent
Yogurt_cry2 天前
[树莓派4B] 闲置近10年的爱普生 L310 打印机爆改无线打印机
linux·物联网·树莓派
爱吃橘子橙子柚子3 天前
3CPU性能排查总结(超详细)【Linux性能优化】
运维·cpu
Johny_Zhao3 天前
OpenClaw中级到高级教程
linux·人工智能·信息安全·kubernetes·云计算·yum源·系统运维·openclaw
Sheffield3 天前
Docker的跨主机服务与其对应的优缺点
linux·网络协议·docker