进程间通信之信号量
信号量:相当于一个计数器,计录当前环境某种资源个数。没有对信号进行传输,保证对信号量操作是原子的。主要为了保护临界资源。生命周期随内核。
临界区:访问共享资源的代码区
临界资源:进程间所共享的资源
互斥:同一时刻,只允许一个进程对这份资源访问,这个进程对资源具有独占性,排他性。
同步:互斥情况下,进程对临界资源访问具有顺序。
信号量有两个基本操作:P,V
p:减一,向信号量申请资源,申请到后若p=0,则将进程挂起,否则信号量减一。
v:加一,使用资源完毕,归还给系统,若有进程挂起,则唤醒等待进程或线程,否则将信号量加一。
使用到的主要函数原型:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
int semctl(int semid, int semnum, int cmd, ...);
int semop(int semid, struct sembuf *sops, unsigned nsops);
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
struct sembuf{
unsigned short sem_num; /* semaphore number */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
}中sem_flg表示以什么形式控制信号量,0表示默认,SEM_NODO表示取消
注意:信号量的创建是以信号量集的形式创建
当semctl(semid,semnum,cmd);要删除时cmd被设置为IPC_RMID,semnum被忽视。当cmd为SETVAL时,对第semnum个信号量初始化。
信号量集中信号量从0开始
grep -ER 'semum' /usr/include/ 查询semum
//comm.h 1 #pragma once 2 #include<stdio.h> 3 #include<stdlib.h> 4 #include<unistd.h> 5 #include<sys/types.h> 6 #include<sys/ipc.h> 7 #include<sys/sem.h> 8 #define _PATH_ "." 9 #define _PROJ_ID_ 0x8888 10 union semun 11 { 12 int val; 13 struct semid_ds* buf; 14 unsigned short *array; 15 struct seminfo *__buf; 16 }; 17 //struct sembuf 18 //{ 19 // unsigned short sem_num; 20 // short sem_op; 21 // short sem_flg; 22 // }; 23 static int _sem_set(int snum,int flags); 24 static int sem_op(int sem_id,int nsops,int flags); 25 int create_sem(int snum); 26 int get_sem(int snum); 27 int init_sem(int sem_id,int snum,int unit_val); 28 int sem_p_element(int sem_id,int nsops); 29 int sem_v_element(int sem_id,int nsops); 30 int destory_sem_element(int sem_id); //comm.c 1 #include"comm.h" 2 static int _sem_set(int snum,int flags) 3 { 4 key_t _key=ftok(_PATH_,_PROJ_ID_); 5 if(_key<0) 6 { 7 perror("ftok"); 8 return -1; 9 } 10 int sem_id=-1; 11 sem_id=semget(_key,snum,flags); 12 if(sem_id<0) 13 { 14 15 perror("semget"); 16 return -1; 17 } 18 return sem_id; 19 } 20 21 int create_sem(int snum) 22 { 23 int flags=IPC_CREAT|IPC_EXCL|0666; 24 int ret= _sem_set(snum,flags); 25 return ret; 26 } 27 int get_sem(int snum) 28 { 29 return _sem_set(snum,IPC_CREAT); 30 } 31 int init_sem(int sem_id,int snum,int unit_val) 32 { 33 union semun _un; 34 _un.val=unit_val; 35 if(semctl(sem_id,snum,SETVAL,_un)<0) 36 { 37 perror("semctl\n"); 38 return -1; 39 } 40 return 0; 41 } 42 static int sem_op(int sem_id,int seqnum,int op) 43 { 44 struct sembuf _sm; 45 _sm.sem_num=seqnum; 46 _sm.sem_op=op; 47 _sm.sem_flg=0; 48 if(semop(sem_id,&_sm,1)<0) 49 { 50 perror("semop"); 51 return -1; 52 } 53 return 0; 54 } 55 int sem_p_element(int sem_id,int seqnum) 56 { 57 return sem_op(sem_id,seqnum,-1); 58 } 59 int sem_v_element(int sem_id,int seqnum) 60 { 61 return sem_op(sem_id,seqnum,1); 62 } 63 int destory_sem_element(int sem_id) 64 { 65 if(semctl(sem_id,IPC_RMID,0,NULL)<0) 66 { 67 perror("semctl\n"); 68 return -1; 69 } 70 return 0; 71 } //test.c 1 #include"comm.h" 2 int main() 3 { 4 int sem_id=create_sem(1); 5 if(sem_id<0) 6 { 7 printf("error\n"); 8 return -1; 9 } 10 init_sem(sem_id,1,1); 11 pid_t pid=fork(); 12 if(pid<0) 13 { 14 perror("pid"); 15 return -1; 16 } 17 else if(pid==0) 18 { 19 int sem_pid=get_sem(1); 20 while(1) 21 { 22 sem_p_element(sem_pid,0); 23 printf("A"); 24 sleep(1); 25 fflush(stdout); 26 printf("A"); 27 sleep(8); 28 fflush(stdout); 29 sem_v_element(sem_pid,0); 30 } 31 } 32 else 33 { 34 while(1) 35 { 36 sem_p_element(sem_id,0); 37 sleep(3); 38 printf("B"); 39 sleep(2); 40 fflush(stdout); 41 printf("B"); 42 sleep(5); 43 fflush(stdout); 44 sem_v_element(sem_id,0); 45 } 46 waitpid(pid,NULL,0); 47 destory_sem_element(sem_id); 48 49 } 50 return 0; 51 } //Makefile 1 .PHONY:all 2 all:test 3 test:test.c comm.c 4 gcc -o $@ $^ 5 .PHONY:clean 6 clean: 7 rm -f test
未使用信号量之前:
使用后: