Linux信号量同步共享内存实验.


简述

  1. 本文主要内容是自己对信号量和共享内存系统函数的整理,及简单使用,以方便以后可能再次使用的情况.也可以为比较熟悉信号量和共享内存的人方便的回忆使用方法.

实验简述. 
1.本实验程序有两个进程,一个写,一个读. 
2.写进程不断向创建的共享内存写数据. 
3.读进程通过getchar()共享内存的最新数据. 
4.读写共享内存时通过信号量同步.

程序流程

信号量和共享内存的系统函数

信号量系统函数及接口
描述 获取一个信号量集的标识符.
头文件 #include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
原型 int semget(key_t key, int num_sems, int sem_flags);
参数 key:整数值(唯一非零),多个进程可以通过它访问同一个信号量,其中有个特殊值IPC_PRIVATE用于创建当前进程的私有信号量。
—-
nsems:指定需要创建的信号量数目,它的值几乎总是1。
—- 
sem_flags: sem_flags是一组标志,同open()权限位,可以用八进制标识;当想要当信号量不存在时创建一个新的信号量,可以和值IPC_CREAT做按位或操作。设置了IPC_CREAT标志后,即使给出的键是一个已有信号量的键,也不会产生错误。而IPC_CREAT|IPC_EXCL则可以创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误。
返回值 成功: 信号标识符(非零) 
失败:-1.
描述 对信号量集为semid的集合中的一个或多个信号量进行P或V操作
头文件 #include<sys/types.h>
#include<sys/sem.h>
#include<sys/ipc.h>
原型 int semop(int semid, struct sembuf *sops, size_t nsops);
参数 key:整数值(唯一非零),多个进程可以通过它访问同一个信号量,其中有个特殊值IPC_PRIVATE用于创建当前进程的私有信号量。
—-
sops:指向一个信号量操作数组的指针,其定义如下
struct sem_buf{ 
unsigned short sem_num; /* 信号量集合中信号量编号,0表示第一个信号量 /
short sem_op; /
 要进行的操作P:-1,V:+1*/
short sem_flg; /* 0 :设置信号量默认操作
IPC_NOWAIT设置信号量操作不等待 
SEM_UNDO 让内核记录一个调用进程相关的UNDO记录,如果进程退出或崩溃,内核会自动释放进程占用的信号量资源*/
}
—- 
nsops: nsops指要操作的信号量个数,即sops数组中操作个数(>=1).通常取1
返回值 成功: 信号标识符(非零) 
失败:-1.
描述 通过cmd参数对信号量集执行特殊的控制操作
头文件 #include<sys/types.h>
#include<sys/sem.h>
#include<sys/ipc.h>
原型 int semctl(int semid, int semnum, int cmd, …);
参数 semid:信号量集标识符.
—-
semnum:此函数包含三或四个参数,取决于cmd,但都必包含该共用体,其定义如下
union semun{ 
int val; /*cmd为SETVAL时,设置的值*/
struct semid_ds *buf; /* IPC_STAT, IPC_SET操作的缓存*/
unsigned short *array; /* GETALL, SETALL操作的数组*/
struct seminfo *__buf; /*IPC_INFO操作的缓存 */ 
}
—- 
cmd: 要对信号量进行的操作,例几个常用的:
IPC_RMID:从内核删除信号量. 
SETVAL: 使用 semun的val成员值设置信号量集合中单个信号量的值 
GETVAL:返回信号量集合内单个信号量的值
返回值 成功: IPC_RMD,SETVAL:0 GETVAL:信号量当前值 
失败:-1.
  1. #include "sem.h"
  2. #include "debug.h"
  3. #define IPCKEY_PATH "/"
  4. #define SEM_NUMS 1
  5. #define SEM_OPS_NUM 1
  6. #define ACCESS_BIT 0666
  7. #define TAG "SemInterface"
  8. int sem_new(unsigned char projid, int init_val)
  9. {
  10. key_t key;
  11. int semid;
  12. union semun sem_union;
  13. key = ftok(IPCKEY_PATH, projid);
  14. if(key < 0)
  15. {
  16. LOG_E("ftok error: %s\n", strerror(errno));
  17. return SEM_FAILURE;
  18. }
  19. semid = semget(key, SEM_NUMS, ACCESS_BIT|IPC_CREAT|IPC_EXCL);
  20. if(semid < 0)
  21. {
  22. if(errno == EEXIST)
  23. {
  24. LOG_E("sem exist: %s\n", strerror(errno));
  25. return SEM_EXIST;
  26. }
  27. LOG_E("create sme error: %s\n", strerror(errno));
  28. return SEM_FAILURE;
  29. }
  30. sem_union.val = init_val;
  31. if((semctl(semid, 0, SETVAL, sem_union)) < 0)
  32. {
  33. LOG_E("set sem val error: %s\n", strerror(errno));
  34. return SEM_FAILURE;
  35. }
  36. return semid;
  37. }
  38. int sem_get(unsigned char proj_id)
  39. {
  40. key_t key;
  41. int semid;
  42. union semun sem_union;
  43. key = ftok(IPCKEY_PATH, proj_id);
  44. if(key < 0)
  45. {
  46. LOG_E("ftok error: %s\n", strerror(errno));
  47. return SEM_FAILURE;
  48. }
  49. semid = semget(key, SEM_NUMS, ACCESS_BIT);
  50. if(semid < 0)
  51. {
  52. LOG_E("create sme error: %s\n", strerror(errno));
  53. return SEM_FAILURE;
  54. }
  55. return semid;
  56. }
  57. int sem_p(int semid)
  58. {
  59. struct sembuf sem_buf;
  60. sem_buf.sem_num = 0;
  61. sem_buf.sem_op = -1;
  62. sem_buf.sem_flg = SEM_UNDO;
  63. if(semop(semid, &sem_buf, SEM_OPS_NUM) < 0)
  64. {
  65. LOG_E("sem P opration error: %s", strerror(errno));
  66. return SEM_FAILURE;
  67. }
  68. return SEM_SUCCESS;
  69. }
  70. int sem_v(int semid)
  71. {
  72. struct sembuf sem_buf;
  73. sem_buf.sem_num = 0;
  74. sem_buf.sem_op = 1;
  75. sem_buf.sem_flg = SEM_UNDO;
  76. if(semop(semid, &sem_buf, SEM_OPS_NUM) < 0)
  77. {
  78. LOG_E("sem V opration error: %s", strerror(errno));
  79. return SEM_FAILURE;
  80. }
  81. return SEM_SUCCESS;
  82. }
  83. int sem_del(int semid)
  84. {
  85. union semun sem_union;
  86. if(semctl(semid, 0, IPC_RMID, sem_union) < 0)
  87. {
  88. LOG_E("remove sem error: %s\n", strerror(errno));
  89. return SEM_FAILURE;
  90. }
  91. return SEM_SUCCESS;
  92. }
共享内存系统函数及接口
描述 创建共享内存。也就是从内存中获得一段共享内存区域.
头文件 #include<sys/ipc.h>
#include<sys/shm.h>
原型 int shmget(key_t key, size_t size, int shmflg);
参数 key:同信号量也是Linux IPC共通的整数值(唯一非零),共享内存的键值, 这个进程可以通过它访问同一个共享内存,其中有个特殊值IPC_PRIVATE,用于创建当前进程的私有共享内存 。
—-
size:要创建共享内存的大小。
—- 
shmflags: sem_flags是一组标志,同open()权限位,可以用八进制标识;当共享内存不存在时创建一个新的共享内存,可以和值IPC_CREAT做按位或操作。设置了IPC_CREAT标志后,即使给出的键是一个已有共享内存的键,也不会产生错误。而IPC_CREAT|IPC_EXCL则可以创建一个新的,唯一的共享内存,如果共享内存已存在,返回一个错误。
返回值 成功: 共享内存标识符(非零) 
失败:-1.
描述 映射共享内存。也就是把这段创建的共享内存映射到具体的进程空间中,这里使用的函数是shmat(),到这一步就可以使用这段共享内存了,也就是可以使用不带缓冲的I/O读写命令对其进行操作。
头文件 #include<sys/ipc.h>
#include<sys/shm.h>
原型 void *shmat(int shmid, const void *shmaddr, int shmflg);
参数 shmid:想要映射的共享内存标识符
—-
shmaddr:将共享内存映射到指定地址,注意这里的是一个void型的指针(为0则表示系统自动分配地址并把该段共享内存映射到调用进程的地址空间)
—- 
shmflg: 一组位屏蔽标志:
SHM_RDONLY : 共享内存只读.
默认 0 : 共享内存可读写.
返回值 成功: 映射到进程内的共享内存段地址. 
失败:-1.
描述 分离撤销调用进程通过shmat创建的共享内存的地址映射
原型 int shmdt(const void *shmaddr);
头文件 #include<sys/ipc.h>
#include<sys/shm.h>
参数 shmaddr:映射到进程内的共享内存段地址.
返回值 成功: 0. 
失败:-1.
描述 通过cmd参数对共享内存执行特殊的控制操作
头文件 #include<sys/ipc.h>
#include<sys/shm.h>
原型 int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数 shmid:共享内存标识符
—-
cmd:例举两个:
IPC_STAT: 拷贝内核空间的shmid关联的shmid_ds结构体到指向shmid_ds的指针buf指向的结构体中.
IPC_RMID:删除共享内存,实际上,仅在最后一个使用此共享内存的进程与其分离后才会被销毁.使用此命令,第三个参数会被忽略.
—- 
buf: 指向shmid_ds的结构体:
struct shmid_ds {
struct ipc_perm shm_perm; /* 所有权及权限 */
size_t shm_segsz; /* 段大小(byts) */
time_t shm_atime; /*最后一次映射时间*/
time_t shm_dtime; /* 最后一次分离时间 */
time_t shm_ctime; /*最后一次改变的时间*/
pid_t shm_cpid; /*创建者的进程ID */
pid_t shm_lpid; /* 最后调用shmat/shmdt的进程ID*/
shmatt_t shm_nattch; /* No. of current attaches */
};
返回值 成功: 
IPC_STAT: 0 
IPC_RMID: 0. 
失败:-1.
  1. #include "shmem.h"
  2. #include "debug.h"
  3. #define IPCKEY_PATH "/"
  4. #define ACCESS_BIT 0666
  5. #define TAG "ShareMem"
  6. int shm_new(unsigned char PROJID, size_t size)
  7. {
  8. int shmid;
  9. key_t key;
  10. key = ftok(IPCKEY_PATH, PROJID);
  11. if(key < 0)
  12. {
  13. LOG_E("get IPC key error: %s\n", strerror(errno));
  14. return SHM_FAILURE;
  15. }
  16. shmid = shmget(key, size, ACCESS_BIT|IPC_CREAT);
  17. if(shmid < 0)
  18. {
  19. LOG_E("get share mem error: %s\n", strerror(errno));
  20. return SHM_FAILURE;
  21. }
  22. return shmid;
  23. }
  24. char *shem_get_addr(int shmid)
  25. {
  26. char *p;
  27. if((p = (shmat(shmid, NULL, 0))) == (char *)-1)
  28. {
  29. LOG_E("get share mem addr error: %s\n", strerror(errno));
  30. return NULL;
  31. }
  32. return p;
  33. }
  34. int shm_del(int shmid)
  35. {
  36. if(shmctl(shmid, IPC_RMID, NULL) < 0)
  37. {
  38. LOG_E("remove share mem error: %s\n", strerror(errno));
  39. return SHM_FAILURE;
  40. }
  41. return SHM_SUCCESS;
  42. }
  43. int shm_detach(char *shmaddr)
  44. {
  45. if(shmdt(shmaddr) < 0)
  46. {
  47. LOG_E("share mem detach error: %s\n", strerror(errno));
  48. return SHM_FAILURE;
  49. }
  50. return SHM_SUCCESS;
  51. }
  52. void shm_read(char *buf, char *shmaddr)
  53. {
  54. strncpy(buf, shmaddr, strlen(shmaddr) + 1);
  55. }
  56. void shm_write(char *shmaddr, char *buf)
  57. {
  58. strncpy(shmaddr, buf, strlen(buf) + 1);
  59. }
  60. void shm_data_init(char *shmaddr)
  61. {
  62. memset(shmaddr, 0, SHM_SIZE);
  63. //strncpy(shmaddr, INIT_DATA, strlen(INIT_DATA) + 1);
  64. }

写程序

写进程通过fgets()模拟输入,从终端读取数据写入共享内存,遇到exit时退出程序.

  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <unistd.h>
  4. #include "debug.h"
  5. #include "sem.h"
  6. #include "shmem.h"
  7. #define SEM_PROJID '*'
  8. #define SHM_PROJID '-'
  9. #define TAG "SemServer"
  10. //#define ENDOFSTR '\0'
  11. #define SHM_SIZE 0x1F4000
  12. int main()
  13. {
  14. int semid;
  15. int shmid;
  16. char *shmaddr;
  17. char buf[SHM_SIZE];
  18. semid = sem_new(SEM_PROJID, 1);
  19. if(SEM_FAILURE == semid)
  20. {
  21. exit(-1);
  22. } else if(SEM_EXIST == semid)
  23. {
  24. if((semid = sem_get(SEM_PROJID)) < 0)
  25. {
  26. exit(-1);
  27. }
  28. LOG_D("get sem success\n");
  29. }
  30. else LOG_D("get new sem success\n");
  31. shmid = shm_new(SHM_PROJID, SHM_SIZE);
  32. if(SEM_FAILURE == shmid)
  33. {
  34. exit(-1);
  35. }
  36. LOG_D("get share mem success\n");
  37. shmaddr = shem_get_addr(shmid);
  38. if(NULL == shmaddr)
  39. {
  40. exit(-1);
  41. }
  42. shm_data_init(shmaddr);
  43. while(1)
  44. {
  45. if(fgets(buf, SHM_SIZE, stdin) == NULL)
  46. {
  47. LOG_E("get std input error: %s", strerror(errno));
  48. exit(-1);
  49. }
  50. buf[strlen(buf) - 1] = '\0';
  51. sem_p(semid);
  52. shm_write(shmaddr, buf);
  53. LOG_D("you write : %s\n",buf);
  54. sem_v(semid);
  55. if(strncmp(buf, "exit", 4) == 0)
  56. {
  57. LOG_D("process finish exit\n");
  58. break;
  59. }
  60. }
  61. if(shm_detach(shmaddr) == SHM_FAILURE)
  62. {
  63. exit(-1);
  64. }
  65. LOG_D("process exit\n");
  66. return 0;
  67. }

读程序

读进程通过getchar(),输入回车后, 开始读取共享内存中最新一次的数据.遇到exit后退出程序.

  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <unistd.h>
  4. #include "debug.h"
  5. #include "sem.h"
  6. #include "shmem.h"
  7. #define SEM_PROJID '*'
  8. #define SHM_PROJID '-'
  9. #define TAG "SemServer"
  10. #define SHM_SIZE 0x1F4000
  11. int main()
  12. {
  13. int semid;
  14. int shmid;
  15. char *shmaddr;
  16. char buf[SHM_SIZE];
  17. semid = sem_new(SEM_PROJID, 1);
  18. if(SEM_FAILURE == semid)
  19. {
  20. exit(-1);
  21. } else if(SEM_EXIST == semid)
  22. {
  23. if((semid = sem_get(SEM_PROJID)) < 0)
  24. {
  25. exit(-1);
  26. }
  27. LOG_D("get sem success\n");
  28. }
  29. else LOG_D("get new sem success\n");
  30. shmid = shm_new(SHM_PROJID, SHM_SIZE);
  31. if(SEM_FAILURE == shmid)
  32. {
  33. exit(-1);
  34. }
  35. LOG_D("get share mem success\n");
  36. shmaddr = shem_get_addr(shmid);
  37. if(NULL == shmaddr)
  38. {
  39. exit(-1);
  40. }
  41. while(1)
  42. {
  43. getchar();
  44. sem_p(semid);
  45. shm_read(buf, shmaddr);
  46. LOG_D("you read : %s\n",buf);
  47. sem_v(semid);
  48. if(strncmp(buf, "exit", 4) == 0)
  49. {
  50. LOG_D("process finish exit\n");
  51. break;
  52. }
  53. }
  54. if(shm_del(shmid) == SHM_FAILURE)
  55. {
  56. exit(-1);
  57. }
  58. LOG_D("process exit\n");
  59. return 0;
  60. }

程序测试:

Linux信号量同步共享内存实验.的更多相关文章

  1. linux进程间通信同步-共享内存

    参考:https://www.cnblogs.com/charlesblc/p/6142868.html 使用有名信号量,sem_open().sem_close().sem_post().sem_w ...

  2. Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存

    Linux进程间通信--进程,信号,管道,消息队列,信号量,共享内存 参考:<linux编程从入门到精通>,<Linux C程序设计大全>,<unix环境高级编程> ...

  3. 进程间通信IPC:消息队列,信号量,共享内存

    2015.3.4星期三 阴天 进程间通信:IPC 文件对象:记录文件描述符,文件开关等 IPC标示符:系统全局的流水号两个进程要通信,打开的是唯一的对象进行通讯,通过key操作 XSI IPC:消息队 ...

  4. Linux环境进程间通信: 共享内存

    Linux环境进程间通信: 共享内存 第一部分 共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式.两个不同进程A.B共享内存的意思是,同一块物理内存被映射到进程A.B各自的进程地址空间.进 ...

  5. linux编程之共享内存

    linux 进程间通信(IPC)包括3种机制:消息队列.信号量.共享内存.消息队列和信号量均是内核空间的系统对象,经由它们 的数据需要在内核和用户空间进行额外的数据拷贝:而共享内存和访问它的所有应用程 ...

  6. Linux IPC之共享内存C 事例

    Linux IPC之共享内存 标签: linuxrandomnull工作 2011-08-25 11:52 4123人阅读 评论(0) 收藏 举报  分类: Linux(3)  读书札记(3)  版权 ...

  7. Linux进程间通信—使用共享内存

    Linux进程间通信-使用共享内存 转自: https://blog.csdn.net/ljianhui/article/details/10253345 下面将讲解进程间通信的另一种方式,使用共享内 ...

  8. 转:Linux--进程间通信(信号量,共享内存)

    源地址:http://www.cnblogs.com/forstudy/archive/2012/03/26/2413724.html Linux--进程间通信(信号量,共享内存)(转)   一. 信 ...

  9. Linux进程间通信(四) - 共享内存

    共享内存的优势 采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝.对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只 ...

随机推荐

  1. Elasticsearch技术解析与实战(一)基础概念及环境搭建

    序言 ES数据架构的主要概念(与关系数据库Mysql对比) 集群(cluster) 集群,一个ES集群由一个或多个节点(Node)组成,每个集群都有一个cluster name作为标识.一下是我们的4 ...

  2. 即时新闻展示插件jQuery News Ticker,超级简单!

    有时候我们为了节省页面空间,会在页面明显处放一小条,用来展示比较重要的即时新闻,一般以轮播的形式出现.今天要介绍的jQuery News Ticker插件就是用来实现这个即时新闻展示功能的,效果图如下 ...

  3. 读懂复杂C声明的黄金法则

    在网上遇见felix,他让我读 http://www.felix021.com/blog/read.php?2072,读完之后觉得收获很大,需要练习一下. 黄金法则:从声明的变量开始,先向右看,再向左 ...

  4. ARC官方文档翻译! - iPhone App开发外包专区 - 威锋论坛 - 威锋网

    CHENYILONG Blog ARC官方文档翻译! - iPhone App开发外包专区 - 威锋论坛 - 威锋网  http://bbs.weiphone.com/read-htm-tid-344 ...

  5. Mysql查看建表语句以及修改引擎

    更多内容推荐微信公众号,欢迎关注: 1 查看系统支持的存储引擎 show engines; 2 查看表使用的存储引擎 两种方法: a.show table status from db_name wh ...

  6. Html 使用技巧 -- 设置display属性可以使div隐藏后释放占用的页面空间

         div的visibility可以控制div的显示和隐藏,但是隐藏后页面显示空白: style="visibility: none;" document.getElemen ...

  7. python学习之argparse模块的使用

    以下内容主要来自:http://wiki.jikexueyuan.com/project/explore-python/Standard-Modules/argparse.html argparse ...

  8. java四舍五入BigDecimal和js保留小数点两位

    java四舍五入BigDecimal保留两位小数的实现方法: // 四舍五入保留两位小数System.out.println("四舍五入取整:(3.856)="      + ne ...

  9. vue项目下使用iview总结

    iview在IE浏览器下有问题,打开页面是空白

  10. python之assert断言

    assert只是一个检查,为真不做任何事,不为真时,抛出异常,并包含错误信息 1.根据布尔值判断 2.判断是否相等 3.返回值是否为空 4.是否包含某值 x in y 其他方法,可以参考http:// ...