信号量
英文名字:semaphore
这里的
进程信号量
会为下列的信号量打基础
- Ucos系统的信号量
- c线程的信号量
- java进程和线程的信号量
信号量作用
当多个进程/线程进行共享操作时,用于资源保护,以防止出现相互干扰的情况
信号量用于“资源的保护“
(1)进程信号量---实现的是进程所操作资源的保护。
(2)线程信号量---实现的是线程所操作资源的保护。
什么是进程的资源保护
1.多个进程同时向共享内存
里面写数据
时,可能会出现数据相互干扰的情况
某个进程写数据操作还没有写完时,进程的时间片就到了
然后被切换到另一个"共享内存"的写进程上运行
这个进程会接着往共享内存里面写数据,此时显然就把第一个进程写的数据给干扰了
这就形成了数据相互干扰。
2.当多个进程同时共享向文件
里面写数据时,同样会出现和共享写“共享内存”相同的情况
避免出现相互干扰
,需要加入资源保护
的措施
保护的目的就是:
保证每个进程在没有把数据读、写完整之前,其它进程不能进行读、写操作,以防止干扰别人
资源保护中资源指的就是操作的数据
保护的目的就是不要出现相互干扰,导致紊乱和错误数据的产生。
资源保护的种类:同步或互斥
信号量在资源保护中:即可以实现互斥又可以实现同步
但是文件锁只能实现互斥不同实现同步
互斥
对于互斥
操作来说,多进程共享操作时:
-
多个进程间不关心谁先操作、谁后操作的
先后顺序
问题 -
它们只关心一件事,那就是
我在操作时别人不能操作
---就算当前正在操作的进程它的时间片到了,切换到了其它进程上;
---但是当该进程检测到上一个进程还没有操作完时,该进程在当前的时间片内会休眠;
---直到再次切换会上一个进程,将操作完成后再切换回来,此时才能进行操作。
跟上厕所时把门关起来是一样的:
我在蹲坑时你不能蹲,你在蹲坑时我不能蹲,这就是互斥
至于蹲坑先后顺序并没有要求。
同步
同步其实本身就包含了互斥,不过同步不仅仅只互斥
同步对于谁先操作、谁后操作的先后顺序
有要求
规定A进程先写,然后是B进程写,然后是C进程写,绝对不能出现这操作顺序以外的顺序
很像:银行办理业务,又注重互斥还注重顺序
互斥和同步的实现本质–加锁
信号量就是一个加锁机制,通过加锁来实现同步和互斥
同步和互斥的目的其实就是为了实现“资源”的保护,不要让数据出现紊乱
信号量是什么
信号量从代码上就是:结构体,链表,数组等数据结构写的锁。
通过数据结构可以
标记
这个进程的执行情况
,实现加锁
功能
信号量是加锁机制,为什么会被归到了进程间通信里面
资源保护时,某个进程的操作没有完全完成之前,别人是不能操作的
那么
进程间必须相互知道对方的操作状态
,也就是一定会涉及通信过程
信号量实现资源保护的本质
通过通信让各个进程了解到操作状态
,然后查看自己能不能操作
信号量实现互斥的原理
进程信号量既能实现进程的互斥,也能实现进程的同步
父子进程同时写文件(不加信号量)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void print_err(char *str){
perror(str);
exit(-1);
}
int main(void){
int ret =0;
/*fork()前打开open*/
int fd=-1;
//fork之前无需O_APPEND,父子进程不相互干扰
fd=open("./share_file",O_RDWR|O_CREAT|O_TRUNC,0664);
if(fd==-1) print_err("open fails\n");
ret=fork();
if(ret>0){//父进程写数据
while(1){
write(fd,"aaaaaaaaa ",10);
write(fd,"bbbbbbbbb\n",10);
}
}
else if(ret==0){//子进程写数据
while(1){
write(fd,"ccccccccc ",10);
write(fd,"ddddddddd\n",10);
}
}
return 0;
}
父子进程这两个相同的“文件描述符”指向的是相同的“文件表”,文件表的指针位置是同一个不会覆盖
如果父子进程各自打开文件,各自会有自己的文件表,那么虽然通过索引得到同样的文件,但是各自文件表中的指针位置不同步,那么读写会覆盖
不能保证互斥,因为会
切换进程
进程信号量实现互斥的原理
什么是进程信号量
简单理解的话:
----信号量其实是OS创建的一个共享变量
--------进程在进行操作之前,会先检查这个变量的值,这变量的值就是一个标记
--------通过这个标记就可以知道可不可以操作,以实现互斥
信号量是共享变量,这个变量的值就是操作标记
二值信号量
- 同步和互斥时使用的都是二值信号量
- 二值信号量的值就两个:0和1,0表示不可以操作,1表示可以操作
比如父进程写之前将共享变量值设为0,进行加锁,两个写语句设为加锁代码段;如果没写完就切换,那么切换后的进程发现变量值为0,就阻塞等待,不进行操作;直到两个write结束了,就解锁,也就是把共享变量值设为1。如此循环
多值信号量
- 信号量的最大值>1,比如为3的话,信号量允许的值为0、1、2、3
多值信号量用的不是很多
信号量集合
信号量其实是一个OS创建的,供相关进程共享
的int变量,只不过在调用相关API创建信号量时,创建的都是一个信号量集合
所谓集合就是可能会包含好多个信号量
- 用于互斥时,集合中只包含一个信号量
- 用于同步时,集合中会包含多个信号量,至于多少个,需要看情况。
信号量的使用
使用步骤
-
进程调用
semget函数
创建新的信号量
集合,或者获取已有的信号量集合 -
调用
semctl函数
给集合中的每个信号量
设置初始值
-
调用
semop函数
,对集合中的信号量进行pv操作
-
调用
semctl
删除信号量集合
创建或获取->设初值->pv操作->删除
PV操作
pv操作其实说白了就是加锁、解锁操作
P操作(加锁)
:对信号量的值进行-1,如果信号量的值为0,p操作就会阻塞V操作(解锁)
:对信号量的值进行+1,V操作不存在阻塞的问题
通过pv操作(加锁、解锁),就能够实现互斥,以防止出现干扰
信号量的API
semget()
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
//根据key值创建新的、或者获取已有的信号量集合,并返回其标识符
int semget(key_t key, int nsems, int semflg);
/*
实现互斥时:集合中只需要一个信号量
实现同步时:集合中需要多个信号量
*/
-
key:设置同消息队列和共享内存。
一般都使用ftok获取key值
-
nsems:指定集合中信号量的个数
- 用于
互斥
时,数量都指定为1
,因为只需要一个信号量 - 如果是
同步
的话就需要至多为多个
- 用于
-
semflg:设置同消息队列和共享内存
一般都设置为
0664|IPC_CREAT
成功
返回信号量集合的标识符,失败
返回-1
semctl()
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
//根据cmd的要求对集合中的各个信号量进行控制
int semctl(int semid, int semnum, int cmd, ...);
//返回值:调用成功返回非-1值,失败则返回-1,errno被设置
...
表示它是一个变参函数
,如果第四个参数用不到的话,可以省略不写
-
semid
:信号量标识符 -
semnum
:集合中某个
信号量的编号- 信号量的编号为非负整数,而且是自动从0开始编号的
通过信号量编号就能找到
集合中
对应信号量 -
cmd
:控制选项-
IPC_STAT
:将信号量的属性信息从内核读到第四个参数所以指定的struct semid_ds缓存中 -
IPC_SET
:修改属性信息,此时也会用到struct semid_ds结构体变量 -
IPC_RMID
:删除信号量当集合中所有的信号量都被删除后,信号量集合也就被删除了
删除操作时第四个参数用不到,比如:
semctl(semid, 0, IPC_RMID);
-
SETVAL
:通过第四个
参数,给集合中semnu编号的信号量设置一个int初始值
-
- SETVAL确属于进程信号量所
独有
的选项如果是二值信号量的话,设置初始值要么是0,要么是1
如果信号量的目的是
互斥
的话,基本都是设置为1
当设置为1后,多几个进程互斥操作时,那就是谁先运行就谁先操作
…
第四个参数具体设置依赖于“cmd”
- cmd为
IPC_STAT
:第四个参数应为struct semid_ds类型的缓存 - cmd为
SETVAL
:第四个参数应该设置为一个int的值,用于初始化信号量
第四个参数对应内容是变着的,为了应对这种变化就用到了一个联合体
union semun {
int val;
struct semid_ds *buf;
unsigned short *array; /* 不做要求 */
struct seminfo *__buf; /* 不做要求 */
};
//这个联合体类型并没有被定义在信号量相关的系统头文件中,使用时需要自己定义这个类型
指定struct semid_ds缓存时
union semun sem_un; //定义一个联合体变量
struct semid_ds buff; //定义一个struct semid_ds缓存
sem_un.buf = &buff; //现在整个联合体的值就是buf中缩放的buff的地址
semctl(semid, 0, IPC_STAT, sem_un); //这里将联合体传递给semctl函数,
//其实就是将buff的地址传递给了semctl函数
定信号量的int初始值
union semun sem_un;
sem_un.val = 1; //现在整个联合体的值就是1
semctl(semid, 0, IPC_STAT, sem_un);
删除信号量
int semctl(int semid, int semnum, int cmd, …);
删除信号量集合时,并不需要把所有的信号量都删除掉后才能删除
---------只需要指定semid和IPC_RMID就可以不把整个信号量集合删除,其中第二个参数semnum没有被用到,所以semnum的值可以随便写,不过我们一般都是把它写为0。
所以删除整个信号量集合时,删除的写法可以统一的为:
semctl(semid, 0, IPC_RMID);
semop()
op是operate操作的意思
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
//功能:对指定的信号量进行p操作、或者是v操作
int semop(int semid, struct sembuf *sops, size_t nsops);
//返回值:调用成功返回0,失败则返回-1,errno被设置
- p操作:将信号量的值-1
- 当信号量的值为0时,p操作默认是阻塞的
- v操作:将信号量的值+1
- v操作不存在阻塞的问题
对于二值信号量来说 v操作后,值就从0变为了1 这就表示我操作完了,其它进程运行时就可以进行p操作了
参数
semid
:信号量集合的标识符sops
:这个参数更好理解的写法是struct sembuf sops[]
nsops
:用于指定数组元素个数的
1. 每一个数组成员对应一个信号量
2. 每一个元素都是一个struct sembuf结构体变量
内部成员
的决定着:
- 你要对集合中哪一个信号量进行操作
- 要进行的是p操作呢,还是v操作
//结构体成员
struct sembuf{
unsigned short sem_num;
short sem_op;
short sem_flg;
}
//这个结构体不需要我们自己定义,因为在semop的头文件中已经定义了
-
sem_num
:信号量编号,决定对集合中哪一个信号量进行pv操作 -
sem_op
:设置为-1,表示想-1进行p操作,设置1表示想+1进行v操作 -
sem_flg
:-
IPC_NOWAIT
如果你不想阻塞的话,可以指定这个选项
-
SEM_UNDO
如果进程在结束时没有V操作的话,OS会自动帮忙V操作,防止死锁
-
以二值信号量为例:
当进程在v操作之前就结束时,信号量的值就会一直保持为0,那么其它进程将永远无法p操作成功,会使得进程永远休眠下去,这造成就是死锁。
互斥完整程序
信号量封装程序
#ifndef mm
#define mm
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sem.h>
void print_err(char* str){
perror(str);
exit(-1);
}
union semun {//联合体
int val;
struct semid_ds *buf;
};
int create_or_get_sem(int nsems){
int semid=0;
int fd=open("./semaFile",O_CREAT|O_RDWR,0664);
if(fd==-1) print_err("open fails\n");
key_t key=0;
key=ftok("./semaFile",'a');
if(key==-1) print_err("ftok fails\n");
semid=semget(key,nsems,0664|IPC_CREAT);
if(semid==-1) print_err("semget fails\n");
return semid;
}
//初始化信号量----传信号量的编号,信号量集合的某个具体信号编号,初值
void init_sema(int semid,int semnum,int val){
int ret=-1;
union semun sem_un;
sem_un.val=val;
ret=semctl(semid,semnum,SETVAL,sem_un);
if(ret==-1) print_err("semctl fails\n");
}
//删除信号量
//信号量从0开始连续,传入删除的总数,单个删
void del_sema(int semid,int nsems){
int i=0;
for(;i<nsems;i++)
semctl(semid,i,IPC_RMID);
//删除文件
// remove("./share_file");
}
//加锁P操作,资源-1
void p_sema(int semid,int sembuf[],int n){//传入要设置的信号量编号集合
struct sembuf sem_buf[n];
int i=0;
for(;i<n;i++){
sem_buf[i].sem_num=sembuf[i];
sem_buf[i].sem_op=-1;//p操作-1
sem_buf[i].sem_flg=SEM_UNDO;
}
semop(semid,sem_buf,n);
}
//释放锁v操作,资源(值)加一
void v_sema(int semid,int sembuf[],int n){
struct sembuf sem_buf[n];
int i=0;
for(;i<n;i++){
sem_buf[i].sem_num=sembuf[i];
sem_buf[i].sem_op=1;//v操作+1
sem_buf[i].sem_flg=SEM_UNDO;
}
semop(semid,sem_buf,n);
}
#endif
父子进程通信程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "semaphore.h"
#include <signal.h>
#define NSEMS 1
int semID = -1;
void signal_fun(int signo)
{ //捕获删除
del_sema(semID, NSEMS);
exit(-1);
}
int main(void)
{
int ret = 0;
/*fork()前打开open*/
int fd = -1;
// fork之前无需O_APPEND,父子进程不相互干扰
fd = open("./share_file", O_RDWR | O_CREAT | O_TRUNC, 0664);
if (fd == -1)
print_err("open fails\n");
// fork前创建信号量
semID = create_or_get_sem(NSEMS);
//父进程初始化
int i;
for (i = 0; i < NSEMS; i++)
init_sema(semID, i, 1); //谁先来谁使用
ret = fork();
if (ret > 0)
{ //父进程写数据
//父进程捕获
signal(SIGINT, signal_fun);
int sem_op[1] = {0}; //定义要操作的编号集合
while (1)
{
sem_op[0] = 0;
p_sema(semID, sem_op, 1); // P加锁,资源减一
write(fd, "aaaaaaaaa ", 10);
write(fd, "bbbbbbbbb\n", 10);
sem_op[0] = 0;
v_sema(semID, sem_op, 1); // v释放锁,资源加一
}
}
else if (ret == 0)
{ //子进程写数据
int sem_op[1] = {0}; //定义要操作的编号集合
while (1)
{
sem_op[0] = 0;
p_sema(semID, sem_op, 1); // P加锁,资源减一
write(fd, "ccccccccc ", 10);
write(fd, "ddddddddd\n", 10);
sem_op[0] = 0;
v_sema(semID, sem_op, 1); // v释放锁,资源加一
}
}
return 0;
}
程序复习
#ifndef MY_SEMA
#define MY_SEMA
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
//创建或得到一个信号量的semId
//semget(key,信号量集合个数,0664|IPC_CREAT);
int create_get_sema(int numSema){
int fd=-1;
key_t key=0;
int semId=0;
fd=open("./share_file",O_CREAT|O_RDWR,0664);
key=ftok("./share_file",'a');
semId=semget(key,numSema,0664|IPC_CREAT);
}
//联合:cmd的第四个可变参数
union semun {
int val;
struct semid_ds *buf;
};
//初始化信号量
//semctl(semId,信号量集合个数,SETVAL,初值)
void init_sema(int semId,int numSema,int val){
union semun ss;
ss.val=val;
semctl(semId,numSema,SETVAL,ss);
}
//删除信号量
//semctl(semId,信号量的集合个数,IPC_RMID)
void del_sema(int semId,int n){
int i;
for(i=0;i<n;i++)
semctl(semId,i,IPC_RMID);
}
//P操作,加锁,资源-1
//sembuf成员:信号量编号;pv(+1,-1);IPC_NOWAIT//SEM_UNDO
//semop(semid,sembuf数组地址,数组个数)
void p_sema_plus(int semId,int semaSet[],int n){
struct sembuf semBuf[n];
int i;
for(i=0;i<n;i++){
semBuf[i].sem_num=i;
semBuf[i].sem_op=-1;
semBuf[i].sem_flg=SEM_UNDO;
}
semop(semId,semBuf,n);
}
//v操作,释放锁,资源+1
void v_sema_minus(int semId,int semaSet[],int n){
struct sembuf semBuf[n];
int i;
for(i=0;i<n;i++){
semBuf[i].sem_num=i;
semBuf[i].sem_op=1;//v+1
semBuf[i].sem_flg=SEM_UNDO;
}
semop(semId,semBuf,n);
}
#endif
#include"semaphore.h"
#include<signal.h>
#include<stdlib.h>
#define SEMA 1
int semId=-1;
void signal_fun(int signo){
del_sema(semId,SEMA);
exit(-1);
}
int main(){
int fd=-1;
fd=open("./share_file",O_CREAT|O_RDWR|O_TRUNC,0664);
semId=create_get_sema(SEMA);
/*信号量初始化*/
int i;
for(i=0;i<SEMA;i++)
init_sema(semId,i,1);
int ret=fork();
if(ret>0){//父进程写,一定别加等号
signal(SIGINT,signal_fun);//父进程调用捕获
int semaSET[1]={0};
while(1){
//p,v操作前必须定义信号量集合
semaSET[0]=0;
p_sema_plus(semId,semaSET,1);
write(fd,"hello\t",6);
write(fd,"world\n",6);
semaSET[0]=0;//只有一个信号量,编号为0
v_sema_minus(semId,semaSET,1);
}
}
else if(ret==0){
int semaSET[1]={0};
while(1){
semaSET[0]=0;
p_sema_plus(semId,semaSET,1);
write(fd,"learn\t",6);
write(fd,"happy\n",6);
semaSET[0]=0;//只有一个信号量,编号为0
v_sema_minus(semId,semaSET,1);
}
}
return 0;
}
信号量实现同步
什么是同步
同步本身就是互斥
的,让多个进程按照固定的步调
做事(多了顺序)
让三个进程按照顺序打印:11111,22222,33333
#ifndef MY_SEMA
#define MY_SEMA
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
/*错误打印函数*/
void print_err(char* str){
perror(str);
exit(-1);
}
/*创建信号量*/
int create_get_sema(int numSema){
int fd=-1;
key_t key=0;
int semId=0;
fd=open("./share_file",O_RDWR|O_CREAT|O_TRUNC,0664);
if(fd==-1) print_err("open fails\n");
key=ftok("./share_file",'a');
if(key==-1) print_err("ftok fails\n");
semId=semget(key,numSema,IPC_CREAT|0664);
if(semId==-1) print_err("semget fails\n");
return semId;
}
union semun{
int val;
struct semid_ds* buf;
};
/*初始化信号量*/
void init_sema(int semId,int numSema,int val){
union semun sema;
sema.val=val;
semctl(semId,numSema,SETVAL,sema);
}
/*删除信号量*/
void del_sema(int semId,int numSema){
semctl(semId,0,IPC_RMID);//一下子全删
remove("./share_file");
}
/*P操作,加锁,资源-1*/
void p_sema_minus(int semId,int bufop[],int n){
int i;
struct sembuf sem_op[n];
for(i=0;i<n;i++){
sem_op[i].sem_num=bufop[i];
sem_op[i].sem_op=-1;
sem_op[i].sem_flg=SEM_UNDO;
}
semop(semId,sem_op,n);
}
/*V操作,释放锁,资源+1*/
void v_sema_plus(int semId,int bufop[],int n){
int i;
struct sembuf sem_op[n];
for(i=0;i<n;i++){
sem_op[i].sem_num=bufop[i];
sem_op[i].sem_op=1;
sem_op[i].sem_flg=SEM_UNDO;
}
semop(semId,sem_op,n);
}
#endif
#include"semaphore.h"
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#define SEMA 3
int semId=-1;
void signal_fun(int signo){
del_sema(semId,SEMA);
exit(-1);
}
int main(void){
int buf_sema[1]={0};
int ret=0;
/*创建信号量集合*/
semId=create_get_sema(SEMA);
/*初始化信号量集合*/
int i;
for(i=0;i<SEMA;i++){
if(i==0) init_sema(semId,i,1);
else init_sema(semId,i,0);
}
/*三个亲缘进程*/
//信号量数组,每次pv只针对一个信号量
ret=fork();
if(ret>0){
//父进程再次fork()
ret=fork();
if(ret>0){//父进程
while(1){
buf_sema[0]=2;
p_sema_minus(semId,buf_sema,1);
printf("33333\n");
sleep(1);
buf_sema[0]=0;
v_sema_plus(semId,buf_sema,1);
}
}
else if(ret==0){//子进程2
while(1){
buf_sema[0]=1;
p_sema_minus(semId,buf_sema,1);
printf("22222\n");
sleep(1);
buf_sema[0]=2;
v_sema_plus(semId,buf_sema,1);
}
}
}
else if(ret==0){//子进程1
//负责删除信号量
signal(SIGINT,signal_fun);
while(1){
buf_sema[0]=0;
p_sema_minus(semId,buf_sema,1);
printf("11111\n");
sleep(1);
buf_sema[0]=1;
v_sema_plus(semId,buf_sema,1);
}
}
return 0;
}
易错点
int i;
for(i=0;i<SEMA;i++){
if(i==0) init_sema(semId,i,1);
else init_sema(semId,i,0);//else千万别扔了
}
/*或者*/
int i;
for(i=0;i<SEMA;i++){
if(i==0) {init_sema(semId,i,1);
break;}//跳出,因为不想执行下述代码
init_sema(semId,i,0);
}
-
创建信号量指定的是要用到的信号量总数,这里是3
-
但是pv操作传入的要是具体的某一个信号量的数组集合
int buf_sema[1]={0}; buf_sema[0]=2; p_sema_minus(semId,buf_sema,1); buf_sema[0]=0; v_sema_plus(semId,buf_sema,1);
创建的三个信号量分别为:0,1,2
每次p,v只针对一个信号量,也就是信号量数组只有一个元素
- 数组【下标】=传入的信号量编号
回顾共享内存实现通信
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHMSIZE 2046
int shmId=-1;
void* shmAddr=NULL;
char buf[100]={"aaaaaaaaabbbbbbbbbbbcccccccccccccccc"};
//创建共享内存(共享内存虚拟地址更重要,不返回shmId)
void create_get_shmId(){
open("./sharefile",O_RDWR|O_CREAT,0664);
key_t key=ftok("./sharefile",'a');
shmId=shmget(key,SHMSIZE,IPC_CREAT|0664);
}
//信号捕获去删除共享内存
void signal_fun(int signo){
//删除前必须取消映射
shmdt(shmAddr);
//删除共享内存
shmctl(shmId,IPC_RMID,NULL);
remove("./sharefile");
exit(-1);
}
int main(){
signal(SIGINT,signal_fun);
create_get_shmId();
//NULL:内核自动分配虚拟地址
shmAddr=shmat(shmId,NULL,SHM_RND);
//判错
if(shmAddr==(void*)-1){
perror("shmat fails\n");
exit(-1);
}
//往共享内存拷贝数据
while(1){
memcpy(shmAddr,buf,sizeof(buf));
sleep(2);
}
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHMSIZE 2046
int shmId=-1;
void* shmAddr=NULL;
//创建共享内存(共享内存虚拟地址更重要,不返回shmId)
void create_get_shmId(){
open("./sharefile",O_RDWR|O_CREAT,0664);
key_t key=ftok("./sharefile",'a');
shmId=shmget(key,SHMSIZE,IPC_CREAT|0664);
}
//信号捕获去删除共享内存
void signal_fun(int signo){
//删除前必须取消映射
shmdt(shmAddr);
//删除共享内存
shmctl(shmId,IPC_RMID,NULL);
remove("./sharefile");
exit(-1);
}
int main(){
signal(SIGINT,signal_fun);
create_get_shmId();
//NULL:内核自动分配虚拟地址
shmAddr=shmat(shmId,NULL,SHM_RND);
//判错
if(shmAddr==(void*)-1){
perror("shmat fails\n");
exit(-1);
}
//往共享内存拷贝数据
while(1){
if(strlen((char *)shmAddr)!=0){//需要判断,不然打出无用空白
printf("%s\n",(char*)shmAddr);//别忘了类型强转
bzero(shmAddr,sizeof(shmAddr));
}
}
return 0;
}
- 共享内存中:shmat()返回的(void*)shmAddr十分重要
不管是读还是写都需要这个地址,如果读,强转:(char*)
- 删除需要取消映射
也就是所有程序都需要
shmdt(shmAddr)
后才能删了共享内存即:
shmctl(shmId,IPC_RMID,NULL)
用信号量解决程序等待问题
读进程如果优先写进程执行,共享内存没数据,会一直循环等待,从而浪费CPU
必须:写进程先写,读进程后读,读写进程有顺序,需要用信号量同步
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "semaphore.h"
#define SHMSIZE 2046
int shmId=-1;
int semaId=-1;
void* shmAddr=NULL;
char buf[100]={"aaaaaaaaabbbbbbbbbbbcccccccccccccccc"};
//创建共享内存(共享内存虚拟地址更重要,不返回shmId)
void create_get_shmId(){
open("./sharefile",O_RDWR|O_CREAT,0664);
key_t key=ftok("./sharefile",'a');
shmId=shmget(key,SHMSIZE,IPC_CREAT|0664);
}
//信号捕获去删除共享内存
void signal_fun(int signo){
//删除前必须取消映射
shmdt(shmAddr);
//删除共享内存
shmctl(shmId,IPC_RMID,NULL);
remove("./sharefile");
exit(-1);
}
int main(){
signal(SIGINT,signal_fun);
semaId=create_get_sema(2);
//sem 0=1; sem 1=0;
init_sema(semaId,0,1);
init_sema(semaId,1,0);
int semaSet[1]={0};
create_get_shmId();
//NULL:内核自动分配虚拟地址
shmAddr=shmat(shmId,NULL,SHM_RND);
//判错
if(shmAddr==(void*)-1){
perror("shmat fails\n");
exit(-1);
}
//往共享内存拷贝数据
while(1){
//p 0
semaSet[0]=0;//传入第一个信号量
p_sema_plus(semaId,semaSet,1);
memcpy(shmAddr,buf,sizeof(buf));
sleep(2);
//v 1
semaSet[0]=1;//传入第二个信号量
v_sema_minus(semaId,semaSet,1);
}
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include "semaphore.h"
#define SHMSIZE 2046
int shmId=-1;
void* shmAddr=NULL;
int semaId=-1;
//创建共享内存(共享内存虚拟地址更重要,不返回shmId)
void create_get_shmId(){
open("./sharefile",O_RDWR|O_CREAT,0664);
key_t key=ftok("./sharefile",'a');
shmId=shmget(key,SHMSIZE,IPC_CREAT|0664);
}
//信号捕获去删除共享内存
void signal_fun(int signo){
//删除前必须取消映射
shmdt(shmAddr);
//删除共享内存
shmctl(shmId,IPC_RMID,NULL);
del_sema(semaId);
remove("./sharefile");
exit(-1);
}
int main(){
signal(SIGINT,signal_fun);
semaId=create_get_sema(2);
create_get_shmId();
int semaSet[1]={0};
/*初始化信号量只需要一个进程
init_sema(semaId,0,1);
init_sema(semaId,1,0);*/
//NULL:内核自动分配虚拟地址
shmAddr=shmat(shmId,NULL,SHM_RND);
//判错
if(shmAddr==(void*)-1){
perror("shmat fails\n");
exit(-1);
}
//往共享内存拷贝数据
while(1){
semaSet[0]=1;
p_sema_plus(semaId,semaSet,1);
//无需if,肯定有数据
printf("%s\n",(char*)shmAddr);//别忘了类型强转
bzero(shmAddr,sizeof(shmAddr));
semaSet[0]=0;
v_sema_minus(semaId,semaSet,1);
}
return 0;
}