Linux進程間通信三 System V 信號量簡介與示例

1. System V信號量簡介

SystemV信號量主要用於解決生產者和消費者問題,一個信號量能夠控制多個資源,說它是信號量集也不爲過。

2. API接口介紹

2.1 創建或打開信號量集

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>


/**
* @brief 創建信號量或者返回已存在的信號量
*
* @params key 與信號量關聯的key,三種生成方式,包括IPC_PRIVATE,注意key和信號量不是綁定關係,相同的key產生的標識符不一定相同,比如系統重啓的情況
* @params nsems 集合中信號量的個數
* @params shmflg 標誌位和權限控制標誌位,可以多個用or運算。IPC_CREAT、 IPC_EXCL
*
* @returns 成功返回信號量集標識符,失敗返回-1
*/

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

2.2 操作信號量集

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

/**
* @brief 操作信號量集
*
* @params semid 信號量集標識符
* @params sops 具體的操作行爲,指向一個數組,每個元素都是sembuf結構,該結構指定了要操作哪個信號量以及相應的值等。
*
* @params nsops 表名sembuf的數量
*
* @returns 成功返回0,失敗返回-1,注意這裏的操作是原子性的,如果操作多個信號量,要麼全部成功,要麼什麼都不做
*/

int semop(int semid, struct sembuf *sops, size_t nsops);

struct sembuf {
    unsigned short int sem_num;    // 指定操作信號量的下標,從0開始
    short sem_op;     // 信號量的值
    short sem_flg;    // 操作標誌位,可選 IPC_NOWAIT and SEM_UNDO,前者表示非阻塞,後者標誌當進程退出後,系統自動恢復對這個信號量的操作。
};


2.3 控制操作

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

/**
* @brief 操作信號量集
*
* @params semid 信號量集標識符
* @params semnum 要操作的信號量序號,下標從0開始
* @params cmd 控制行爲,可選值IPC_STAT,IPC_RMID, IPC_SET, IPC_INFO
* @params 可選參數,這裏的參數結構體需要用戶自定義。
* @returns 不同的cmd返回不通值
*/


int semctl(int semid, int semnum, int cmd, ...);

// 可選參數,需要用戶自定義
union semun {
    int              val;    /* Value for SETVAL */
    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  /* Array for GETALL, SETALL */
    struct seminfo  *__buf;  /* Buffer for IPC_INFO (Linux-specific) */
};

3. 示例

下面是信號量集的一個簡單示例,分爲生產者和消費者,其中生產者生產資源,消費者消費資源

生產者:

/*
**  Name: sem_wait.c
**  Desc: 生產者
**  Author: masonf
**  Date: 20200626
*/

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

// 自定義結構體
union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo *__buf;
};

int main()
{
    int ret = 0;
    key_t key = 10240;
    int sem_id;

    // 創建或獲取信號量集,其中包含一個信號量,這裏key是固定的
    sem_id = semget(key, 1, IPC_CREAT);
    if (sem_id == -1)
    {
        perror("create sem error");
        return -1;
    }


    // 設置第0個信號量,資源數加1
    struct sembuf sem_operation[1]={0};
    sem_operation[0].sem_op=1;
    sem_operation[0].sem_num = 0;

    // 初始化信號量的值爲1
    if (semop(sem_id, sem_operation, 1) == -1)
    {
        perror("init sem error");
        // 初始化失敗則刪除信號量集
        semctl(sem_id, 1, IPC_RMID);
        return -1;
    }

    int food_nums = 1;
    while (food_nums < 5)
    {
        printf("start producing foods\n");

        // 生產食物
        sem_operation[0].sem_num=0; // 操作第一個信號量,下標從0開始
        sem_operation[0].sem_op = 1;
        while (semop(sem_id, sem_operation, 1) == -1)
        {
            printf("producing error, continue\n");
            continue;
        }
        ++food_nums;
    }

    // 刪除信號量集
    //semctl(sem_id, 1, IPC_RMID);
    printf("work done, Go home!\n");
    return 0;    
}

消費者

/*
**  Name: sen_wait.c
**  Desc: 消費者    
**  Author: mason
**  Date: 20200626
*/

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

// 自定義結構體
union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo *__buf;
};

int main()
{
    int ret = 0;
    key_t key = 10240;
    int sem_id;

    // 創建或獲取信號量集
    sem_id = semget(key, 1, IPC_CREAT);
    if (sem_id == -1)
    {
        perror("create sem error");
        return -1;
    }

    //操作信號量,默認是阻塞
    struct sembuf sem_operation[1]={0};
    sem_operation[0].sem_num=0;
    sem_operation[0].sem_op=-1;         // 申請一個資源
    //sem_operation[0].sem_flg=SEM_UNDO;
    
    // 消費,每次消費一個
    while (semop(sem_id, sem_operation, 1) != -1)
    {
        printf("consuming foods\n");
    }
    return 0;    
}

 編譯運行

4. 注意事項

4.1 信號量的創建和初始化操作是分開的,這點需要注意,好在有一種解決方法,信號量集相關的結構體中有一個sem_otime字段,初始化爲0,可以通過檢查sem_otime是否爲0來判斷是否已經經過初始化

 

2. 資源限制

SEMMSL 系統範圍內信號量集的最大數量,可通過/proc/sys/kernel/sem查看
SEMMSL 單個信號量集中信號量的最大個數
SEMMNS 系統範圍內信號量的最大數量

 

5.參考文檔

1. https://man7.org/linux/man-pages/man2/semop.2.html

2. 《Linux環境編程 從應用到內核》

3. 《Unix網絡編程 卷二 進程間通信》

================================================================================================

Linux應用程序、內核、驅動、後臺開發交流討論羣(745510310),感興趣的同學可以加羣討論、交流、資料查找等,前進的道路上,你不是一個人奧^_^。...
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章