进程间通信(5)——共享内存

0. 前言

共享内存可以说是最有用的进程间通信方式,也是最快的IPC 形式。两个不同的进程A、B共享内存的基本原理是,同一块物理内存映射到进程A、B各自的进程地址空间。进程A可以即时看到B对共享内存中数据的更新,反之亦然。

如图:

共享内存特点:

  • 共享内存是最快的IPC形式,因为内存映射到共享它的进程的地址空间,这些进程数据传递就不再涉及到内核了,也就是说说不再通过执行进入内核的系统调用来传递彼此的数据,所以,它的速度是最快的。
  • 共享内存的生命周期随进程,也需要显示地删除。
  • 共享内存没有互斥与同步机制,因此,我们在使用时,需要自己添加。

1. 创建共享内存

系统调用shmget() 创建或获取一个已经存在的共享内存的标识符,函数原型:

#include <sys/shm.h>       //里面包含 #include <sys/ipc.h>

int shmget(key_t key, size_t size, int shmflg);

参数:

  • key 共享内存的关键字,可以通过ftok() 创建,详细看进程间通信——消息队列
  • size 共享内存的大小
  • shmflg 有两个选项,IPC_CREAT 表示内核中没有此共享内存则创建它。IPC_EXCL 当和IPC_CREAT一起使用时,如果共享内存已经存在,则返回错误。

返回值:成功返回标识符,否则返回-1。通过errno和perror函数可以查看错误信息。

2. 连接共享内存

shmat() 可以获取一个共享内存的地址,并将其连接到进程中,函数原型:

#include <sys/shm.h>

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

参数:

  • shmid 共享内存的标识符。
  • shmaddr 指定的连接地址。若shmaddr取NULL,表示核心自动选择一个地址;若不为NULL且shmflg⽆SHM_RND标记,则以shmaddr为连接地址;若不为NULL且shmflg设置了SHM_RND标记,则连接的地址会⾃自动向下调整为SHMLBA的整数倍。公式:shmaddr - (shmaddr % SHMLBA)
  • shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY(只读共享内存)

返回值:成功返回一个指针,指向共享内存的第⼀个节;失败返回-1

3. 脱离共享内存

当一个进程不在需要共享内存时,用shmdt() 把共享内存从进程地址空间中脱离,但不等于将共享内存段从系统内核中删除。函数原型:

#include <sys/shm.h>

int shmdt(const void *shmaddr);

参数:shmaddr 为shmat() 函数的连接共享内存的地址。

返回值:成功返回0;失败返回-1

4. 控制共享内存

进程间通信——共享内存 ,在共享内存中标识符的属性被记录在一个shmid_ds的结构中:

struct shmid_ds{
    struct ipc_perm   shm_perm;         /* operation permission struct */
    size_t            shm_segsz;        /* size of segment in bytes */
    time_t            shm_amime;        /* time of last shmat() */
    time_t            shm_dtime;        /* time of last shmdt() */
    time_t            shm_ctime;        /* time of last change by shmctl() */
    pid_t             shm_cpid;         /* pid of creator */
    pid_t             shm_lpid;         /* pid of last shmdt */
    shmatt_t          shm_nattach;      /* number of current attaches */
    ...
}

通过 shmctl() 可以对共享内存进行控制或一些属性的修改,函数原型:

#include <sys/shm.h>

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

参数: 

  • shmid:共享内存的标识码 
  • cmd:有三个可选的值,在此我们使用IPC_RMID
选项功能
IPC_STAT把msqid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET进程有足够权限的前提下,把共享内存的当前关联值设置为msqid_ds数据结构中给出的值
IPC_RMID删除共享内存段

5. 实例

test_shm.h

#ifndef __TEST_SHARED_MEMORY_INCLUDE__
#define __TEST_SHARED_MEMORY_INCLUDE__

//include this header file for shared memory
#include <sys/shm.h>

#define shm_key "shm_key"
#define SHARE_BUF 16*1024

typedef struct{
    int flag;
} shm_buf_t;

#define END 1000

int create_shm();
int get_shm();
int remove_shm(int shmid);

#endif //#ifndef __TEST_SHARED_MEMORY_INCLUDE__

test_shm.c

#include <stdio.h>

#include "test_shm.h"

static int create_shm_common(int flags)
{
    printf("create shared memory\n");

    key_t key = ftok(shm_key, 's');
    if (key == -1) {
        perror("ftok error.\n");
        return -1;
    }
    printf("key is 0x%x\n", key);

    int shmid = shmget(key, SHARE_BUF, flags);
    if (shmid == -1) {
        perror("shmget error.\n");
        return -1;
    }
    printf("shmid is %d\n", shmid);

    return shmid;
}

int create_shm()
{
    return create_shm_common(IPC_CREAT | IPC_EXCL | 0644);
}

int get_shm()
{
    return create_shm_common(IPC_CREAT);
}

int remove_shm(int shmid)
{
    if (shmctl(shmid, IPC_RMID, NULL) == -1) {
        perror("shmctl error.\n");
        return -1;
    }

    return 0;
}

test_read.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "test_shm.h"


// server for testing
int main()
{
    printf("main of server for reading shm.\n");

    int shmid = create_shm();

    shm_buf_t *shbuf = shmat(shmid, 0, SHM_RDONLY);
    if (shbuf == (void*) -1L) {
        perror("shmat error.\n");
        return -1;
    }
    printf("test_read: Memory attched at %X\n", (int)shbuf);

    int n = 100;
    while (1) {
        printf("test_read: buf->flag = %d\n", shbuf->flag);
        
        if (shbuf->flag == END || n < 0)
            break;
        sleep(1);
    }

    return 0;
}

test_write.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <memory.h>

#include "test_shm.h"


// client for testing
int main()
{
    printf("main of server for writing shm.\n");

    int shmid = get_shm();

    shm_buf_t *shbuf = shmat(shmid, NULL, 0);
    if (shbuf == (void*) -1L) {
        perror("shmat error.\n");
        return -1;
    }
    printf("test_write: Memory attched at %X\n", (int)shbuf);

    memset(shbuf, 0, SHARE_BUF);
    shm_buf_t *buf = malloc(sizeof(shm_buf_t));

    int n = 10;
    while (n > 0) {
        if (n == 1) {
            buf->flag = END;
        } else {
            buf->flag = n;
            sleep(2);
        }

        printf("test_write: buf->flag = %d\n", buf->flag);
        memcpy(shbuf, buf, sizeof(shm_buf_t));
        n--;
    }

    free(buf);

    return 0;
}

运行结果:

./test_read 
main of server for reading shm.
create shared memory
key is 0x7300a4fe
shmid is 9273347
test_read: Memory attched at 3F52D000
test_read: buf->flag = 10
test_read: buf->flag = 9
test_read: buf->flag = 9
test_read: buf->flag = 8
test_read: buf->flag = 8
test_read: buf->flag = 7
test_read: buf->flag = 7
test_read: buf->flag = 6
test_read: buf->flag = 6
test_read: buf->flag = 5
test_read: buf->flag = 5
test_read: buf->flag = 4
test_read: buf->flag = 4
test_read: buf->flag = 3
test_read: buf->flag = 3
test_read: buf->flag = 1000
./test_write 
main of server for writing shm.
create shared memory
key is 0x7300a4fe
shmid is 9273347
test_write: Memory attched at 65DBA000
test_write: buf->flag = 10
test_write: buf->flag = 9
test_write: buf->flag = 8
test_write: buf->flag = 7
test_write: buf->flag = 6
test_write: buf->flag = 5
test_write: buf->flag = 4
test_write: buf->flag = 3
test_write: buf->flag = 2
test_write: buf->flag = 1000

相关博文:

进程间通信(0)——序 

进程间通信(1)——信号(Signal)

进程间通信(2)——管道(PIPE)

进程间通信(3)——命名管道(FIFO)

进程间通信(4)——消息队列

进程间通信(5)——共享内存

进程间通信(6)——信号量(semaphore​​​​​​)

进程间通信(7)——套接字(socket)

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
Linux下,进程间通信的一种方式是通过共享内存来实现的。共享内存允许两个或多个进程共享一定的存储区,这样它们就可以直接访问同一块内存区域,而不需要进行数据的复制。共享内存是一种高效的进程间通信方式,因为数据直接写入内存,不需要多次数据拷贝,所以传输速度很快\[2\]。 在使用共享内存进行进程间通信时,需要给共享内存创建一个唯一的身份ID,以便区分不同的共享内存。当进程需要访问共享内存时,需要在映射时带上这个ID,这样就可以确定访问的是哪一个共享内存\[3\]。 需要注意的是,共享内存并没有提供同步机制,也就是说,在一个进程结束对共享内存的写操作之前,并没有自动机制可以阻止另一个进程开始对它进行读取。为了实现多个进程对共享内存的同步访问,通常会使用信号量来实现对共享内存的同步访问控制\[2\]。 总结起来,Linux下的共享内存是一种高效的进程间通信方式,允许多个进程共享一块存储区。通过给共享内存创建唯一的身份ID,可以区分不同的共享内存。然而,共享内存并没有提供同步机制,需要使用信号量来实现对共享内存的同步访问控制\[2\]\[3\]。 #### 引用[.reference_title] - *1* *3* [Linux进程间通信&mdash;&mdash;共享内存实现](https://blog.csdn.net/zhm1949/article/details/124909541)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Linux进程间通信方式&mdash;&mdash;共享内存](https://blog.csdn.net/xujianjun229/article/details/118584955)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

私房菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值