Linux系統信號量控制

Linux系統信號量控制

實現代碼

#include <bits/stdc++.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/wait.h>
using namespace std;

const int TIMES = 5;
const int DELAY = 5;

union Semaphore {
    int value;
    semid_ds *buffer;
    unsigned short *array;
    seminfo *infomation;
};

static int semaphoreID = 0;

static bool initializeSemaphoreValue() {
    Semaphore semaphoreUnion;

    semaphoreUnion.value = 1;

    return semctl(semaphoreID, 0, SETVAL, semaphoreUnion) == -1 ? false : true;
}

static bool semaphoreP() {
    sembuf semaphoreBuffer;

    semaphoreBuffer.sem_num = 0;
    semaphoreBuffer.sem_op = -1;
    semaphoreBuffer.sem_flg = SEM_UNDO;

    return semop(semaphoreID, &semaphoreBuffer, 1) == -1 ? false : true;
}

static bool semaphoreV() {
    sembuf semaphoreBuffer;

    semaphoreBuffer.sem_num = 0;
    semaphoreBuffer.sem_op = 1;
    semaphoreBuffer.sem_flg = SEM_UNDO;

    return semop(semaphoreID, &semaphoreBuffer, 1) == -1 ? false : true;
}

static void deleteSemaphoreSet() {
    Semaphore semaphoreUnion;

    if (semctl(semaphoreID, 0, IPC_RMID, semaphoreUnion) == -1) {
        fprintf(stderr, "Failed to delete semaphore\n");
    }
}

int main(int argc, char const *argv[])
{
    if ((semaphoreID = semget(IPC_PRIVATE, 1, 0666 | IPC_CREAT)) == -1) {
        fprintf(stderr, "Create semaphore set failed!\n");
        abort();
    }

    if (!initializeSemaphoreValue()) {
        fprintf(stderr, "Initialize semaphore failed!\n");
        abort();
    }

    pid_t pid = fork();
    string message;
    switch (pid) {
        case -1:
            perror("Fork fail!\n");
            abort();
        case 0:
            message = "Child";
            break;
        default:
            message = "Parent";
            break;
    }

    srand((unsigned)getpid());

    for (int i = 0; i < TIMES; i++) {
        if (!semaphoreP()) {
            printf("Semaphore P failed!\n");
            abort();
        }
        cout << message <<endl;
        fflush(stdout);
        sleep(rand() % DELAY);
        if (!semaphoreV()) {
            printf("Semaphore V failed!\n");
            abort();
        }
    }

    if (pid > 0) {
        wait(NULL);
        deleteSemaphoreSet();
    }

    printf("PID %d finished!\n", getpid());

    return 0;
}

相關函數

semctl()

  • 函數原型
    int semctl(int semid,int semnum,int cmd, /*union semun arg*/);
  • 參數

系統調用semctl()的第一個參數是信號量集IPC標識符。第二個參數是操作信號在信號集中的編號,第一個信號的編號是0
根據參數cmd選擇功能
參數arg代表一個semun的實例。semun是在linux/sem.h中定義的:
/arg for semctl systemcalls./
union semun{
int val;/value for SETVAL/
struct semid_ds *buf;/buffer for IPC_STAT&IPC_SET/
ushort *array;/array for GETALL&SETALL/
struct seminfo *__buf;/buffer for IPC_INFO/
void *__pad;
val當執行SETVAL命令時使用。buf在IPC_STAT/IPC_SET命令中使用。代表了內核中使用的信號量的數據結構。array在使用GETALL/SETALL命令時使用的指針。

  • 返回值

若成功,則爲一個正數,否則爲-1

  • 功能

參數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設置信號量集中的一個單獨的信號量的值。

semop()

  • 函數原型
    int semopi(nt semid,struct sembuf *sops,size_t nsops);
  • 參數

semid:信號集的識別碼,可通過semget獲取。
sops:指向存儲信號操作結構的數組指針,信號操作結構的原型如下
struct sembuf
{
unsigned short sem_num; /* semaphore number /
short sem_op; /
semaphore operation /
short sem_flg; /
operation flags */
};
這三個字段的意義分別爲:
sem_num:操作信號在信號集中的編號,第一個信號的編號是0。
sem_op:如果其值爲正數,該值會加到現有的信號內含值中。通常用於釋放所控資源的使用權;如果sem_op的值爲負數,而其絕對值又大於信號的現值,操作將會阻塞,直到信號值大於或等於sem_op的絕對值。通常用於獲取資源的使用權;如果sem_op的值爲0,如果沒有設置IPC_NOWAIT,則調用該操作的進程或者線程將暫時睡眠,直到信號量的值爲0;否則,進程或者線程不會睡眠,函數返回錯誤EAGAIN。
sem_flg:信號操作標誌,可能的選擇有兩種
IPC_NOWAIT //對信號的操作不能滿足時,semop()不會阻塞,並立即返回,同時設定錯誤信息。
SEM_UNDO //程序結束時(不論正常或不正常),保證信號值會被重設爲semop()調用前的值。這樣做的目的在於避免程序在異常情況下結束時未將鎖定的資源解鎖,造成該資源永遠鎖定。
nsops:信號操作結構的數量,恆大於或等於1。
timeout:當semtimedop()調用致使進程進入睡眠時,睡眠時間不能超過本參數指定的值。如果睡眠超時,semtimedop()將失敗返回,並設定錯誤值爲EAGAIN。如果本參數的值爲NULL,semtimedop()將永遠睡眠等待。

  • 功能

操作一個或一組信號

semget()

  • 函數原型
    int semget(key_t key,int nsems,int semflg);
  • 參數

key:所創建或打開信號量集的鍵值。
nsems:創建的信號量集中的信號量的個數,該參數只在創建信號量集時有效。
semflg:調用函數的操作類型,也可用於設置信號量集的訪問權限,兩者通過or表示

  • 返回值

若成功,則返回信號量集的IPC標識符,否則返回-1

  • 功能

建立信號量集

輸出結果

Parent
Child
Parent
Child
Parent
Child
Parent
Child
Parent
Child
PID 15250 finished!
PID 15249 finished!

注意

若註釋掉main函數中的semaphoreV()函數和semaphoreP()函數,則會不能保證輸出結果的正確,由於父進程和子進程相互競爭輸出結果,導致輸出序列的混亂,信號量機制保證父進程和子進程交替進行,得到想要的結果

Parent
Child
Parent
Parent
Parent
Parent
Child
Child
Child
Child
PID 19265 finished!
PID 19264 finished!

鳴謝

百度百科

最後

  • 由於博主水平有限,不免有疏漏之處,歡迎讀者隨時批評指正,以免造成不必要的誤解!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章