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

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


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

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

程序流程

Alt text

信号量和共享内存的系统函数
信号量系统函数及接口
描述获取一个信号量集的标识符.
头文件#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;
}

程序测试:

Alt text

转载于:https://www.cnblogs.com/ailumiyana/p/9264739.html

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux中,共享内存是一种用于进程间通信的机制,多个进程可以访问同一块共享内存区域。使用ipcs -m命令可以查看当前系统中的共享内存信息,而使用ipcrm -m shmid命令可以删除指定的共享内存信号是一种用于进程同步和互斥的机制,可以用来保证对共享内存的正确访问。在Linux中,可以通过信号来实现共享内存同步共享内存本身不提供任何保护资源,因此需要利用其他机制来保证数据的正确性。通过使用信号,可以实现多个进程之间对共享内存同步和互斥操作,从而避免数据访问的竞争问题。 在Linux中,要创建一个共享内存,可以使用shmget函数,该函数的原型为: int shmget(key_t key, size_t size, int shmflg); 通过指定key、size和shmflg参数,可以创建一个指定大小和属性的共享内存区域。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [Linux——进程间通信(2)——共享内存信号信号](https://blog.csdn.net/jason_stanson/article/details/117156055)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [Linux--信号共享内存](https://blog.csdn.net/m0_59052131/article/details/127970222)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值