信号量
问题
程序中,有时存在一种特殊代码,
最多只允许一个进程执行该部分代码。
这部分区域,称为“临界区”然而在多进程并发执行时,当一个进程进入临界区,因某种原因被挂起时,
其他进程就有可能也进入该区域。解决办法:使用信号量
什么是信号量
信号量,是一种特殊的变量。
只能对信号量执行P操作和V操作P操作, 如果信号量的值 > 0, 则把该信号量减1
如果信号量的值 ==0, 则挂起该进程。V操作: 如果有进程因该信号量而被挂起,则恢复该进程运行
如果没有进程因该信号量而挂起,则把该信号量加1注意:P操作、V操作都是原子操作,即其在执行时,不会被中断。
注意:此指的“信号量”是指System V IPC的信号量,
与线程所使用的信号量不同。
该信号量,用于进程间通信。信号量的使用
1) 信号量的获取
semget原型:int semget(key_t key, int nsems, int semflg); 功能:获取一个已存在的、或创建一个新的信号量量,返回该信号量的标识符 参数:key, 键值,该键值对应一个唯一的信号量。 类似于共享内存的键值。 不同的进程可通过该键值和semget获取唯一的信号量。 特殊键值:IPC_PRIVATE, 该信号量只允许创建者本身, 可用于父子进程间通信。 nsems, 需要的信号量数目,一般取1 sem_flags, 与共享内存的sem_flags类似。 IPC_CREAT, 如果该信号量未存在,则创建该信号量 如果该信号量已存在,也不发送错误。 返回值: 成功,则返回一个正数 失败, 返回返回-1
2) 信号量的操作
semop原型:int semop(int semid, struct sembuf *sops, unsigned nsops);
功能:改变信号量的值,即对信号量执行P操作、或V操作。
参数:semid, 信号量标识符, 即semget的返回值
sops, 是一个数组,元素类型为struct sembufstruct sembuf { short sem_num; //信号量组中的编号(即指定对哪个信号量操作) //semget实际是获取一组信号量 //如果只获取了一个信号量,则该成员取0 short sem_op; // -1, 表示P操作 // 1, 表示V操作 short sem_flg; // SEM_UNDO : 如果进程在终止时,没有释放信号量 // 如果不设置指定标志,应该设置为0 则,自动释放该信号量 } nsops, 表示第二个参数sops所表示的数组的大小, 即表示有几个struct sembuf
返回值: 失败, 返回-1
成功, 返回 03) 信号量的控制
semctl
原型:int semctl(int semid, int sem_num, int cmd, …);
功能:对信号量进行控制
参数:semid, 信号量标识符
sem_num, 信号量组中的编号,如果只有一个信号量,则取0
cmd, SETVAL 把信号量初始化为指定的值,具体的值由第4个参数确定
注意:只能对信号量初始化一次,
如果在各进程中,分别对该信号量进行初始化,则可能导致错误!
IPC_RMID 删除信号量
参数4,
类型为:union semun {
int val; // SETVAL命令要设置的值
struct semid_ds *buf;
unsigned short *array;
}
注意:union semun类型要求自己定义
有些Linux发行版在sys/sem.h中定义,有些发行版则没有定义。可自定义如下: ------------------------------------------------------------------------------------- #if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED) #else union semun { int val; struct semid_ds *buf; unsigned short int *array; struct seminfo *__buf; }; #endif --------------------------------------------------------------------------------------
实例
实例1:不使用信号量,并发执行多个进程,观察对临界区的访问。
main1.c实例2:使用信号量,并发执行多个进程,观察对临界区的访问。
main2.c (对main1.c改进)使用信号量实现对文件操作的互斥访问。 程序1,对test.txt写入学生记录信息10条 程序2,对test.txt写入教师记录信息10条 程序1和程序2并发执行 main3/write_teacher.c main3/write_student.c main3/write.h 注意:1)在临界区内异常退出时,一定要先释放信号量 2)信号量只初始化一次
买票进程
//信号量s,已经初始化为0
P(s);
if (count > 0) {
printf(“sold NO. %d\n”, count);
}
count–;
V(s);
//进程的互斥
//操作原语
./a.out 1
./a.out
1、 本地
init
服务器
时间服务进程
用户进程A
ODU
MODEM
CLI
WEB
CONTROL
客户端
- 网络
服务器
客户端
信号量的作用:
1)进程之间的互斥
2)进程之间的同步
进程1
向文件test.txt写入数据
进程2
从文件test.txt读取数据