Linux下關(guān)于互斥鎖及同步的移植
將win32程序關(guān)于多線程的內(nèi)容移植到Linux下面,不能簡(jiǎn)單的按照函數(shù)對(duì)應(yīng)來(lái)移植。不過(guò)通過(guò)下面的對(duì)應(yīng)關(guān)系,再加上你對(duì)這些模式的深入了解,相信會(huì)移植的很成功。
信號(hào)量
Windows 信號(hào)量是一些計(jì)數(shù)器變量,允許有限個(gè)線程/進(jìn)程訪問(wèn)共享資源。Linux POSIX 信號(hào)量也是一些計(jì)數(shù)器變量,可以用來(lái)在 Linux 上實(shí)現(xiàn) Windows 上的信號(hào)量功能。
信號(hào)量的類型: Windows 提供了有名(named)信號(hào)量和無(wú)名(unnamed)信號(hào)量。有名信號(hào)量可以在進(jìn)程之間進(jìn)行同步。在 Linux 上,在相同進(jìn)程的不同線程之間,則只使用 POSIX 信號(hào)量。在進(jìn)程之間,可以使用 System V 信號(hào)量。
等待函數(shù)中的超時(shí): 當(dāng)在一個(gè)等待函數(shù)中使用時(shí),可以為 Windows 信號(hào)量對(duì)象指定超時(shí)值。在 Linux 中,并沒(méi)有提供這種功能,只能通過(guò)應(yīng)用程序邏輯處理超時(shí)的問(wèn)題。
創(chuàng)建信號(hào)量
在 Windows 中,可以使用 CreateSemaphore() 創(chuàng)建或打開(kāi)一個(gè)有名或無(wú)名的信號(hào)量。
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
LONG lInitialCount,
LONG lMaximumCount,
LPCTSTR lpName
);
在這段代碼中:
lpSemaphoreAttributes 是一個(gè)指向安全性屬性的指針。如果這個(gè)指針為空,那么這個(gè)信號(hào)量就不能被繼承。
lInitialCount 是該信號(hào)量的初始值。
lMaximumCount 是該信號(hào)量的最大值,該值必須大于 0。
lpName 是信號(hào)量的名稱。如果該值為 NULL,那么這個(gè)信號(hào)量就只能在相同進(jìn)程的不同線程之間共享。否則,就可以在不同的進(jìn)程之間進(jìn)行共享。
這個(gè)函數(shù)創(chuàng)建信號(hào)量,并返回這個(gè)信號(hào)量的句柄。它還將初始值設(shè)置為調(diào)用中指定的值。這樣就可以允許有限個(gè)線程來(lái)訪問(wèn)某個(gè)共享資源。
在 Linux 中,可以使用 sem_init() 來(lái)創(chuàng)建一個(gè)無(wú)名的 POSIX 信號(hào)量,這個(gè)調(diào)用可以在相同進(jìn)程的線程之間使用。
它還會(huì)對(duì)信號(hào)量計(jì)數(shù)器進(jìn)行初始化:int sem_init(sem_t *sem, int pshared, unsigned int value)。在這段代碼中:
value(信號(hào)量計(jì)數(shù)器)是這個(gè)信號(hào)量的初始值。
pshared 可以忽略,因?yàn)樵谀壳暗膶?shí)現(xiàn)中,POSIX 信號(hào)量還不能在進(jìn)程之間進(jìn)行共享。
這里要注意的是,最大值基于 demaphore.h 頭文件中定義的 SEM_VALUE_MAX。
在 Linux 中,semget() 用于創(chuàng)建 System V 信號(hào)量,它可以在不同集成的線程之間使用??梢杂盟鼇?lái)實(shí)現(xiàn)與 Windows 中有名信號(hào)量相同的功能。這個(gè)函數(shù)返回一個(gè)信號(hào)量集標(biāo)識(shí)符,它與一個(gè)參數(shù)的鍵值關(guān)聯(lián)在一起。當(dāng)創(chuàng)建一個(gè)新信號(hào)量集時(shí),對(duì)于與 semid_ds 數(shù)據(jù)結(jié)構(gòu)關(guān)聯(lián)在一起的信號(hào)量,semget() 要負(fù)責(zé)將它們進(jìn)行初始化,方法如下:
sem_perm.cuid 和 sem_perm.uid 被設(shè)置為調(diào)用進(jìn)程的有效用戶 ID。
sem_perm.cgid 和 sem_perm.gid 被設(shè)置為調(diào)用進(jìn)程的有效組 ID。
sem_perm.mode 的低 9 位被設(shè)置為 semflg 的低 9 位。
sem_nsems 被設(shè)置為 nsems 的值。
sem_otime 被設(shè)置為 0。
sem_ctime 被設(shè)置為當(dāng)前時(shí)間。
用來(lái)創(chuàng)建 System V 信號(hào)量使用的代碼是:int semget(key_t key, int nsems, int semflg)。下面是對(duì)這段代碼的一些解釋:
key 是一個(gè)惟一的標(biāo)識(shí)符,不同的進(jìn)程使用它來(lái)標(biāo)識(shí)這個(gè)信號(hào)量集。我們可以使用 ftok() 生成一個(gè)惟一的鍵值。
IPC_PRIVATE 是一個(gè)特殊的 key_t 值;當(dāng)使用 IPC_PRIVATE 作為 key 時(shí),這個(gè)系統(tǒng)調(diào)用就會(huì)只使用 semflg 的低 9 位,但卻忽略其他內(nèi)容,從而新創(chuàng)建一個(gè)信號(hào)量集(在成功時(shí))。
nsems 是這個(gè)信號(hào)量集中信號(hào)量的數(shù)量。
semflg 是這個(gè)新信號(hào)量集的權(quán)限。要新創(chuàng)建一個(gè)信號(hào)量集,您可以將使用 IPC_CREAT 來(lái)設(shè)置位操作或訪問(wèn)權(quán)限。如果具有該 key 值的信號(hào)量集已經(jīng)存在,那么 IPC_CREAT/IPC_EXCL 標(biāo)記就會(huì)失敗。
注意,在 System V 信號(hào)量中,key 被用來(lái)惟一標(biāo)識(shí)信號(hào)量;在 Windows 中,信號(hào)量是使用一個(gè)名稱來(lái)標(biāo)識(shí)的。
為了對(duì)信號(hào)量集數(shù)據(jù)結(jié)構(gòu)進(jìn)行初始化,可以使用 IPC_SET 命令來(lái)調(diào)用 semctl() 系統(tǒng)調(diào)用。將 arg.buf 所指向的 semid_ds 數(shù)據(jù)結(jié)構(gòu)的某些成員的值寫(xiě)入信號(hào)量集數(shù)據(jù)結(jié)構(gòu)中,同時(shí)更新這個(gè)結(jié)構(gòu)的 sem_ctime member 的值。用戶提供的這個(gè) arg.buf 所指向的 semid_ds 結(jié)構(gòu)如下所示:
sem_perm.uid
sem_perm.gid
sem_perm.mode (只有最低 9 位有效)
#p#調(diào)用進(jìn)程的有效用戶 ID 應(yīng)該是超級(jí)用戶,或者至少應(yīng)該與這個(gè)信號(hào)量集的創(chuàng)建者或所有者匹配: int semctl(int semid, int semnum, int cmd = IPC_SET, ...)。在這段代碼中:
semid 是信號(hào)量集的標(biāo)識(shí)符。
semnum 是信號(hào)量子集偏移量(從 0 到 nsems -1,其中 n 是這個(gè)信號(hào)量集中子集的個(gè)數(shù))。這個(gè)命令會(huì)被忽略。
cmd 是命令;它使用 IPC_SET 來(lái)設(shè)置信號(hào)量的值。
args 是這個(gè)信號(hào)量集數(shù)據(jù)結(jié)構(gòu)中要通過(guò) IPC_SET 來(lái)更新的值(在這個(gè)例子中會(huì)有解釋)。
最大計(jì)數(shù)器的值是根據(jù)在頭文件中定義的 SEMVMX 來(lái)決定的。
打開(kāi)信號(hào)量
在 Windows 中,我們使用 OpenSemaphore() 來(lái)打開(kāi)某個(gè)指定信號(hào)量。只有在兩個(gè)進(jìn)程之間共享信號(hào)量時(shí),才需要使用信號(hào)量。在成功打開(kāi)信號(hào)量之后,這個(gè)函數(shù)就會(huì)返回這個(gè)信號(hào)量的句柄,這樣就可以在后續(xù)的調(diào)用中使用它了。
HANDLE OpenSemaphore(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
)
在這段代碼中:
dwDesiredAccess 是針對(duì)該信號(hào)量對(duì)象所請(qǐng)求的訪問(wèn)權(quán)。
bInheritHandle 是用來(lái)控制這個(gè)信號(hào)量句柄是否可繼承的標(biāo)記。如果該值為 TRUE,那么這個(gè)句柄可以被繼承。
lpName 是這個(gè)信號(hào)量的名稱。
在 Linux 中,可以調(diào)用相同的 semget() 來(lái)打開(kāi)某個(gè)信號(hào)量,不過(guò)此時(shí) semflg 的值為 0:int semget(key,nsems,0)。在這段代碼中:
key 應(yīng)該指向想要打開(kāi)的信號(hào)量集的 key 值。
為了打開(kāi)一個(gè)已經(jīng)存在的信號(hào)量,可以將 nsems 和標(biāo)記設(shè)置為 0。semflg 值是在返回信號(hào)量集標(biāo)識(shí)符之前對(duì)訪問(wèn)權(quán)限進(jìn)行驗(yàn)證時(shí)設(shè)置的。
獲取信號(hào)量
在 Windows 中,等待函數(shù)提供了獲取同步對(duì)象的機(jī)制??梢允褂玫牡却瘮?shù)有多種類型;在這一節(jié)中,我們只考慮 WaitForSingleObject()(其他類型將會(huì)分別進(jìn)行討論)。這個(gè)函數(shù)使用一個(gè)信號(hào)量對(duì)象的句柄作為參數(shù),并會(huì)一直等待下去,直到其狀態(tài)變?yōu)橛行盘?hào)狀態(tài)或超時(shí)為止。
DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds );
在這段代碼中:
hHandle 是指向互斥句柄的指針。
dwMilliseconds 是超時(shí)時(shí)間,以毫秒為單位。如果該值是 INFINITE,那么它阻塞調(diào)用線程/進(jìn)程的時(shí)間就是不確定的。
在 Linux 中,sem_wait() 用來(lái)獲取對(duì)信號(hào)量的訪問(wèn)。這個(gè)函數(shù)會(huì)掛起調(diào)用線程,直到這個(gè)信號(hào)量有一個(gè)非空計(jì)數(shù)為止。然后,它可以原子地減少這個(gè)信號(hào)量計(jì)數(shù)器的值:int sem_wait(sem_t * sem)。
在 POSIX 信號(hào)量中并沒(méi)有超時(shí)操作。這可以通過(guò)在一個(gè)循環(huán)中執(zhí)行一個(gè)非阻塞的 sem_trywait() 實(shí)現(xiàn),該函數(shù)會(huì)對(duì)超時(shí)值進(jìn)行計(jì)算:int sem_trywait(sem_t * sem)。
在使用 System V 信號(hào)量時(shí),如果通過(guò)使用 IPC_SET 命令的 semctl() 調(diào)用設(shè)置初始的值,那么必須要使用 semop() 來(lái)獲取信號(hào)量。semop() 執(zhí)行操作集中指定的操作,并阻塞調(diào)用線程/進(jìn)程,直到信號(hào)量值為 0 或更大為止:int semop(int semid, struct sembuf *sops, unsigned nsops)。
函數(shù) semop() 原子地執(zhí)行在 sops 中所包含的操作 也就是說(shuō),只有在這些操作可以同時(shí)成功執(zhí)行時(shí),這些操作才會(huì)被同時(shí)執(zhí)行。sops 所指向的數(shù)組中的每個(gè) nsops 元素都使用 struct sembuf 指定了一個(gè)要對(duì)信號(hào)量執(zhí)行的操作,這個(gè)結(jié)構(gòu)包括以下成員:
unsigned short sem_num; (信號(hào)量個(gè)數(shù))
short sem_op; (信號(hào)量操作)
short sem_flg; (操作標(biāo)記)
要獲取信號(hào)量,可以通過(guò)將 sem_op 設(shè)置為 -1 來(lái)調(diào)用 semop();在使用完信號(hào)量之后,可以通過(guò)將 sem_op 設(shè)置為 1 來(lái)調(diào)用 semop() 釋放信號(hào)量。通過(guò)將 sem_op 設(shè)置為 -1 來(lái)調(diào)用 semop(),信號(hào)量計(jì)數(shù)器將會(huì)減小 1,如果該值小于 0(信號(hào)量的值是不能小于 0 的),那么這個(gè)信號(hào)量就不能再減小,而是會(huì)讓調(diào)用線程/進(jìn)程阻塞,直到其狀態(tài)變?yōu)橛行盘?hào)狀態(tài)為止。
sem_flg 中可以識(shí)別的標(biāo)記是 IPC_NOWAIT 和 SEM_UNDO。如果某一個(gè)操作被設(shè)置了 SEM_UNDO 標(biāo)記,那么在進(jìn)程結(jié)束時(shí),該操作將被取消。如果 sem_op 被設(shè)置為 0,那么 semop() 就會(huì)等待 semval 變成 0。這是一個(gè)等待為 0 的操作,可以用它來(lái)獲取信號(hào)量。
記住,超時(shí)操作在 System V 信號(hào)量中并不適用。這可以在一個(gè)循環(huán)中使用非阻塞的 semop()(通過(guò)將 sem_flg 設(shè)置為 IPC_NOWAIT)實(shí)現(xiàn),這會(huì)計(jì)算超時(shí)的值。
#p#釋放信號(hào)量
在 Windows 中,ReleaseSemaphore() 用來(lái)釋放信號(hào)量。
BOOL ReleaseSemaphore(
HANDLE hSemaphore,
LONG lReleaseCount,
LPLONG lpPreviousCount
);
在這段代碼中:
hSemaphore 是一個(gè)指向信號(hào)量句柄的指針。
lReleaseCount 是信號(hào)量計(jì)數(shù)器,可以通過(guò)指定的數(shù)量來(lái)增加計(jì)數(shù)。
lpPreviousCount 是指向上一個(gè)信號(hào)量計(jì)數(shù)器返回時(shí)的變量的指針。如果并沒(méi)有請(qǐng)求上一個(gè)信號(hào)量計(jì)數(shù)器的值,那么這個(gè)參數(shù)可以是 NULL。
這個(gè)函數(shù)會(huì)將信號(hào)量計(jì)數(shù)器的值增加在 lReleaseCount 中指定的值上,然后將這個(gè)信號(hào)量的狀態(tài)設(shè)置為有信號(hào)狀態(tài)。
在 Linux 中,我們使用 sem_post() 來(lái)釋放信號(hào)量。這會(huì)喚醒對(duì)這個(gè)信號(hào)量進(jìn)行阻塞的所有線程。信號(hào)量的計(jì)數(shù)器同時(shí)被增加 1。要為這個(gè)信號(hào)量的計(jì)數(shù)器添加指定的值(就像是 Windows 上一樣),可以使用一個(gè)互斥變量多次調(diào)用以下函數(shù):int sem_post(sem_t * sem)。
對(duì)于 System V 信號(hào)量來(lái)說(shuō),只能使用 semop() 來(lái)釋放信號(hào)量:int semop(int semid, struct sembuf *sops, unsigned nsops)。
函數(shù) semop() 原子地執(zhí)行 sops 中包含的一組操作(只在所有操作都可以同時(shí)成功執(zhí)行時(shí),才會(huì)將所有的操作同時(shí)一次執(zhí)行完)。sops 所指向的數(shù)組中的每個(gè) nsops 元素都使用一個(gè) struct sembuf 結(jié)構(gòu)指定了一個(gè)要對(duì)這個(gè)信號(hào)量執(zhí)行的操作,該結(jié)構(gòu)包含以下元素:
unsigned short sem_num;(信號(hào)量個(gè)數(shù))
short sem_op; (信號(hào)量操作)
short sem_flg; (操作標(biāo)記)
要釋放信號(hào)量,可以通過(guò)將 sem_op 設(shè)置為 1 來(lái)調(diào)用 semop()。通過(guò)將 semop() 設(shè)置為 1 來(lái)調(diào)用 semop(),這個(gè)信號(hào)量的計(jì)數(shù)器會(huì)增加 1,同時(shí)用信號(hào)通知這個(gè)信號(hào)量。
關(guān)閉/銷毀信號(hào)量
在 Windows 中,我們使用 CloseHandle() 來(lái)關(guān)閉或銷毀信號(hào)量對(duì)象。
BOOL CloseHandle(
HANDLE hObject
);
hObject 是指向這個(gè)同步對(duì)象句柄的指針。
在 Linux 中,sem_destroy() 負(fù)責(zé)銷毀信號(hào)量對(duì)象,并釋放它所持有的資源: int sem_destroy(sem_t *sem)。對(duì)于 System V 信號(hào)量來(lái)說(shuō),只能使用 semctl() 函數(shù)的 IPC_RMID 命令來(lái)關(guān)閉信號(hào)量集:int semctl(int semid, int semnum, int cmd = IPC_RMID, ...)。
這個(gè)命令將立即刪除信號(hào)量集及其數(shù)據(jù)結(jié)構(gòu),并喚醒所有正在等待的進(jìn)程(如果發(fā)生錯(cuò)誤,則返回,并將 errno 設(shè)置為 EIDRM)。調(diào)用進(jìn)程的有效用戶 ID 必須是超級(jí)用戶,或者可以與該信號(hào)量集的創(chuàng)建者或所有者匹配的用戶。參數(shù) semnum 會(huì)被忽略
【編輯推薦】


























