信号量的本质是一种数据操作锁,它本身不具有数据交换的功能,而是通过控制其他的
通信资源(文件,外部设备)来实现进程间通信,它本身只是一种外部资源的标识。信号
量在此过程中负责数据操作的互斥、同步等功能。
当请求一个使用信号量来表示的资源时,进程需要先读取信号量的值来判断资源是否可用。大于0,资源可以请求,等于0,无资源可用,进程会进入睡眠状态直至资源可用。
当进程不再使用一个信号量控制的共享资源时,信号量的值+1,对信号量的值进行的增减
操作均为原子操作,这是由于信号量主要的作用是维护资源的互斥或多进程的同步访问。
而在信号量的创建及初始化上,不能保证操作均为原子性。
一、为什么用信号量
为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,
它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域。
临界区域是指执行数据更新的代码需要独占式地执行。而信号量就可以提供这样的一种访
问机制,让一个临界区同一时间只有一个线程在访问它, 也就是说信号量是用来调协进程
对共享资源的访问的。其中共享内存的使用就要用到信号量。
二 信号量的工作原理
由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这样的:
P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行
V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂
起,就给它加1.
sem.h 1 #pragma once 2 #include<stdio.h> 3 #include<stdlib.h> 4 #include<unistd.h> 5 #include<sys/ipc.h> 6 #include<sys/sem.h> 7 #include<sys/types.h> 8 #define _PATH_ "." 9 #define _PROJ_ID 0X6666 10 union semun { 11 int val; /* Value for SETVAL */ 12 struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ 13 unsigned short *array; /* Array for GETALL, SETALL */ 14 struct seminfo *__buf; /* Buffer for IPC_INFO 15 (Linux-specific) */ 16 }; 17 int create_sem_set(int _sem_nums); 18 int get_sem_set(); 19 int init_sem_set(int _sem_id,int _sem_num,int _init_val); 20 int destroy_sem_set(int sem_id); 21 int p_sem_elem(int _sem_id,int _sem_num); 22 int v_sem_elem(int _sem_id,int _sem_num); sem.c 1 #include "sem.h" 2 3 static int com_create_sem_set(int _sem_nums,int flags) 4 { 5 key_t key=ftok(_PATH_,_PROJ_ID); 6 if(key<0) 7 { 8 perror("ftok\n"); 9 return -1; 10 } 11 int _sem_id=semget(key,_sem_nums,flags); 12 if(_sem_id<0) 13 { 14 perror("semget"); 15 return -1; 16 } 17 return _sem_id; 18 } 19 int create_sem_set(int _sem_nums) 20 { 21 int flags=IPC_CREAT|IPC_EXCL|0666; 22 return com_create_sem_set(_sem_nums,flags); 23 24 } 25 int get_sem_set(int _sem_nums) 26 { 27 int flags=IPC_CREAT; 28 return com_create_sem_set(_sem_nums,flags); 29 30 } 31 int init_sem_set(int _sem_id,int _sem_num,int _init_val) 32 { 33 union semun _semnu ; 34 _semnu.val=_init_val; 35 if(semctl(_sem_id,_sem_num,SETVAL,_semnu)) 36 { 37 perror("semctl"); 38 return -1; 39 } 40 return 0; 41 } 42 int destroy_sem_set(int _sem_id) 43 { 44 if(semctl(_sem_id,0,IPC_RMID)) 45 { 46 perror("semctl"); 47 return -1; 48 } 49 return 0; 50 51 } 52 int com_op_sem_elem(int _sem_id,int op,int _sem_num) 53 { 54 struct sembuf _sembuf; 55 _sembuf.sem_num=_sem_num; 56 _sembuf.sem_op=op; 57 _sembuf.sem_flg=0; 58 if(semop(_sem_id,&_sembuf,1)) 59 { 60 perror("semop"); 61 return -1; 62 } 63 return 0; 64 65 } 66 int p_sem_elem(int _sem_id,int _sem_num) 67 { 68 return com_op_sem_elem(_sem_id,-1,_sem_num); 69 } 70 int v_sem_elem(int _sem_id,int _sem_num) 71 { 72 return com_op_sem_elem(_sem_id,1,_sem_num); 73 } test.c: 1 #include "sem.h" 2 int main() 3 { 4 5 int _sem_id=create_sem_set(1); 6 pid_t pid=fork(); 7 init_sem_set(_sem_id,0,1); 8 if(pid<0) 9 { 10 perror("pid"); 11 return -1; 12 } 13 else if(pid==0) 14 { 15 while(1) 16 { 17 int _sem_id= get_sem_set(1); 18 p_sem_elem(_sem_id,0); 19 printf("A"); 20 sleep(3); 21 printf("A"); 22 fflush(stdout); 23 sleep(2); 24 v_sem_elem(_sem_id,0); 25 } 26 } 27 28 //destroy_sem_set(_sem_id);; 29 else 30 { 31 while(1) 32 { 33 p_sem_elem(_sem_id,0); 34 printf("B"); 35 sleep(5); 36 printf("B"); 37 fflush(stdout); 38 sleep(5); 39 v_sem_elem(_sem_id,0); 40 41 } 42 waitpid(pid,NULL,0); 43 destroy_sem_set(_sem_id); 44 } 45 return 0; 46 } Makefile: 1 SEM:sem.c test.c 2 gcc -o $@ $^ 3 .PHONY:clean 4 clean: 5 rm -f SEM 结果: [admin@localhost SEM]$ ./SEM AABBAAAABBAAAABBAAAABBAAAABBAAAABBAAAABBAAAABBAAAABBAA^C