十、进程间通信-共享内存

一、概述

 

 

 

  • 共享内存用于用于进程间的数据共享,

  • 开辟一块物理内存空间, 各个进程将同一块物理内存空间映射到自己的虚拟地址空间中, 通过虚拟地址进行访问, 进而实现数据共享

  • 共享内存是最快的进程间通信方式, 因为通过虚拟地址空间映射后, 直接通过虚拟地址访问物理内存, 相较于其他方式少了两步数据拷贝的操作.

  • 用于高效率传输大量数据

二、共享内存的用法

1、定义一个唯一key(ftok)

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

2、构造一个共享内存对象(shmget)

(1)头文件

1
2
#include <sys/ipc.h>
#include <sys/shm.h>

 (2)函数原型

1
int shmget(key_t key,int size,int shmflg)

(3)参数

  • key:  键值;

  • size: 共享内存的大小。

  • shmflg:

    •  IPC_CREATE:共享内存不存在则创建 

    • mode:共享内存的权限

(4)返回值 

  • 成功:共享内存ID;

  • 失败:-1

3、共享内存映射(shmat)

1
int shmat(int shmid,const void *shmaddr,int shmflg)

(1)参数

  • shmid:共享内存ID

  • shmaddr:映射地址,NULL为自动分配

  • shmflg:

    •  SHM_RDONLY:只读方式映射

    •  0:可读可写

(2)返回值

  • 成功:共享内存首地址
  • 失败:-1

4、解除共享内存映射(shmdt)

1
int shmdt(const void *shmaddr)

   (1)参数

  • shmaddr:映射的地址

(2)返回值 

  • 成功:0
  • 失败:-1

5、删除共享内存(shmctl RMID)

1
int shmctl(int shmid,int cmd,struct shmid_ds *buf)  //获取或设置共享内存的相关属性

(1)参数:

  • shmid:共享内存ID

  • cmd

    • IPC_STAT:获取共享内存的属性信息
    • IPC_SET:设置共享内存的属性
    •  IPC_RMID:删除共享内存
  •  buf:属性缓冲区

(2)返回值:

  • 成功:由cmd类型决定
  • 失败:-1

三、实例

  使用共享内存,子进程拷贝数据到共享内存,父进程读取共享内存的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include <sys/shm.h>
 
#define DELAY_TIME 3
union semun{
    int val;
    struct seimd_ds *buf;
};
 
 
/*初始化信号量*/
int init_sem(int sem_id,int init_value)
{
    union semun sem_union;
    sem_union.val = init_value;
      /*设置信号量集中的一个单独的信号量的值*/
    if(semctl(sem_id,0,SETVAL,sem_union) == -1)
    {
        printf("Initialize semaphore failed!\n");
        return -1;
    }
    return 0;
}
 
/*删除信号量*/
int del_sem(int sem_id)
{
    union semun sem_union;
    if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
    {
        perror("Delete semaohore fail!\n");
        return -1;
    }
    return 0;
}
 
/*P 操作:执行P操作时,信号量-1*/ 
 
int sem_p(int sem_id)
{
    struct sembuf sops;
    sops.sem_num = 0;
    sops.sem_op  = -1;
    sops.sem_flg = SEM_UNDO;/*系统自动释放将会在系统中残留的信号量*/
 
    if(semop(sem_id,&sops,1) == -1)
    {
        perror("P operation failed\n");
        return -1;
    }
    return 0;
}
 
/*V 操作:执行P操作时,信号量+1*/
int sem_v(int sem_id)
{
    struct sembuf sops;
    sops.sem_num = 0;   /*单个信号量的编号应该为 0*/
    sops.sem_op  = 1;    /*表示V操作*/
    sops.sem_flg = SEM_UNDO;  /*系统自动释放将会在系统中残留的信号量*/
 
    if(semop(sem_id,&sops,1) == -1)
    {
        perror("V operation failed\n");
        return -1;
    }
    return 0;
}
 
void main(void)
{
    pid_t result;
    int sem_id;
    int shm_id;
    char* addr;
   //这里6666的key也可以用ftok去生成
    sem_id = semget((key_t)6666,1,0666|IPC_CREAT);/*创建一个信号量*/
     
    shm_id = shmget((key_t)7777,1024,0666|IPC_CREAT); /*创建一个共享内存对象*/
 
    init_sem(sem_id,0);  //初始化信号量的值为0,只能先执行V操作+1,然后才能执行P操作-1
 
    /*调用fork()函数*/
    result = fork();
    if(result == -1)
    {
        perror("Fork\n");
    }
    else if(result == 0)
    {
        printf("Child proess will wait for some seconds...\n");
        sleep(DELAY_TIME); 
        /*映射共享内存*/
        addr = shmat(shm_id,NULL,0);
        if(addr == (void*)-1)
        {
            printf("shmat111 error!");
            exit(-1);
        }
        /*设置共享内存的内容*/
        memcpy(addr,"helloworld",11);
        printf("the child process is running...\r\n");
        sem_v(sem_id); //如果有其他进程因等待信号量而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.
    }
    else /*返回值大于0代表父进程*/
    {
        sem_p(sem_id); //如果信号量等于0,则进程挂起,直到信号量大于0,执行V操作
        printf("the father process is running...\r\n");
        /*映射共享内存地址*/
        addr = shmat(shm_id,NULL,0);
          if(addr == (void*)-1)
        {
            printf("shmat222 error!");
            exit(-1);
        }
        printf("shared memory string:%s\r\n",addr);
 
        /*解除共享内存映射*/
        shmdt(addr);
 
        /*删除共享内存映射*/
        shmctl(shm_id,IPC_RMID,NULL);
 
        /*删除信号量*/
        del_sem(sem_id);
    }
     
    exit(0);
}

执行结果:

 

 父进程读出了共享内存地址的数据helloworld,代码中的信号量用于共享资源的互斥。

  

  

posted @   轻轻的吻  阅读(146)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示