要么改变世界,要么适应世界

进程之间使用共享内存通信

2020-04-22 21:41:00
119
目录

共享内存

共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。

但是它并未提供同步机制,即在某一个进程结束对共享内存的写操作之前,并不能可以阻止第二个进程开始对它进行读取,因此我们需要人为阻止通过其他机制进行同步。

shmget()函数

用于得到一个共享内存标识符或创建一个共享内存。

函数原型:

int shmget(key_t key, size_t size, int shmflg);
参数 说明
key 标识共享内存的键值,shmget()函数成功时返回一个与key相关的共享内存标识符(非负整数),用于后续的共享内存函数。调用失败返回-1.
size 指明共享内存的大小,以字节为单位
shmflg 权限标志
IPC_CREAT 如果共享内存不存在,则创建一个共享内存,否则打开操作。
IPC_EXCL 只有在共享内存不存在的时候,新的共享内存才建立,否则就产生错误。

shmat()函数

第一次创建完共享内存后,它还不能被任何进程访问,shmat()函数的作用就是用来启动对该共享内存的访问,并把共享内存连接到当前进程的地址空间。

原型:

void *shmat(int shmid, const void *shmaddr, int shmflg)
参数 说明
shmid 共享存储的id,如shmget()函数的返回值
shmaddr 指定共享内存连接到当前进程中的地址位置,一般为0,表示让内核自己决定一个合适的地址位置
shmflg SHM_RDONLY:为只读模式,其他为读写模式,如0

调用成功时返回一个指向共享内存第一个字节的指针,如果调用失败返回-1。

shmdt()函数

shmat函数相反,是用来断开与共享内存附加点的地址,禁止本进程访问此片共享内存,但是并不是删除该共享内存。

原型:

int shmdt(const void *shmaddr);

shmaddr:连接的共享内存的起始地址,如shmat()函数的返回值。调用成功的时候返回0,失败时返回-1。

shmctl()函数

控制共享内存。

原型:

int shmctl(int shm_id, int command, struct shmid_ds *buf);
参数 说明
shm_id 共享内存标识符,如shmget()的返回值
command 一些命令,如
IPC_STAT:得到共享内存的状态,把共享内存的shmid_ds结构复制到buf中
IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
IPC_RMID:删除这片共享内存
buf 共享内存管理结构体。

shmid_ds结构 至少包括以下成员:

struct shmid_ds
{
    uid_t shm_perm.uid;
    uid_t shm_perm.gid;
    mode_t shm_perm.mode;
};

例如一个简单的例子,父子进程通过共享内存的方式进行通信:

/*
 * @Author: YaleXin
 * @Date: 2020-04-22 14:22:16
 * @LastEditTime: 2020-04-22 21:38:34
 * @LastEditors: YaleXin
 * @Description:
 * @FilePath: \my_c_workspace\others\shareMemory.c
 * @祈祷不出现BUG
 */
#include <stdio.h>
#include <string.h>
#include <sys/shm.h>
#include <unistd.h>
struct shareMemoryDate {
    int mod;
    char msg[100];
};
int main() {
    int *shm = NULL;
    struct shareMemoryDate *share;
    // 创建一个共享内存
    // IPC_CREAT表示如果共享内存不存在,则创建一个共享内存,否则打开操作。
    // 需要与IPC对象存取权限(如0600)进行|运算来确定信号量集的存取权限
    int shm_id =
        shmget((key_t)0, sizeof(struct shareMemoryDate), 0600 | IPC_CREAT);
    if (shm_id == -1) {
        printf("创建共享内存失败,我也不知为什么\n");
        return 0;
    }
    int pId = fork();
    if (pId == -1) {
        printf("创建子进程失败,我也不知为什么\n");
    } else if (pId == 0) {
        //子进程
        // 将共享内存连接到当前进程的地址空间
        shm = shmat(shm_id, 0, 0);
        if (shm == NULL) {
            printf("链接失败,我也不知为什么\n");
            return 0;
        }
        share = (struct shareMemoryDate *)shm;
        char buffer[100];
        printf("我是子进程,请输入想要写入共享内存的内容:\n");
        fgets(buffer, sizeof(buffer), stdin);
        strcpy(share->msg, buffer);
        // 表示将数据写入完毕
        share->mod = 1;
        // 把共享内存从当前进程中分离
        if (shmdt(shm) == -1) {
            printf("分离失败,我也不知为什么\n");
        }
    } else if (pId > 0) {
        //父进程
        // 将共享内存连接到当前进程的地址空间
        shm = shmat(shm_id, 0, 0);
        if (shm == NULL) {
            printf("链接失败,我也不知为什么\n");
            // 删除共享内存
            if (shmctl(shm_id, IPC_RMID, 0) == -1) {
                printf("(无法链接)删除共享内存失败,我也不知为什么\n");
                return 0;
            }
        }
        share = (struct shareMemoryDate *)shm;
        //等待子进程写入完毕
        while (share->mod != 1) sleep(1);
        printf("我是父进程,让我看看我的子进程在共享内存里边写了什么东西:\n");
        printf("%s\n", share->msg);
        printf("噢,原来是这些鬼玩意\n");
        // 删除共享内存
        if (shmctl(shm_id, IPC_RMID, 0) == -1) {
            printf("(读取完毕)删除共享内存失败,我也不知为什么\n");
        }
    }
    return 0;
}

调试:

yalexin@yalexin-PC:/media/yalexin/软件/my_c_workspace/others$ gcc -o share shareMemory.c
yalexin@yalexin-PC:/media/yalexin/软件/my_c_workspace/others$ ./share
我是子进程,请输入想要写入共享内存的内容:
你好啊
我是父进程,让我看看我的子进程在共享内存里边写了什么东西:
你好啊

噢,原来是这些鬼玩意
yalexin@yalexin-PC:/media/yalexin/软件/my_c_workspace/others$ 
历史评论
开始评论