进程间通信(四) 信号量

xiaoxiao2021-02-28  38

信号量的作用

我们知道如果多个进程在访问临界资源时如果不加限制,那么就会出现进程对资源的争用,可能会出现结果的错误,更严重会造成进程的死锁

生活中也有很多例子,例如一个打印机在某个时刻只能打印一份文件,而且在这个文件打印完之前是不能打印其他文件的,因为打印机是共享的,就是临界资源,必须保证打印机的同步与互斥

信号量就是控制临界区域的临界资源的一种手段

信号量

信号量可以抽象为一种数据结构

包含了一些值和对这些值的操作

信号量的值可以假象为该类资源的计数器

假设 信号量的值为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了

转载请注明原文地址: https://www.6miu.com/read-2624887.html

最新回复(0)