Linux进程间通信-共享存储

什么是共享存储

顾名思义,共享存储段允许多个进程访问同一个存储区域。使用时,将共享的存储空间的地址连接到需要通信的进程中。但是,共享存储段并没有实现同步机制,需要自行使用信号量作为同步。

在这里插入图片描述
在这里插入图片描述

内核为每个共享存储都维护着一个结构:

struct shmid_ds {
	struct ipc_perm		shm_perm;	/* operation perms */
	int			        shm_segsz;	/* size of segment (bytes) */
	__kernel_time_t		shm_atime;	/* last attach time */
	__kernel_time_t		shm_dtime;	/* last detach time */
	__kernel_time_t		shm_ctime;	/* last change time */
	__kernel_ipc_pid_t	shm_cpid;	/* pid of creator */
	__kernel_ipc_pid_t	shm_lpid;	/* pid of last operator */
	unsigned short		shm_nattch;	/* no. of current attaches */
	unsigned short 		shm_unused;	/* compatibility */
	void 			*shm_unused2;	/* ditto - used by DIPC */
	void			*shm_unused3;	/* unused */
};

获取共享存储段

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

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

参数:

  • key: 关键值。
  • size: 共享存储的长度,以字节为单位。
    • 创建存储段:通常取系统页长PAGE_SIZE的整数倍。
    • 引用存储段:赋值为0即可。
  • shmflg:
    • IPC_CREAT: 创建新的共存储段或者引用已存在的存储段。
      • 如果不存在,则创建新的共享内存段,并返回段的ID。(表示创建)
      • 如果已存在且未指定IPC_EXCL,则返回段的ID。(表示引用)
      • 如果已存在且制定了IPC_EXCL,则返回-1并设置errno。(表示关键值正在使用,创建失败)
    • IPC_EXCL: 配合IPC_CREAT使用。
    • 权限: 八进制权限。

返回值

  • 成功:返回共享存储段的ID。
  • 失败:返回-1,并设置errno。

连接共享存储段

#include <sys/types.h>
#include <sys/shm.h>

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

参数

  • shmid: 共享存储段的ID
  • shmaddr:
    • 为0,内核分配一个可用地址。
    • 不为0,并且没有指定SHM_RND,则连接到shmaddr指定的地址上。
    • 不为0,并且指定了SHM_RND,则连接到shmaddr mod SHMLBA所表示的地址上。
  • shmflg:
    • SHM_RND: 取整。
    • SHM_RDONLY: 以只读的方式连接。
    • SHM_REMAP: take-over region on attach
    • SHM_EXEC: execution access

返回值

  • 成功:连接地址。
  • 失败:(void*)-1,设置errno。

操作共享存储段

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

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

参数

  • shmid: 共享存储段的ID。
  • cmd:操作指令。
    • IPC_STAT: 取此段的shmid_ds结构,并将它存储在由buf指向的结构体中。
    • IPC_SET: 按buf指向的结构中的值设置与此相关的shmid_ds结构中的shm_perm.uid, shm_perm.gid, shm_perm.mode。该命令只能由具有超级用户特权的用户或者有效用户ID等于shm_perm.cuid或shm_perm.uid的进程。
    • IPC_RMID: 从系统中删除该共享存储段。因为每个共享存储段维护着一个连接计数(shmid_ds结构中的shm_nattach字段),所以除非使用该段的最后一个j进程终止或与该段分离,否则不会实际上删除该存储段。不管此段是否仍在使用,该段的标识符立即被删除,所以不能再连接。该命令只能由具有超级用户特权的用户或者有效用户ID等于shm_perm.cuid或shm_perm.uid的进程。

返回值

  • 成功:根据cmd的值返回不同的值:
    • IPC_INFO、SHM_INFO: 内核中共享内存段的索引
    • SHM_STAT: shmid
    • 其它:0
  • 失败:-1,设置errno

分离共享存储段

#include <sys/types.h>
#include <sys/shm.h>

int shmdt(const void* shmaddr);

参数

  • shmaddr:要删除的连接到共享存储段的地址,也就是shmat()返回的地址。

返回值

  • 成功:0。
  • 失败:-1,设置errno。

示例

//sem_writer.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <signal.h>


#define SHM_SIZE    4096


int shm_id;
int sem_id;


union semun
{
    int val;
    struct semid_ds* buf;
    unsigned short* array;
    struct seminfo* __buf;
    void* __pad;
};


int sig_handler(int signum)
{
    shmctl(shm_id, IPC_RMID, NULL);
    semctl(sem_id, 0, IPC_RMID);
    exit(0);
}


int main()
{
    // 注册信号
    signal(SIGINT, sig_handler);
    signal(SIGKILL, sig_handler);

    // 获得关键值
    key_t  shm_key = ftok(".", 1);
    key_t  sem_key = ftok(".", 2);

    // 创建共享存储段
    shm_id = shmget(shm_key, SHM_SIZE, IPC_CREAT | 0600);
    if(shm_id < 0)
    {
        perror("shmget error");
        exit(-1);
    }
    printf("shm id: %d\n", shm_id);

    // 创建信号量
    sem_id = semget(sem_key, 1, IPC_CREAT | 0600);
    if(sem_id < 0)
    {
        perror("semget error");
        exit(-1);
    }
    printf("sem id: %d\n", sem_id);

    // 操作信号量
    union semun sem_un;
    sem_un.val = 1;
    semctl(sem_id, 0, SETVAL, sem_un);
    struct sembuf sem_v = {0, 1, 0};

    // 连接共享存储段
    void* shm_addr = shmat(shm_id, NULL, SHM_RND);
    if(shm_addr == (void*)-1)
    {
        perror("shmat error");
        shmctl(shm_id, IPC_RMID, NULL);
        exit(-1);
    }

    // 操作共享存储段
    while(1)
    {
        int n = read(0, (char*)shm_addr, SHM_SIZE - 1);
        *(char*)(shm_addr + n) = '\0';  
        semop(sem_id, &sem_v, 1);
        sleep(1);
    }

    return 0;
}
//shm_writer.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <signal.h>


#define SHM_SIZE    4096


int shm_id;
int sem_id;


union semun
{
    int val;
    struct semid_ds* buf;
    unsigned short* array;
    struct seminfo* __buf;
    void* __pad;
};


int sig_handler(int signum)
{
    shmctl(shm_id, IPC_RMID, NULL);
    semctl(sem_id, 0, IPC_RMID);
    exit(0);
}


int main()
{
    // 注册信号
    signal(SIGINT, sig_handler);
    signal(SIGKILL, sig_handler);

    // 获得关键值
    key_t  shm_key = ftok(".", 1);
    key_t  sem_key = ftok(".", 2);

    // 创建共享存储段
    shm_id = shmget(shm_key, SHM_SIZE, IPC_CREAT | 0600);
    if(shm_id < 0)
    {
        perror("shmget error");
        exit(-1);
    }
    printf("shm id: %d\n", shm_id);

    // 创建信号量
    sem_id = semget(sem_key, 1, IPC_CREAT | 0600);
    if(sem_id < 0)
    {
        perror("semget error");
        exit(-1);
    }
    printf("sem id: %d\n", sem_id);

    // 操作信号量
    union semun sem_un;
    sem_un.val = 1;
    semctl(sem_id, 0, SETVAL, sem_un);
    struct sembuf sem_p = {0, -1, 0};

    // 连接共享存储段
    void* shm_addr = shmat(shm_id, NULL, SHM_RND);
    if(shm_addr == (void*)-1)
    {
        perror("shmat error");
        shmctl(shm_id, IPC_RMID, NULL);
        exit(-1);
    }

    // 操作共享存储段
    while(1)
    {
        semop(sem_id, &sem_p, 1);
        printf("%s", (char*)shm_addr);
        sleep(1);
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值