文章目錄
- 并發編程相關基礎概念
- 信號量
- 深刻理解信號量
- 使用共享資源的方式
- 分塊使用共享資源的方式會出現的問題
- 舉例子理解信號量的第二個特性---預定
- 信號量要成為計數器面臨的問題
- 信號量相關操作接口--POSIX
- 庫函數:sem_init
- 庫函數:sem_destroy
- 庫函數:sem_wait
- 庫函數:sem_post
- 信號量相關操作接口--systemV
- 系統調用:semget
- 系統調用:semop
并發編程相關基礎概念
保護共享內存的本質就是:
對訪問共享內存的代碼(臨界區)進行限制
信號量
信號量和其他進程間通信的作用不太一樣,它主要是用來實現進程(線程)間同步互斥的
信號量(進程)的生命周期也和共享內存一樣:隨內核
所以
①要么由進程使用系統調用釋放信號量
②要么操作系統重啟
深刻理解信號量
信號量本質是一個對資源進行預定
的計數器
使用共享資源的方式
訪問臨界資源的時候,有兩種訪問方式:
-
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操作