目录
掌握:信号灯机制、信号灯初始化、打开/创建信号灯、信号灯-P操作、信号灯-V操作、信号灯、共享内存
信号量代表某一类资源,其值表示系统中该资源的数量。
概念:是不同进程间或一个给定进程内部不同线程间同步的机制。类似我们的生产者和消费者场景。
类似之前讲过的条件变量。
信号量是一个受保护的变量,只能通过三种操作来访问
- P(S) 含义如下:
- if (信号量的值大于0) {
- 申请资源的任务继续运行;
- 信号量的值减一;
- }
- else { 申请资源的任务阻塞;}
-
-
- V(S) 含义如下:
- 信号量的值加一;
- if (有任务在等待资源) {
- 唤醒等待的任务,让其继续运行
- }
信号灯也叫信号量,用于进程/线程同步或互斥的机制
信号灯的类型
Posix 有名信号灯和无名信号灯使用:

wait 相当于p操作,post相当于v操作
- 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
int sem_close(sem_t *sem);
int sem_unlink(const char* name);
int sem_init(sem_t *sem, int shared, unsigned int value);
参数:
sem:需要初始化的信号灯变量
shared: shared指定为0,表示信号量只能由初始化这个信号量的进程使用,不能在进程间使用,linux 不支持进程间同步。0 – 线程间 1 – 进程间
Value:信号量的值
int sem_destroy(sem_t* sem);
成功时返回0,失败时返回EOF
sem 指向要操作的信号量对象
int sem_wait(sem_t *sem);
成功时返回0,失败时返回EOF
sem 指向要操作的信号量对象
获取资源,如果信号量为0,表示这时没有相应资源空闲,那么调用线程就将挂起,直到有空闲资源可以获取
int sem_post(sem_t *sem);
释放资源,如果没有线程阻塞在该sem上,表示没有线程等待该资源,这时该函数就对信号量的值进行增1操作,表示同类资源多增加了一个。如果至少有一个线程阻塞在该sem上,表示有线程等待资源,信号量为0,这时该函数保持信号量为0不变,并使某个阻塞在该sem上的线程从sem_wait函数中返回
注意:编译posix信号灯需要加pthread动态库。
避免读写混乱:规定写资源初值为1,读资源初值为0。获取写入权限,后写-1,读+1,获取读权限后,读-1,写+1。
sem_r.c
- #include
/* For O_* constants */ - #include
/* For mode constants */ - #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- //删除信号,以免程序退出(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操作,获取读资源,读资源-1
- printf("%s\n",shmaddr);
- sem_post(sem_w); //V操作,写资源+1
- }
- }
-
sem_w.c
- #include
/* For O_* constants */ - #include
/* For mode constants */ - #include
-
- #include
- #include
- #include
- #include
- #include
- #include
-
- 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);
- }
-
- }
-
- #include
/* For O_* constants */ - #include
/* For mode constants */ - #include
-
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
-
- 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); //有名信号灯创建,读初值0
- sem_init(&sem_w,0,1); //有名信号灯创建,写初值1
-
- pthread_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操作
- }
-
- }
-
int semget(key_t key, int nsems, int semflg);
参数:key:ftok产生的key值(和信号灯关联的key值)
nsems:信号灯集中包含的信号灯数目
semflg:信号灯集的访问权限,通常为IPC_CREAT |0666 IPC_EXCL
返回值:成功:信号灯集ID ; 失败:-1
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
int semctl ( int semid, int semnum, int cmd…/*union semun arg*/);
功能:信号灯集合的控制(初始化/删除)
参数:semid:信号灯集ID
semnum: 要操作的集合中的信号灯编号
cmd:
GETVAL:获取信号灯的值,返回值是获得值
SETVAL:设置信号灯的值,需要用到第四个参数:共用体
IPC_RMID:从系统中删除信号灯集合
返回值:成功 0 ; 失败 -1
System V 信号灯是一个或多个计数信号灯的集合
可同时操作集合中的多个信号灯
申请多个资源时避免死锁
- #include
- #include
- #include
- #include
- #include
-
- #include
- #include
- #include
- #include
-
-
-
- #define SEM_READ 0
- #define SEM_WRITE 1
-
- union 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);
- }
-
- }
- }