个人主页:Lei宝啊
愿所有美好如期而遇
理论
信号量是对公共资源的一种预定机制,资源不一定非要持有才算自己的,预定了也算,在未来任意时刻,仍然可以使用。
像我们申请有一块共享内存,如果一个进程正在使用,那么其他进程就需要进行等待,使得资源不会被并发访问,但是这种一次申请一块共享内存却只能由一个进程访问,效率并不高,于是就有了分配资源,将这个共享内存划分成几块,不同的进程去申请这些资源,申请成功则可以使用,失败则等待,而信号量就是对这些资源进行计数的!本质上信号量也是属于进程间通信。
于是,所有的进程在访问临界资源时,都需要去申请信号量,这也是被共同遵守的。
下面,回答几个问题,介绍几个概念:
临界资源是什么?临界资源就是被保护起来的,任何时刻只允许一个执行流访问的公共资源。访问临界资源的代码,我们叫做临界区,保护临界资源,实质上就是保护临界区。
而我们的信号量是用来保护临界资源的,访问临界资源前,需要向他申请,但是我们的信号量也是临界资源,谁来保护他呢?我们有没有一个问题,在申请和释放信号量的过程中,如果突然被打断,会发生什么?
第一个问题,信号量的申请和释放(PV操作)是原子的,由操作系统去保护,什么叫原子?原子就是一个操作不可以再被分割,是就是是,不是就是否,要么做完,要么不做。
第二个问题,他不会被打断,要么申请成功,要么就失败。
第三个问题,既然你信号量是个计数器,我直接搞一个count不就行了吗,使用他来计数不可以吗?在多进程中,我们希望每个进程都能够一直看见count的值,并且要求count++和--的操作是原子的,但是count变量++和--的操作不是原子的,在汇编层面,他会将count先写进CPU,然后CPU对他做++,然后写回变量,这个过程是可以被打断的,同时,我们无法让一个普通变量一直被多进程看到,所以我们有了信号量。
我们还可以通过他的接口可以看到他和共享内存,消息队列之间的一些共性,比如说 struct ipc_perm,三者的结构体变量中都有这样一个结构体变量,而且都放在结构体的头部,我们看图:
在操作系统内核中,是这样一个结构,看图:
而我们知道,一个结构体的地址在值上和他的第一个成员变量的值是相等的。于是我们对共享内存等的管理,就变成了对柔性数组的增删查改。(通过找到ipc_perm中的_key)
那么我们只通过ipc_perm*怎么知道哪个是共享内存,哪个是信号量?在ipc_perm中,有一个mode变量,存放着我们创建时的类型,使用宏的方式,同时我们定义了这样的宏,所以,确定类型,就变成了ipc_perm* ->mode & 宏,我们写三个接口,就可以判断类型,返回类型后对ipc_perm*类型强制类型转换,就可以进行访问了。
而我们的shmid,msgid,semid,都是柔性数组中的下标。
其实这里不好的点在于,他们的结构没有向文件靠拢,如果将他们的结构体再做一层封装,封进strcut file中,将struct file*填入数组中返回下标,这样就契合了Linux下一切皆文件,在使用时我们也能够统一看待,也就能使用统一的接口,而不是像现在这样。