Linux作業系統——簡單程式同步

晨晨mak發表於2023-12-05


一、訊號量相關函式

1. 建立訊號量集

本次實驗使用訊號量機制來實現程式的同步與互斥,因此首先需要建立訊號量集。


使用函式semget可以建立訊號量集,它的原型如下:


int semget(key_t key, int nsems, int semflg)

其中,key是建立訊號量集的鍵,nsems是訊號量集中訊號量量的數量;


semflg是指定的選項及其許可權位,包含IPC_CREAT(建立新的訊號量集)、IPC_EXCEL(如果訊號量集已經存在,則返回錯誤)等。


這個函式建立的是一個訊號量集,其中包含多個訊號量,可以透過函式semop來訪問訊號量集中的某個訊號量,或者使用semctl函式來對訊號量進行操作,一般建立資料集後都要首先使用semctl對每個訊號量設定初始值。semop和semctl函式的介紹在下面。


2. 獲取訊號量集

一個程式建立號訊號量集後,另一個程式想要訪問這個訊號量集,可以使用semget函式傳入KEY值來獲取該訊號量集的id,該函式原型如下:


int semid = semget(KEY, 0, 0);

上述程式碼只為獲取一個已經存在的訊號量集,不需要nsems和semflg,全部為0即可。獲取成功會返回訊號量集id,如果獲取失敗的話會返回-1 。


3. 等待、通知訊號量集

使用semop函式可以訪問一個訊號量集,進行獲取(P操作)和釋放(V操作),其原型如下:


int semop(int semid,struct sembuf *sops,unsigned nsops)

semid是使用semget函式獲取到的訊號量集的id;


sops是一個sembuf型別的結構,用於描述訊號量的操作:等待、通知等,其定義如下:


struct  sembuf{

       short sem_num;    // 要訪問的訊號量在訊號量集中的索引

       short sem_op;    // 對訊號量的操作,為負數是P操作,正數是V操作

       short sem_flg;   // 操作標誌,可以是0或IPC_NOWAIT(非阻塞方式)

}

nsops是指定訊號量集中操作的訊號量個數。


4. 控制訊號量集

要對整個訊號量集進行操作可以使用semctl函式,其原型如下:


int semctl(int semid, int semnum, int cmd, union semun arg)

semid是由semget函式返回的訊號量集id


semnum是訊號量在訊號量集中的索引


cmd是控制命令,用於對訊號量執行指定的操作,命令包括:


IPC_STAT:獲取訊號量集合的屬性資訊,將結果寫入指定的結構體中。


IPC_SET:設定訊號量集合的屬性資訊,使用指定的結構體中的值進行設定。


GETVAL:獲取指定訊號量的值。


SETVAL:設定指定訊號量的值。


GETALL: 獲取訊號量集合中所有訊號量的值


SETALL: 設定所有訊號量的值


GETPID:獲取最後一個執行 semop() 操作的程式 ID。


GETNCNT:獲取等待該訊號量值增加的程式數。


GETZCNT:獲取等待該訊號量值變為 0 的程式數。


IPC_RMID:刪除訊號量集合。


二、簡單程式同步

現在使用上面給出的函式編寫幾個程式實現程式同步。


訊號量機制實現程式同步需要使用一些簡單的訊號量集操作,包括建立訊號量集,P操作,V操作,刪除訊號量集。


1. 建立訊號量集

createSem.cpp


#include<iostream>

#include<sys/sem.h>

#include<sys/types.h>

#include<sys/ipc.h>

using namespace std;

#define KEY 2002

int main(){

int semid = semget(KEY, 1, IPC_CREAT);

semctl(semid, 0, SETVAL, 0);

 

}

上面程式碼使用semid函式建立一個只包含一個訊號量的訊號量集,隨後使用semctl將該訊號量的值初始化為0 。


2. P操作

p1.cpp


#include<iostream>

#include<sys/sem.h>

#include<sys/types.h>

#include<sys/ipc.h>

#define KEY 2002

using namespace std;

int main(){

int semid = semget(KEY, 0, 0);

struct sembuf *sops = new sembuf;

sops->sem_num = 0;

sops->sem_op = -1;

sops->sem_flg = 0;

if(semop(semid, sops, 1) == -1){

char err[] = "semop";

perror(err);

exit(1);

}

cout << "獲取成功" << endl;

return 0;

}


上一個程式createSem建立完訊號量集後,訊號量集就儲存在緩衝區中,此時另一個程式可以使用semget函式訪問該訊號量集,使用同樣的鍵值KEY就能訪問到上一個程式建立的訊號量集。


上述程式碼首先獲取已經建立好的訊號量集,隨後定義sembuf型別結構指標sops,設定訪問訊號量索引sem_num為0,訊號量操作sem_op為-1(p操作,訊號量減一),操作標誌為0 。接著就使用semop函式傳入sops對訊號量進行操作,如果操作失敗(如訊號量集不存在或訊號量索引非法等)就輸出錯誤資訊,結束程式。如果獲取成功則會執行下面的語句列印“獲取成功”,如果進行P操作時該訊號量為0,此時進行會阻塞,等待其他程式釋放訊號,為了簡化問題,一開始設定訊號量為0,此時執行p1程式一定會阻塞。


3. V操作

p2.cpp


#include<iostream>

#include<sys/sem.h>

#include<sys/types.h>

#include<sys/ipc.h>

#define KEY 2002

using namespace std;

int main(){

int semid = semget(KEY, 0, 0);

struct sembuf *sops = new sembuf;

sops->sem_num = 0;

sops->sem_op = 1;

sops->sem_flg = 0;

if(semop(semid, sops, 1) == -1){

char err[] = "semop";

perror(err);

exit(1);

}

return 0;

}


上面程式碼與p1基本時一樣的,只是修改了sem_op,改為1(V操作,訊號量加1),執行該程式後,對應訊號量會加一,原本阻塞的程式就能結束等待,繼續執行下去。


4. 刪除訊號量集

deletSem.cpp


訊號量集使用完畢後需要刪除訊號量集,使用semctl函式實現,控制命令選擇IPC_RMID。


#include<iostream>

#include<sys/sem.h>

#include<sys/types.h>

#include<sys/ipc.h>

using namespace std;

#define KEY 2002

int main(){

int semid = semget(KEY, 0, 0);

semctl(semid, 0, IPC_RMID, 0);



來自 “ ITPUB部落格 ” ,連結:https://blog.itpub.net/70001647/viewspace-2998725/,如需轉載,請註明出處,否則將追究法律責任。

相關文章