我们知道如果多个进程在访问临界资源时如果不加限制,那么就会出现进程对资源的争用,可能会出现结果的错误,更严重会造成进程的死锁
生活中也有很多例子,例如一个打印机在某个时刻只能打印一份文件,而且在这个文件打印完之前是不能打印其他文件的,因为打印机是共享的,就是临界资源,必须保证打印机的同步与互斥
信号量就是控制临界区域的临界资源的一种手段
信号量可以抽象为一种数据结构
包含了一些值和对这些值的操作
信号量的值可以假象为该类资源的计数器
假设 信号量的值为s,则代表下列含义
s>0 表示此类资源还剩多少个s=0 此类资源刚好用完,无进程等待s<0 它的绝对值就是等待资源的进程(厕所人满了,厕所门口排的队)同时信号量的操作仅支持P,V操作
1.P操作:使资源 -1
2 V操作: 使资源 +1
如果信号量的值为1,
它就称为二元信号量
因为这样它只有两个可能取值 0和1
分别
0表示此资源被占用其他进程无法获得
1表示此资源没有被占用,可以被其他进程获得
因为System V的IPV接口风格都差不多,
具体函数使用方法这里不做过多介绍,参阅man手册即可
我们通过fork创建一个子进程
父进程和子进程打印AA和BB
那么屏幕就成了这两个进程的临界资源
#include"comm.h" int main() { pid_t pid = fork(); if( pid < 0 ) { perror("error"); return -1; } if( pid == 0 ) { /*int _semid = getSemSet(0);*/ while(1) { printf("A "); fflush(stdout); //sleep挂起进程会导致争用资源 sleep(1); printf("A "); fflush(stdout); } } if( pid > 0) { while(1) { printf("B "); fflush(stdout); sleep(1); printf("B "); fflush(stdout); } wait(NULL); } return 0; }结果如下
发现不能成对的打印AA或者BB
把程序修改 用信号量维护屏幕
comm.c
#include<stdio.h> #include<sys/types.h> #include<sys/ipc.h> #include<sys/sem.h> #include<unistd.h> #define PATHNAME "/tmp" #define PRO_J 0X6666 union semun { int val; struct semid_ds *buf; unsigned short *array; struct seminfo *__buf; }; int createSemSet(int nsems); int getSemSet(int nsems); //类似 创建一个数组semid,数组个数nums,每个元素的值 initVal //num 0 1 2 3.... int initSem(int semid,int nums,int initVal); int P(int semid,int who); int V(int semid,int who); int destorySemSet(int semid);comm.h
#include"comm.h" static int getSemId(int nums,int flags) { key_t key = ftok(PATHNAME,PRO_J); if( key < 0 ) { perror("ftok"); return -1; } int semid = semget(key,nums,flags); if( semid < 0 ) { perror("semget"); return -1; } return semid; } int createSemSet(int nums) { return getSemId(nums,IPC_CREAT|IPC_EXCL|0666); } int getSemSet(int nums) { return getSemId(nums,IPC_CREAT); } int initSem(int semid,int nums,int inintVal) { union semun _un; _un.val = inintVal; if(semctl(semid,nums,SETVAL,_un) < 0) { perror("init Sem:semctl"); return -1; } return 0; } int destorySemSet(int semid) { if( semctl(semid, 0,IPC_RMID ) < 0 ) { perror("semctl"); return -1; } return 0; } int commPV(int semid,int who,int op) { struct sembuf _buf; _buf.sem_num = who; _buf.sem_op = op; _buf.sem_flg = 0; if(semop(semid,&_buf, 1) < 0) { perror("semop"); return -1; } return 0; } int P(int semid,int who) { commPV(semid,who, -1); } int V(int semid,int who) { commPV(semid,who, 1); }pv.c
#include"comm.h" int main() { int semid = createSemSet(1); initSem(semid,0,1); pid_t pid = fork(); if( pid < 0 ) { perror("error"); return -1; } if( pid == 0 ) { int _semid = getSemSet(0); while(1) { //对_semid数组0号下标的信号量进行P操作 P(_semid,0); printf("A "); fflush(stdout); //sleep会导致争用资源 sleep(1); printf("A "); fflush(stdout); V(_semid,0); } } if( pid > 0) { while(1) { P(semid,0); printf("B "); fflush(stdout); sleep(1); printf("B "); fflush(stdout); V(semid,0); } wait(NULL); } destorySemSet(semid); return 0; } }程序现在可以正确打印成对的AABB了