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;
}
程序测试: