信号量sem ----- 负责进程间 互斥、同步 等功能 ---- 计量某种资源的个数
1、 本质是一种 数据操作锁(计数器),它本身不具有数据交换的功能,而是通过控制其他的通信资源(文件,外部设备)来实现进程间通信,它本身只是一种外部资源的标识。
信号量以 信号量集 申请资源。
临界资源:多个进程能够访问的资源
临界区 :访问临界资源的一段代码
互斥 :独占临界资源
同步 :带着顺序性的进程运行,(大部分)建立在互斥的情况下
P操作:检查信号量,不为0 则 信号量-1;反之(为0),挂起 --- 申请资源
V操作:检查信号量,为0 则 信号量+1 并 唤醒,进入临界区;反之(!=0),挂起
int semop(int semid, struct sembuf *sops, unsigned nsops);
信号量的 P、V操作及 增减 可以保证 原子性,而其 创建和初始化,则不能保证。
2、 联合 和 结构体:
union semun { int val; // 使用的值 struct semid_ds *buf; // IPC_STAT、IPC_SET 使用缓存区 unsigned short *array; // GETALL,、SETALL 使用的数组 struct seminfo *__buf; // IPC_INFO(Linux特有) 使用缓存区 };
struct sembuf { unsigned short sem_num; /* semaphore number */ short sem_op; /* semaphore operation */ short sem_flg; /* operation flags */ };
3、SEM_UNDO :
程序结束时(不论正常或不正常),保证信号值会被重设为semop()调用前的值。这样做的目的在于避免程序在异常情况下结束时未将锁定的资源解锁,造成该资源永远锁定。
(1)semop操作: 它基于每个具体指定的sem_op操作的三个可能值:正数,负数或0。
1. 如果sem_op是正数,其值就加到semval上,这对应于释放由某个信号量控制的资源。
如果指定了SEM_UNDO标志,那就从相应信号灯的semadj值中减掉sem_op的值。
2. 如果sem_op是负数,那么调用者希望等待semval变为大于或等于sem_op的绝对值。这对对应于分配资源。
如果semval大于或等于sem_op的绝对值,那就从semval中减掉sem_op的绝对值。
如果指定了SEM_UNDO标志,那么sem_op的绝对值就加到相应信号灯的semadj值上。
3. 如果sem_op等于0,则程序必须具有 读 权限,等待进程直至有进程发生。
(2)使用注意事项:
-
- 使用SEM_UNDO标志会在semadj中记录对信号量进行的操作,如果程序退出时会将semadj的值加到semval上。
- SEM_UNDO需要成对使用,如果不是成对的使用semadj,可能会导致semadj溢出而发生错误。
- 程序启动时需要(重新)初始化所有资源(信号量等),退出时需要清理各种资源(信号量/共享内存等)。
4、实例:信号锁
//comm.h
#pragma once #include<stdio.h> #include<unistd.h> #include<sys/types.h> #include<sys/ipc.h> #include<sys/sem.h> #include<sys/wait.h> #include<errno.h> #define _PATH_ ‘.‘ #define _PROJ_ID_ 0x6666 union semun { int val; // 使的值 struct semid_ds *buf; // IPC_STAT、IPC_SET 使用缓存区 unsigned short *array; // GETALL,、SETALL 使用的数组 struct seminfo *__buf; // IPC_INFO(Linux特有) 使用缓存区 }; static int comm_sem_set(int _sem_num,int flags); int create_sem_set(int _sem_num); int get_sem_set(int _sem_num); int init_sem_set(int _sem_id,int _seq_num,int _init_val); static int comm_sem_op(int _sem_id,int _seq_num,int _op); int p_sem_elem(int _sem_id,int _seq_num); int v_sem_elem(int _sem_id,int _seq_num); int destroy_sem_set(int _sem_id);
//comm.c
#include "comm.h" static int comm_sem_set(int _sem_num,int flags) { key_t _key=ftok((char*)_PATH_,_PROJ_ID_); if(_key < 0) { perror("ftok"); return -1; } int sem_id=semget(_key,_sem_num,flags); if(sem_id < 0) { perror("semget"); return -1; } return sem_id; } int create_sem_set(int _sem_num) { int flags=IPC_CREAT|IPC_EXCL|0666; return comm_sem_set(_sem_num,flags); } int get_sem_set(int _sem_nums) { int flags=IPC_CREAT; return comm_sem_set(_sem_nums,flags); } int init_sem_set(int _sem_id,int _seq_num,int _init_val) { union semun _un; _un.val=_init_val; if(semctl(_sem_id,_seq_num,SETVAL,_un) < 0) { perror("semctl"); return -1; } return 0; } static int comm_sem_op(int _sem_id,int _seq_num,int _op) { struct sembuf _sem_buf[1]; _sem_buf[0].sem_num=_seq_num; _sem_buf[0].sem_op=_op; if(semop(_sem_id,_sem_buf,1) < 0) { perror("semop"); return -1; } return 0; } int p_sem_elem(int _sem_id,int _seq_num) { return comm_sem_op(_sem_id,_seq_num,-1); } int v_sem_elem(int _sem_id,int _seq_num) { return comm_sem_op(_sem_id,_seq_num,1); } int destroy_sem_set(int _sem_id) { if(semctl(_sem_id,0,IPC_RMID,NULL) < 0) { perror("semctl"); return -1; } return 0; }
(1)未加锁前 --->
//sem_lock.c
#include "comm.h" int main() { pid_t id=fork(); if(id < 0) { perror("fork"); return -1; } else if(id == 0)//Child { while(1) { printf("A"); sleep(1); fflush(stdout); printf("A"); sleep(3); fflush(stdout); } } else//Father { while(1) { printf("B"); sleep(1); fflush(stdout); printf("B"); sleep(2); fflush(stdout); } waitpid(id,NULL,0); } return 0; }
运行结果:
(2)加锁后 --->
//sem_lock.c
#include "comm.h" int main() { int sem_id=create_sem_set(1); init_sem_set(sem_id,0,1); pid_t id=fork(); if(id < 0) { perror("fork"); return -1; } else if(id == 0)//Child { int _sem_id=get_sem_set(1); while(1) { p_sem_elem(_sem_id,0); printf("A"); sleep(1); fflush(stdout); printf("A"); sleep(3); fflush(stdout); v_sem_elem(_sem_id,0); } } else//Father { int _sem_id=get_sem_set(1); while(1) { p_sem_elem(_sem_id,0); printf("B"); sleep(1); fflush(stdout); printf("B"); sleep(2); fflush(stdout); v_sem_elem(_sem_id,0); } waitpid(id,NULL,0); } destroy_sem_set(sem_id); return 0; }
运行结果: