文章目录
并发编程相关基础概念
保护共享内存的本质就是:
对访问共享内存的代码(临界区)进行限制
信号量
信号量和其他进程间通信的作用不太一样,它主要是用来实现进程(线程)间同步互斥的
信号量(进程)的生命周期也和共享内存一样:随内核
所以
①要么由进程使用系统调用释放信号量
②要么操作系统重启
深刻理解信号量
信号量本质是一个对资源进行预定
的计数器
使用共享资源的方式
访问临界资源的时候,有两种访问方式:
1.一次直接使用一整个临界资源
比如
我们之前对管道的使用,从来就没有考虑过写入位置和读取位置在哪的问题
只是直接从管道读取,直接向管道写入,是直接以一整个管道为单位进行使用- 此时出于对共享资源的保护的互斥性,一次就只能一个进程访问这个管道
2.把一个临界资源分成多个分区,让不同进程可以使用不同分区
这样的好处是:- 1.可以在不违反保护共享资源特性的情况下,
一个共享资源可以被多个进程同时使用(并发使用)
- 2.申请一块共享资源非常繁琐,所以如果多个进程想两两通信
可以只申请一块共享资源,再把它分区给所有进程使用 只申请一块共享资源也可以减少使用系统调用
- 1.可以在不违反保护共享资源特性的情况下,
分块使用共享资源的方式会出现的问题
分块使用共享资源虽然可以做到一块共享资源,多个进程并发使用
但是一种方法了解决一个问题,必然又会出现新的问题
这个新的问题就是:
共享资源的分区个数<进程个数怎么办?
例如
我们把一个共享资源分成了16个区,但是同时来了30个进程要访问共享资源怎么办?
此时再怎么分配,也不可能让所有进程同时使用这个共享资源=
只能先分配16个进程,等一些进程使用丸了,再让剩下的进程进来用
如何做到这一点呢?
使用计数器!!!
让计数器等于分区数,这样一个进程要使用共享资源前,都先判断一下
const是否>0
如果是就是还有分区空着,可以给它,再count--
如果不是,就让进程阻塞等待同理,如果有一个
进程退出了临界区
,就会有一个分区空出来,count++
所以本质上共享资源使用方法1和2其实是一样的,因为整体使用时不就是分区个数只有1个吗?
举例子理解信号量的第二个特性—预定
我们生活中,不管是看电影还是坐高铁,都要买票,因为它们的名额都是需要竞争的资源
买票的本质就是:对资源进行预定
就是买到票了,我就算不去,那个资源也是属于我的
而且票的个数也有限,因为资源的个数有限,不能出现两个人用同一个资源的情况,所以票又何尝不是一个计数器呢?
我们上面所说的进程竞争使用共享资源的分区,本质也是如此
只要一个进程抢到了计数器的名额,共享资源中就一定有一个分区会给它
所以信号量的本质是和票一样,是对资源进行预定的计数器
所以
人们抢高铁座位,其实是抢票
进程抢共享资源的分区,其实是抢计数器(信号量)名额
所以共享资源分区,最重要的不是分配,而是对计数器(信号量)的正确使用
对共享资源整体使用的时候,计数器count不是等于0就是等于1
此时称这个计数器为:二元信号量或者锁
信号量要成为计数器面临的问题
1.如何让不同进程看到计数器(信号量)呢?
让信号量作为共享资源
!!!2.信号量本身就是共享资源,它去包含其他的共享资源,那谁来保护它呢?
比如两个进程同时看见信号量还剩一个,它们同时去抢,同时抢到手,count同时=0
这不就卡bug了吗?
信号量本质是一个对共享资源进行预定的计数器
计数器的操作无非就是++和–
只要让这两个操作都具有不可中断的原子性
不就可以了吗?
所以操作系统为信号量专门定义了具有两个原子性的操作
即P操作[++]和V操作[–]
信号量相关操作接口–POSIX
使用信号量接口之前,需要定义一个信号量对象(sem_t
类型)
库函数:sem_init
头文件:
semaphore.h
参数表:
1.
sem_t*sem
:
要初始化的信号量的地址2.
int pshared
:
1.如果是pshared是0,则表示线程间信号量
2.如果是pshared非0,则表示进程间信号量3.
unsigned int val
:
即:信号量这个计数器的初始值,也就是把共享资源分成几份/共享资源的个数
作用:
初始化对应的信号量
库函数:sem_destroy
头文件:
semaphore.h
参数表:
sem_t*sem
:要销毁的信号量的地址作用:
销毁对应的信号量
库函数:sem_wait
头文件:
semaphore.h
参数表:
sem_t*sem
:对应的信号量的地址作用:
申请信号量,P操作,信号量–
(申请信号量失败【即信号量减到0
】,线程/进程会被阻塞)
库函数:sem_post
头文件:
semaphore.h
参数表:
sem_t*sem:对应的信号量的地址作用:
释放信号量,V操作,信号量++
信号量相关操作接口–systemV
如果有多个共享内存要分区使用,那么就需要多个信号量
所以操作系统提供信号量,是以信号量集合的方式提供的
即:申请信号量的时候,申请的是一个/多个信号量
系统调用:semget
头文件:
sys/types.h
和sys/ipc.h
和sys/sem.h
返回值:int类型
①成功,返回用户(进程)使用的信号量集合标识符合条件
②失败,返回-1参数表:
①
key_t key
:内核中唯一标识一个信号量集合(key的获取和使用方法于共享内存的key一模一样,使用ftok获取)②
int nsems
:信号量集合中的信号量个数③
int semflag
:位图标志位
也和共享内存的一模一样
IPC_CREAT
:单独使用的话如果对应的共享内存不存在,就创建。存在就返回shmid
该选项主要给获取共享内存的进程使用
IPC_EXCL
:不能单独使用
但是
IPC_CREAT | IPC_EXCL
:
如果对应的共享内存不存在,就创建并在页表建立映射关系
存在就报错
该选项主要给创建共享内存的进程使用
作用:创建(获取)一个信号量集合
系统调用:semop
头文件:
sys/types.h
和sys/ipc.h
和sys/sem.h
参数表:
①
int semid
:进程使用的信号量集合唯一标识②
struct sembuf*p
:指向一个struct sembuf的数组的起始地址
struct sembuf
{
int sem_num//信号量集合的下标
short sem_op//-1表示–,1表示++
short sem_flaf//一般不管,设置为0
}
semop这样设计是为了,可以同时对一个信号量集合中的多个信号量进行PV操作或者对信号量集合进行整体操作③int nsops :参数②指向的数组的元素个数
作用:对一个信号量集合中的一个/多个信号量进行PV操作