linux 之 system-v 共享内存
发布日期:2021-05-01 02:21:35 浏览次数:68 分类:技术文章

本文共 3922 字,大约阅读时间需要 13 分钟。

主要作用

  • 不同进程之间,高效率传输大量的数据。
  • 高效率是相对于之前谈及的 管道消息队列 而言(我的另外几篇博文中有)的。 管道和消息队列 需要借助 linux kernel 来进行数据转交,效率较低,适合传输少量数据。
  • 进程1对应的虚拟地址进程2对应的虚拟地址 映射到同样的物理空间。那么进程1往它的虚拟地址写,进程2读它自己的虚拟地址就能直接读到。

共享内存的用法

  • 定义一个唯一的 key (可以用ftok() 也可以认为指定键值 (key_t 0x666 ))
  • 构造一个 共享内存对象(shmget)
  • 共享内存映射(shmat)
  • 解除共享内存映射(shmdt)
  • 删除共享内存(shmctl RMID)

shmget函数

  • 功能:获取共享内存对象的id
  • 函数原型
int shmget(key_t key,int size,int shmflg);
  • 参数

    • key:共享内存键值(可以自己定义,也可以依靠ftok函数)

    • size:共享内存大小

    • shmflg:

      • IPC_CREAT:共享内存不存在则创建
      • mode:共享内存 rwx 权限
  • 返回值

    • 成功:共享内存id
    • 失败:-1

shmat函数

  • 功能:映射共享内存
  • 函数原型
void* shmat(int shmid,const void *shmaddr,int shmflg);
  • 参数

    • shmid:共享内存id
    • shmaddr:映射地址,为NULL时表示自动分配
    • shmflg:
      SHM_RDONLY:只读方式映射
      0:可读可写
  • 返回值

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

shmdt函数

  • 功能:解除共享内存映射
  • 函数原型
int shmdt(const void *shmaddr);
  • 参数

    • shmaddr:映射地址
  • 参数

    • 成功:0
    • 失败:-1

shmctl函数

  • 功能:获取或设置共享内存的相关属性
  • 函数原型
int shmctl(int shmid,int cmd,struct shmid_ds *buf);
  • 参数

    • shmid:共享内存id

    • cmd:

      IPC_STAT:获取共享内存的属性信息,由参数buf返回

      IPC_SET:设置共享内存的属性,由参数buf传入
      IPC_RMID:删除共享内存

    • buf:属性缓冲区

  • 返回值

    • 成功:由cmd类型决定,上面列出的这三个都返回-1.
    • 失败:-1

测试代码

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
union semun{ int val; struct semid_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.\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 semahore.\n"); return -1; }}// p 操作int sem_p(int sem_id){ pid_t a; struct sembuf sops; printf("%s(%d):pid = %d\n", __FILE__, __LINE__, getpid()); sops.sem_num = 0;//单个信号量的编号应该为0 sops.sem_op = -1;//表示 p 操作,减操作 sops.sem_flg = SEM_UNDO;//到最后系统会自动释放系统中残留的信号量 printf("%s(%d):pid = %d\n", __FILE__, __LINE__, getpid()); if(semop(sem_id,&sops,1) == -1)//不符合操作条件时会阻塞于此 { perror("P operation.\n"); return -1; } printf("%s(%d):pid = %d\n", __FILE__, __LINE__, getpid()); return 0;}// v 操作int sem_v(int sem_id){ pid_t a; struct sembuf sops; printf("%s(%d):pid = %d\n", __FILE__, __LINE__, getpid()); sops.sem_num = 0;//单个信号量的编号为 0 sops.sem_op = 1;//表示 v 操作 sops.sem_flg = SEM_UNDO;//系统自动释放残留的信号量 printf("%s(%d):pid = %d\n", __FILE__, __LINE__, getpid()); if(semop(sem_id,&sops,1) == -1)//不符合操作条件时会阻塞于此 { perror("V operation.\n"); return -1; } printf("%s(%d):pid = %d\n", __FILE__, __LINE__, getpid()); return 0;}#define DELAY_TIME 3int main(void){ pid_t result; int sem_id; int shm_id; char *addr; //创建一个信号量 sem_id = semget((key_t)6666, 1, 0666 | IPC_CREAT); //创建一个共享内存对象 shm_id = shmget((key_t)7777, 1024, 0666 | IPC_CREAT); //信号量的id,和信号量初始化的值为0 init_sem(sem_id, 0); //调用 fork() 函数 result = fork(); if(-1 == result) { perror("Fail fork.\n"); } else if(0 == result)//子进程 { printf("Child process will wait for some seconds ...\n"); sleep(DELAY_TIME); //映射共享内存 addr = shmat(shm_id,NULL,0); if(addr == (void *)-1) { printf("Fail shmat.\n"); exit(-1); } //设置共享内存中的内容 memcpy(addr,"HelloWorld",11); printf("The child process is running ...\n"); //给信号量的值加 1 sem_v(sem_id); } else//父进程 { //同步工作: //若信号量的值已经为0,那么这个减1的操作将阻塞于此 //从而保证子进程运行在父进程的前面 sem_p(sem_id); printf("The father process is running ...\n"); //映射共享内存的地址 addr = shmat(shm_id,NULL,0); if(addr == (void *)-1) { printf("Fail father shmat.\n"); exit(-1); } printf("Shared memory string : %s\n",addr); //解除共享内存映射 shmdt(addr); //删除共享内存映射 shmctl(shm_id,IPC_RMID,NULL); sem_v(sem_id); del_sem(sem_id); } exit(0);}

编译与运行

jl@jl-virtual-machine:~/test$ ./a.out test.c(48):pid = 19588test.c(52):pid = 19588Child process will wait for some seconds ...The child process is running ...test.c(67):pid = 19589test.c(71):pid = 19589test.c(77):pid = 19589test.c(58):pid = 19588The father process is running ...Shared memory string : HelloWorldtest.c(67):pid = 19588test.c(71):pid = 19588test.c(77):pid = 19588jl@jl-virtual-machine:~/test$

测试成功!

  • 补充

转载地址:https://blog.csdn.net/engineer0/article/details/109501632 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Manjaro安装
下一篇:SplashScreenManager控件介绍

发表评论

最新留言

网站不错 人气很旺了 加油
[***.192.178.218]2024年04月18日 19时15分00秒