学习笔记第二十九天

IPC 进程间通信方式:共享内存

原理

共享内存是最高效的进程间通信方式之一,因为它允许两个或多个进程直接访问同一块物理内存区域。这种机制避免了数据在用户空间和内核空间之间的频繁拷贝,从而显著提高了数据传输的效率。

在Linux系统中,共享内存区域由内核管理,但可以由多个进程映射到它们各自的地址空间中。这样,进程就可以像访问本地内存一样直接读写这块共享内存区域。

操作流程
  1. 产生Key值

    使用ftok函数根据给定的路径名和项目ID生成一个唯一的键值(key),这个键值将用于后续的IPC对象操作。

  2. 申请共享内存

    使用shmget函数根据键值申请一块共享内存区域。如果申请成功,该函数会返回一个共享内存标识符(shmid)。

  3. 映射共享内存

    使用shmat函数将共享内存区域映射到进程的地址空间中,以便进程可以直接访问这块内存。

  4. 访问共享内存

    进程通过映射得到的地址直接读写共享内存中的数据。

  5. 解除映射

    使用shmdt函数解除共享内存与进程地址空间的映射关系。

  6. 销毁共享内存

    当不再需要共享内存时,可以使用shmctl函数并指定IPC_RMID命令来删除这块共享内存。

1. ftok - 生成key值
cs 复制代码
#include <sys/types.h> 
#include <sys/ipc.h> 

key_t ftok(const char *pathname, int proj_id);
  • 功能 :将路径名pathname和工程IDproj_id转换为唯一的key值。
  • 参数
    • pathname:一个路径名。
    • proj_id:工程ID,通常是ASCII字符。
  • 返回值:成功时返回唯一的key值,失败时返回-1。
2. shmget - 申请共享内存
cs 复制代码
#include <sys/ipc.h> 
#include <sys/shm.h> 


int shmget(key_t key, size_t size, int shmflg);
  • 功能 :使用唯一键值key向内核提出共享内存使用申请。
  • 参数
    • key:唯一键值。
    • size:要申请的共享内存大小。
    • shmflg:访问权限和创建标志(如IPC_CREAT、IPC_EXCL)。
  • 返回值:成功时返回共享内存ID,失败时返回-1。
3. shmat - 映射共享内存
cs 复制代码
#include <sys/types.h> 
#include <sys/shm.h> 


void *shmat(int shmid, const void *shmaddr, int shmflg);
  • 功能 :将指定shmid对应的共享内存映射到本地内存。
  • 参数
    • shmid:共享内存ID。
    • shmaddr:本地地址,通常为NULL表示由系统自动分配。
    • shmflg:访问权限(如0表示读写,SHM_RDONLY表示只读)。
  • 返回值:成功时返回映射的地址,失败时返回(void*)-1。
4. shmdt - 撤销共享内存映射
cs 复制代码
#include <sys/types.h> 
#include <sys/shm.h> 


int shmdt(const void *shmaddr);
  • 功能:将本地内存与共享内存断开映射关系。
  • 参数shmaddr:要断开的映射地址。
  • 返回值:成功时返回0,失败时返回-1。
5. shmctl - 控制共享内存
cs 复制代码
#include <sys/ipc.h> 
#include <sys/shm.h> 


int shmctl(int shmid, int cmd, struct shmid_ds *buf);
  • 功能:修改共享内存属性或删除共享内存对象。
  • 参数
    • shmid:共享内存ID。
    • cmd:操作命令(如IPC_RMID表示删除对象)。
    • buf:指向shmid_ds结构的指针,用于传递信息或NULL(仅删除对象时)。
  • 返回值:成功时返回0,失败时返回-1。
6.示例

假设我们有两个进程a.outb.out,它们需要通过共享内存交换一个pid_t类型的进程ID。

a.out(写进程)

cs 复制代码
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <sys/ipc.h> 
#include <sys/shm.h> 
#include <sys/types.h> 
#include <unistd.h> 

int main() 
{ 
    key_t key = ftok("/tmp", 'A'); 
    int shmid = shmget(key, sizeof(pid_t), IPC_CREAT | 0666); 
    if (shmid == -1) 
    { 
        perror("shmget"); 
        exit(EXIT_FAILURE); 
    } 

    void *shmaddr = shmat(shmid, NULL, 0); 
    if (shmaddr == (void *)-1) 
    { 
        perror("shmat"); 
        exit(EXIT_FAILURE); 
    } 
    pid_t *pid_ptr = shmaddr; 
    *pid_ptr = getpid(); // 将当前进程的PID写入共享内存 
    shmdt(shmaddr); 
    shmctl(shmid, IPC_RMID, NULL); 
    return 0; 
}

b.out(读进程)

cs 复制代码
#include <stdio.h> 
#include <stdlib.h> 
#include <sys/ipc.h> 
#include <sys/shm.h> 
#include <sys/types.h> 
#include <unistd.h> 

int main() 
{ 
    key_t key = ftok("/tmp", 'A'); 
    int shmid = shmget(key, sizeof(pid_t), 0666); 
    if (shmid == -1) 
    { 
        perror("shmget"); 
        exit(EXIT_FAILURE); 
    } 
    void *shmaddr = shmat(shmid, NULL, 0); 
    if (shmaddr == (void *)-1) 
    { 
        perror("shmat"); 
        exit(EXIT_FAILURE); 
    } 
    pid_t *pid_ptr = shmaddr; 
    printf("Received PID: %d\n", *pid_ptr); 
    shmdt(shmaddr); 
    return 0; 
}
7.总结
  1. 共享内存数据的存储方式是拷贝还是剪切?

    答:在共享内存的情况下,数据是直接访问的,不涉及拷贝或剪切操作。进程直接通过映射的地址访问物理内存区域。

  2. 共享内存的数据如果多次不同进程读写会怎么样?

    答:如果多个进程读写同一块共享内存区域,并且没有适当的同步机制(如信号量),那么可能会出现数据竞争和覆盖的情况

相关推荐
SuperW12 分钟前
linux课程学习二——缓存
学习
xyliiiiiL15 分钟前
一文总结常见项目排查
java·服务器·数据库
shaoing17 分钟前
MySQL 错误 报错:Table ‘performance_schema.session_variables’ Doesn’t Exist
java·开发语言·数据库
lulinhao42 分钟前
HCIA/HCIP基础知识笔记汇总
网络·笔记
腥臭腐朽的日子熠熠生辉1 小时前
解决maven失效问题(现象:maven中只有jdk的工具包,没有springboot的包)
java·spring boot·maven
ejinxian1 小时前
Spring AI Alibaba 快速开发生成式 Java AI 应用
java·人工智能·spring
杉之1 小时前
SpringBlade 数据库字段的自动填充
java·笔记·学习·spring·tomcat
圈圈编码2 小时前
Spring Task 定时任务
java·前端·spring
俏布斯2 小时前
算法日常记录
java·算法·leetcode
27669582922 小时前
美团民宿 mtgsig 小程序 mtgsig1.2 分析
java·python·小程序·美团·mtgsig·mtgsig1.2·美团民宿