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


简述


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

实验简述. 
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.

#include "sem.h"
#include "debug.h"
#define IPCKEY_PATH "/"
#define SEM_NUMS 1
#define SEM_OPS_NUM 1
#define ACCESS_BIT 0666
#define TAG "SemInterface"
int sem_new(unsigned char projid, int init_val)
{
key_t key;
int semid;
union semun sem_union;
key = ftok(IPCKEY_PATH, projid);
if(key < 0)
{
LOG_E("ftok error: %s\n", strerror(errno));
return SEM_FAILURE;
}
semid = semget(key, SEM_NUMS, ACCESS_BIT|IPC_CREAT|IPC_EXCL);
if(semid < 0)
{
if(errno == EEXIST)
{
LOG_E("sem exist: %s\n", strerror(errno));
return SEM_EXIST;
}
LOG_E("create sme error: %s\n", strerror(errno));
return SEM_FAILURE;
}
sem_union.val = init_val;
if((semctl(semid, 0, SETVAL, sem_union)) < 0)
{
LOG_E("set sem val error: %s\n", strerror(errno));
return SEM_FAILURE;
}
return semid;
}
int sem_get(unsigned char proj_id)
{
key_t key;
int semid;
union semun sem_union;
key = ftok(IPCKEY_PATH, proj_id);
if(key < 0)
{
LOG_E("ftok error: %s\n", strerror(errno));
return SEM_FAILURE;
}
semid = semget(key, SEM_NUMS, ACCESS_BIT);
if(semid < 0)
{
LOG_E("create sme error: %s\n", strerror(errno));
return SEM_FAILURE;
}
return semid;
}
int sem_p(int semid)
{
struct sembuf sem_buf;
sem_buf.sem_num = 0;
sem_buf.sem_op = -1;
sem_buf.sem_flg = SEM_UNDO;
if(semop(semid, &sem_buf, SEM_OPS_NUM) < 0)
{
LOG_E("sem P opration error: %s", strerror(errno));
return SEM_FAILURE;
}
return SEM_SUCCESS;
}
int sem_v(int semid)
{
struct sembuf sem_buf;
sem_buf.sem_num = 0;
sem_buf.sem_op = 1;
sem_buf.sem_flg = SEM_UNDO;
if(semop(semid, &sem_buf, SEM_OPS_NUM) < 0)
{
LOG_E("sem V opration error: %s", strerror(errno));
return SEM_FAILURE;
}
return SEM_SUCCESS;
}
int sem_del(int semid)
{
union semun sem_union;
if(semctl(semid, 0, IPC_RMID, sem_union) < 0)
{
LOG_E("remove sem error: %s\n", strerror(errno));
return SEM_FAILURE;
}
return SEM_SUCCESS;
}
共享内存系统函数及接口
描述 创建共享内存。也就是从内存中获得一段共享内存区域.
头文件 #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.

#include "shmem.h"
#include "debug.h"
#define IPCKEY_PATH "/"
#define ACCESS_BIT 0666
#define TAG "ShareMem"
int shm_new(unsigned char PROJID, size_t size)
{
int shmid;
key_t key;
key = ftok(IPCKEY_PATH, PROJID);
if(key < 0)
{
LOG_E("get IPC key error: %s\n", strerror(errno));
return SHM_FAILURE;
}
shmid = shmget(key, size, ACCESS_BIT|IPC_CREAT);
if(shmid < 0)
{
LOG_E("get share mem error: %s\n", strerror(errno));
return SHM_FAILURE;
}
return shmid;
}
char *shem_get_addr(int shmid)
{
char *p;
if((p = (shmat(shmid, NULL, 0))) == (char *)-1)
{
LOG_E("get share mem addr error: %s\n", strerror(errno));
return NULL;
}
return p;
}
int shm_del(int shmid)
{
if(shmctl(shmid, IPC_RMID, NULL) < 0)
{
LOG_E("remove share mem error: %s\n", strerror(errno));
return SHM_FAILURE;
}
return SHM_SUCCESS;
}
int shm_detach(char *shmaddr)
{
if(shmdt(shmaddr) < 0)
{
LOG_E("share mem detach error: %s\n", strerror(errno));
return SHM_FAILURE;
}
return SHM_SUCCESS;
}
void shm_read(char *buf, char *shmaddr)
{
strncpy(buf, shmaddr, strlen(shmaddr) + 1);
}
void shm_write(char *shmaddr, char *buf)
{
strncpy(shmaddr, buf, strlen(buf) + 1);
}
void shm_data_init(char *shmaddr)
{
memset(shmaddr, 0, SHM_SIZE);
//strncpy(shmaddr, INIT_DATA, strlen(INIT_DATA) + 1);
}

写程序

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


#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include "debug.h"
#include "sem.h"
#include "shmem.h"
#define SEM_PROJID '*'
#define SHM_PROJID '-'
#define TAG "SemServer"
//#define ENDOFSTR '\0'
#define SHM_SIZE 0x1F4000
int main()
{
int semid;
int shmid;
char *shmaddr;
char buf[SHM_SIZE];
semid = sem_new(SEM_PROJID, 1);
if(SEM_FAILURE == semid)
{
exit(-1);
} else if(SEM_EXIST == semid)
{
if((semid = sem_get(SEM_PROJID)) < 0)
{
exit(-1);
}
LOG_D("get sem success\n");
}
else LOG_D("get new sem success\n");
shmid = shm_new(SHM_PROJID, SHM_SIZE);
if(SEM_FAILURE == shmid)
{
exit(-1);
}
LOG_D("get share mem success\n");
shmaddr = shem_get_addr(shmid);
if(NULL == shmaddr)
{
exit(-1);
}
shm_data_init(shmaddr);
while(1)
{
if(fgets(buf, SHM_SIZE, stdin) == NULL)
{
LOG_E("get std input error: %s", strerror(errno));
exit(-1);
}
buf[strlen(buf) - 1] = '\0';
sem_p(semid);
shm_write(shmaddr, buf);
LOG_D("you write : %s\n",buf);
sem_v(semid);
if(strncmp(buf, "exit", 4) == 0)
{
LOG_D("process finish exit\n");
break;
}
}
if(shm_detach(shmaddr) == SHM_FAILURE)
{
exit(-1);
}
LOG_D("process exit\n");
return 0;
}

读程序

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


#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include "debug.h"
#include "sem.h"
#include "shmem.h"
#define SEM_PROJID '*'
#define SHM_PROJID '-'
#define TAG "SemServer"
#define SHM_SIZE 0x1F4000
int main()
{
int semid;
int shmid;
char *shmaddr;
char buf[SHM_SIZE];
semid = sem_new(SEM_PROJID, 1);
if(SEM_FAILURE == semid)
{
exit(-1);
} else if(SEM_EXIST == semid)
{
if((semid = sem_get(SEM_PROJID)) < 0)
{
exit(-1);
}
LOG_D("get sem success\n");
}
else LOG_D("get new sem success\n");
shmid = shm_new(SHM_PROJID, SHM_SIZE);
if(SEM_FAILURE == shmid)
{
exit(-1);
}
LOG_D("get share mem success\n");
shmaddr = shem_get_addr(shmid);
if(NULL == shmaddr)
{
exit(-1);
}
while(1)
{
getchar();
sem_p(semid);
shm_read(buf, shmaddr);
LOG_D("you read : %s\n",buf);
sem_v(semid);
if(strncmp(buf, "exit", 4) == 0)
{
LOG_D("process finish exit\n");
break;
}
}
if(shm_del(shmid) == SHM_FAILURE)
{
exit(-1);
}
LOG_D("process exit\n");
return 0;
}

程序测试:

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. HDU 4311 Meeting point-1 求一个点到其它点的曼哈顿距离之和

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4311 解题报告:在一个平面上有 n 个点,求一个点到其它的 n 个点的距离之和最小是多少. 首先不得不 ...

  2. 【leetcode 简单】 第七十五题 第一个错误的版本

    你是产品经理,目前正在带领一个团队开发新的产品.不幸的是,你的产品的最新版本没有通过质量检测.由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的. 假设你有 n 个版本 [1, ...

  3. Python练习-从小就背不下来的99乘法表

    心血来潮,灵机一动,反正就是无聊的做了一个很简单的小玩意: for i in range(1,10):#让i 1-9 循环9次 print("\n")#每循环一次进行一次换行 fo ...

  4. 文件操作fstream

    c++文件操作详解 2009-04-16 20:46:35|  分类: C/C++|举报|字号 订阅 C++ 通过以下几个类支持文件的输入输出: ofstream: 写操作(输出)的文件类 (由ost ...

  5. 20155303 2016-2017-2 《Java程序设计》第八周学习总结

    20155303 2016-2017-2 <Java程序设计>第八周学习总结 目录 学习内容总结(Linux命令) 教材学习中的问题和解决过程 代码调试中的问题和解决过程 代码托管 上周考 ...

  6. Redis简介——(一)

    1.关于关系型数据库和nosql数据库 关系型数据库是基于关系表的数据库,最终会将数据持久化到磁盘上,而nosql数据 库是基于特殊的结构,并将数据存储到内存的数据库.从性能上而言,nosql数据库 ...

  7. mysql中使用日期加减时无法识别年-月格式数据的问题,%Y-%m"这种格式数据

    最新做报表统计的时候处理按月统计部分时发现,虽然使用 DATE_FORMAT( time, '%Y-%m' ) 函数可以将日期格式转成年-月,但是如果是参数是年-月格式,即"2018-10& ...

  8. Serv-U 的升级及数据备份和迁移【转】

    Serv-U 配置备份   在serv-u7.x及以上版本安装目录下,有一个文件Serv-U.Archive是serv-u的配置文件,有一个users文件夹是Serv-U的域和用户的信息,那么我们只需 ...

  9. Django 基于类的视图(CBV)执行流程 CBV 源码分析

    一.CBV(基于类的视图) 视图是可以调用的,它接受请求并返回响应,这不仅仅是一个函数,Django提供了一些可以用作视图的类的例子,这些允许您通过继承或mixin来构建视图并重用代码. 基本示例 D ...

  10. C++ : Boost : Rational 有理数类

    因为一些不为人知的原因, 我需要一些能减少我程序误差的东西.于是找到了这个类. 然后下载了Boost这个庞大的库. 安装与配置 在官网上找到下载地址, 大概有71MB, 下来来解压到任意位置就好了. ...