目录
1 信号量/灯(semaphore)基本概念
2 信号量-P/V操作概念
3 三种信号灯
3.1 有名信号灯
3.1.1 打开
3.1.2 关闭
3.1.3 删除
3.2 无名信号灯
3.2.1 初始化
3.2.2 销毁
3.3 信号灯P操作
3.4 信号灯V操作
3.5 示例
3.5.1 有名信号示例
3.5.2 无名信号灯示例(逻辑同上)
3.5 System V 信号灯使用:
3.5.1 创建/打开信号灯
3.5.2 进行P - V操作
3.5.3 初始化/删除
3.5.4 System V IPC - 信号灯特点
3.5.5 System V信号灯使用步骤
3.6 System V信号灯示例
掌握:信号灯机制、信号灯初始化、打开/创建信号灯、信号灯-P操作、信号灯-V操作、信号灯、共享内存
1 信号量/灯(semaphore)基本概念
信号量代表某一类资源,其值表示系统中该资源的数量。
概念:是不同进程间或一个给定进程内部不同线程间同步的机制。类似我们的生产者和消费者场景。
类似之前讲过的条件变量。
信号量是一个受保护的变量,只能通过三种操作来访问
- 初始化
- P操作(申请资源)
- V操作(释放资源)
2 信号量-P/V操作概念
P(S) 含义如下:if (信号量的值大于0) { 申请资源的任务继续运行;信号量的值减一;}else { 申请资源的任务阻塞;} V(S) 含义如下:信号量的值加一;if (有任务在等待资源) {唤醒等待的任务,让其继续运行}
3 三种信号灯
信号灯也叫信号量,用于进程/线程同步或互斥的机制
信号灯的类型
- Posix 无名信号灯
- Posix有名信号灯 (linux只支持线程同步)
- System V 信号灯
Posix 有名信号灯和无名信号灯使用:
wait 相当于p操作,post相当于v操作
3.1 有名信号灯
3.1.1 打开
sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag,mode_t mode, unsigned int value);
参数:
name:name是给信号灯起的名字
oflag:打开方式,常用O_CREAT
mode:文件权限。常用0666
value:信号量值。二元信号灯值为1,普通表示资源数目
信号灯文件位置:/dev/shm
3.1.2 关闭
int sem_close(sem_t *sem);
3.1.3 删除
int sem_unlink(const char* name);
3.2 无名信号灯
3.2.1 初始化
int sem_init(sem_t *sem, int shared, unsigned int value);
参数:
sem:需要初始化的信号灯变量
shared: shared指定为0,表示信号量只能由初始化这个信号量的进程使用,不能在进程间使用,linux 不支持进程间同步。0 – 线程间 1 – 进程间
Value:信号量的值
3.2.2 销毁
int sem_destroy(sem_t* sem);
成功时返回0,失败时返回EOF
sem 指向要操作的信号量对象
3.3 信号灯P操作
int sem_wait(sem_t *sem);
成功时返回0,失败时返回EOF
sem 指向要操作的信号量对象
获取资源,如果信号量为0,表示这时没有相应资源空闲,那么调用线程就将挂起,直到有空闲资源可以获取
3.4 信号灯V操作
int sem_post(sem_t *sem);
释放资源,如果没有线程阻塞在该sem上,表示没有线程等待该资源,这时该函数就对信号量的值进行增1操作,表示同类资源多增加了一个。如果至少有一个线程阻塞在该sem上,表示有线程等待资源,信号量为0,这时该函数保持信号量为0不变,并使某个阻塞在该sem上的线程从sem_wait函数中返回
注意:编译posix信号灯需要加pthread动态库。
3.5 示例
3.5.1 有名信号示例
避免读写混乱:规定写资源初值为1,读资源初值为0。获取写入权限,后写-1,读+1,获取读权限后,读-1,写+1。
sem_r.c
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <signal.h>//删除信号,以免程序退出(ctrl+c) 重新打开无法开启
void delsemfile(int sig){sem_unlink("mysem_r");exit(0);
}int main(){sem_t *sem_r,*sem_w;key_t key;int shmid;char *shmaddr;struct sigaction act;act.sa_handler = delsemfile;act.sa_flags = 0;sigemptyset(&act.sa_mask);sigaction(SIGINT,&act,NULL); //启用linux中信号退出回调函数delsemfile,用于删除信号量key = ftok(".",100);if(key<0){perror("ftok"); return 0;}shmid = shmget(key,500,0666|IPC_CREAT); //创建信号灯if(shmid<0){perror("shmget");return 0;}shmaddr = shmat(shmid,NULL,0); //创建共享内存sem_r = sem_open("mysem_r",O_CREAT|O_RDWR,0666,0); //创建初值为0的读信号灯sem_w = sem_open("mysem_w",O_CREAT|O_RDWR,0666,1); //创建初值为1的写信号灯while(1){sem_wait(sem_r); //P操作,获取读资源,读资源-1printf("%s\n",shmaddr);sem_post(sem_w); //V操作,写资源+1}
}
sem_w.c
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <signal.h>void delsemfile(int sig){sem_unlink("mysem_w");exit(0);
}int main(){sem_t *sem_r,*sem_w;key_t key;int shmid;char *shmaddr;struct sigaction act;act.sa_handler = delsemfile;act.sa_flags = 0;sigemptyset(&act.sa_mask);sigaction(SIGINT,&act,NULL);key = ftok(".",100);if(key<0){perror("ftok");return 0;}shmid = shmget(key,500,0666|IPC_CREAT); //创建信号灯,500个字节if(shmid<0){perror("shmget");return 0;}shmaddr = shmat(shmid,NULL,0);sem_r = sem_open("mysem_r",O_CREAT|O_RDWR,0666,0);sem_w = sem_open("mysem_w",O_CREAT|O_RDWR,0666,1);while(1){sem_wait(sem_w);printf(">");fgets(shmaddr,500,stdin);sem_post(sem_r);}}
3.5.2 无名信号灯示例(逻辑同上)
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <signal.h>
#include <pthread.h>sem_t sem_r,sem_w;
char *shmaddr;void destroysem(int sig){
// sem_unlink("mysem_w");sem_destroy(&sem_r);sem_destroy(&sem_w);exit(0);}void *readmem(void *arg){while(1){sem_wait(&sem_r); //P操作printf("%s\n",shmaddr);sem_post(&sem_w); //V操作}}int main(){key_t key;int shmid;struct sigaction act;act.sa_handler = destroysem;act.sa_flags = 0;sigemptyset(&act.sa_mask);sigaction(SIGINT,&act,NULL);key = ftok(".",100);if(key<0){perror("ftok");return 0;}shmid = shmget(key,500,0666|IPC_CREAT);if(shmid<0){perror("shmget");return 0;}shmaddr = shmat(shmid,NULL,0);// sem_r = sem_open("mysem_r",O_CREAT|O_RDWR,0666,0);
// sem_w = sem_open("mysem_w",O_CREAT|O_RDWR,0666,1);sem_init(&sem_r,0,0); //有名信号灯创建,读初值0sem_init(&sem_w,0,1); //有名信号灯创建,写初值1pthread_t tid;pthread_create(&tid,NULL,readmem,NULL); //创建线程while(1){sem_wait(&sem_w); //P操作printf(">");fgets(shmaddr,500,stdin);sem_post(&sem_r); //V操作}}
3.5 System V 信号灯使用:
3.5.1 创建/打开信号灯
int semget(key_t key, int nsems, int semflg);
参数:key:ftok产生的key值(和信号灯关联的key值)
nsems:信号灯集中包含的信号灯数目
semflg:信号灯集的访问权限,通常为IPC_CREAT |0666 IPC_EXCL
返回值:成功:信号灯集ID ; 失败:-1
3.5.2 进行P - V操作
int semop ( int semid, struct sembuf *opsptr, size_t nops);
功能:对信号灯集合中的信号量进行P - V操作
参数:semid:信号灯集ID
opsptr: 操作结构体
struct sembuf {short sem_num; // 要操作的信号灯的编号short sem_op; // 1 : 释放资源,V操作// -1 : 分配资源,P操作 short sem_flg; // 0(阻塞),IPC_NOWAIT, SEM_UNDO
};//对某一个信号灯的操作,如果同时对多个操作,则需要定义这种结构体数组
nops: 要操作的信号灯的个数 ,1个
返回值:成功 :0 ; 失败:-1
3.5.3 初始化/删除
int semctl ( int semid, int semnum, int cmd…/*union semun arg*/);
功能:信号灯集合的控制(初始化/删除)
参数:semid:信号灯集ID
semnum: 要操作的集合中的信号灯编号
cmd:
GETVAL:获取信号灯的值,返回值是获得值
SETVAL:设置信号灯的值,需要用到第四个参数:共用体
IPC_RMID:从系统中删除信号灯集合
返回值:成功 0 ; 失败 -1
3.5.4 System V IPC - 信号灯特点
System V 信号灯是一个或多个计数信号灯的集合
可同时操作集合中的多个信号灯
申请多个资源时避免死锁
3.5.5 System V信号灯使用步骤
- 打开/创建信号灯
- semget 信号灯初始化
- semctl P/V操作 semop
- 删除信号灯 semctl
3.6 System V信号灯示例
#include <semaphore.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/sem.h>#define SEM_READ 0
#define SEM_WRITE 1union semun {int val;
}; void Poperation(int semid,int semindex){struct sembuf sbuf;sbuf.sem_num = semindex;sbuf.sem_op = -1;sbuf.sem_flg = 0;semop(semid,&sbuf,1);}
void Voperation(int semid,int semindex){struct sembuf sbuf;sbuf.sem_num = semindex;sbuf.sem_op = 1;sbuf.sem_flg = 0;semop(semid,&sbuf,1);}int main(){key_t key;char *shmaddr;int semid,shmid;key = ftok(".",100);if(key<0){perror("ftok");return 0;}semid = semget(key,2,IPC_CREAT |0666);if(semid<0){perror("semget");return 0;}shmid = shmget(key,500,IPC_CREAT |0666);shmaddr = shmat(shmid,NULL,0);union semun mysem;mysem.val = 0;semctl(semid,SEM_READ,SETVAL,mysem);mysem.val = 1;semctl(semid,SEM_WRITE,SETVAL,mysem);pid_t pid;pid = fork();if(pid<0){perror("fork");shmctl(shmid,IPC_RMID,NULL);semctl(semid,0,IPC_RMID);exit(-1);}else if(pid == 0){while(1){Poperation(semid,SEM_READ);printf("%s\n",shmaddr);Voperation(semid,SEM_WRITE);}}else{while(1){Poperation(semid,SEM_WRITE);printf(">");fgets(shmaddr,32,stdin);Voperation(semid,SEM_READ);}}
}