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中,这是很自然的,因为操作系统要想管理共享内存,必须先描述共享内存再组织共享内存,描述共享内存需要结构体

相关推荐
一叶龙洲15 小时前
win11与Ubuntu之间同步配置、插件
linux·运维·ubuntu
CHANG_THE_WORLD16 小时前
12.总结:深入理解 Linux I/O 多路复用:select、poll、epoll 全解析
linux·运维·服务器
风曦Kisaki16 小时前
# Linux笔记:操作系统优化与资源管理
linux·运维·服务器·笔记
Mr.HeBoYan17 小时前
一次持续三天才出现的丢包故障——深入解析 DPDK Memory Ordering、rte_ring 与 CPU Memory Barrier (下)
linux·网络·算法·架构·dpdk
zhangrelay18 小时前
笔记本轻量高品质延寿工具完整分系统清单
运维·笔记·学习
初願致夕霞19 小时前
C++懒汉单例设计详解
服务器·开发语言·c++
Discipline~Hai19 小时前
ARM01-ARM体系架构
linux·c语言·arm开发·架构
RisunJan20 小时前
Linux命令-screen(终端复用器)
linux·运维
啊哦11120 小时前
安装VMware Workstation
linux·运维·服务器
持敬chijing20 小时前
Linux基础命令与目录命令
linux·运维·服务器