一、信号量的概念理论渗透
1.1 基本概念
- 共享资源:多个执行流,可以看到的一份资源
- 临界资源:被保护起来的资源 —— 保护的方式:同步和互斥
- 互斥:任何时候只能有一个进程在访问共享资源
- 资源,一定要被程序员进行访问的,我们使用代码进行访问, 代码 = 访问共享资源的代码(临界区) + 不访问共享资源的代码(非临界区)
- 所谓的对共享资源进行保护——临界资源——本质上就是对访问共享资源的代码的保护
1.2 什么是同步和互斥
- 由于各进程要求共享资源,而且有些资源需要互斥使用,因此各进程间竞争使用这些资源,进程的这种关系为进程的互斥
- 系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源。
- 在进程中涉及到互斥资源的程序段叫临界区
1.3 特性方面
IPC资源必须删除,否则不会自动消除,除非重启,所以System V IPC资源的生命周期随内核
二、如何理解信号量的理论
信号量(信号灯):用于保护共享资源(临界资源)
我们来举个例子:电影院买票,我们需要进行买票。我们在之前的共享内存是一个整体使用的,但是在电影院中,会有很多的座位供我们挑选。信号量本质上是一个计数器,当我们购买电影票时,我们需要进行电影票的预定,申请信号量的本质就是对公共资源的一种预定机制。
信号量分为二元信号量和多元信号量,在二元信号量中,信号量的个数为1(相当于将临界资源看成一整块),二元信号量本质解决了临界资源的互斥问题,以下面的伪代码进行解释:
while (1) { if(sem == 1) sum--; else // 挂起 // 写入共享内存 sem++; }
根据以上代码,当进程A申请访问共享内存资源时,如果此时sem为1(sem代表当前信号量个数),则进程A申请资源成功,此时需要将sem减减,然后进程A就可以对共享内存进行一系列操作,但是在进程A在访问共享内存时,若是进程B申请访问该共享内存资源,此时sem就为0了,那么这时进程B会被挂起,直到进程A访问共享内存结束后将sem加加,此时才会将进程B唤起,然后进程B再对该共享内存进行访问操作。
在这种情况下,无论什么时候都只会有一个进程在对同一份共享内存进行访问操作,也就解决了临界资源的互斥问题。
实际上,代码中计数器sem减减的操作就叫做P操作,而计数器加加的操作就叫做V操作,P操作就是申请信号量,而V操作就是释放信号量。
三、信号量的操作
3.1 信号量的函数
3.1.1 semget函数
函数的原型:
函数的功能:
创建一个新的信号量或获取一个已经存在的信号量的键值。
函数的参数:
- key:为整型值,用户可以自己设定。有两种情况:键值是IPC_PRIVATE,该值通常为0,意思就是创建一个仅能被进程进程给我的信号量。键值不是IPC_PRIVATE,我们可以指定键值,例如1234;也可以一个ftok()函数来取得一个唯一的键值。
- nsems:表示初始化信号量的个数。比如我们要创建一个信号量,则该值为1.,创建2个就是2。
- semflg:信号量的创建方式或权限。有IPC_CREAT,IPC_EXCL。IPC_CREAT如果信号量不存在,则创建一个信号量,否则获取。IPC_EXCL只有信号量不存在的时候,新的信号量才建立,否则就产生错误。
函数的返回值:
成功返回信号量的标识码ID。失败返回-1
3.1.2 semctl函数
函数的原型:
函数的功能:
控制信号量的函数,在这个函数中我们可以删除信号量或初始化信号量。
函数的参数:
- semid:信号量的标志码(ID),也就是semget()函数的返回值
- semnum:操作信号在信号集中的编号。从0开始
- cmd:表示要进行的操作
cmd参数可以使用的命令:
- IPC_STAT读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。
- IPC_SET设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数。
- IPC_RMID将信号量集从内存中删除。
- GETALL用于读取信号量集中的所有信号量的值。
- GETNCNT返回正在等待资源的进程数目。
- GETPID返回最后一个执行semop操作的进程的PID。
- GETVAL返回信号量集中的一个单个的信号量的值。
- GETZCNT返回这在等待完全空闲的资源的进程数目。
- SETALL设置信号量集中的所有的信号量的值。
- SETVAL设置信号量集中的一个单独的信号量的值。
函数的返回值:
成功返回0,失败返回-1
3.1.3 semop函数
函数的原型:
函数的功能:
用户改变信号量的值。也就是使用资源还是释放资源使用权
函数的参数:
- shmid:信号量的标识码。也就是semget()的返回值。
- sops是一个指向结构体数组的指针。
struct sembuf{ unsigned short sem_num;//第几个信号量,第一个信号量为0; short sem_op;//对该信号量的操作。 short _semflg; };
- nsops:操作结构的数量,恒大于或等于1。
函数的返回值:
成功返回0,失败返回-1
四、信号量的指令
// 查看系统中的信号量 ipcs -s // 删除系统中的信号量 ipcrm -s shmid
五、操作系统如何把共享内存、消息队列、信号量管理起来
通过对system V系列进程间通信的学习,可以发现共享内存、消息队列以及信号量,虽然它们内部的属性差别很大,但是维护它们的数据结构的第一个成员确实一样的,都是ipc_perm类型的成员变量。
这样设计的好处就是,在操作系统内可以定义一个struct ipc_perm类型的数组,此时每当我们申请一个IPC资源,就在该数组当中开辟一个这样的结构。