1. 程式人生 > >System V訊號量

System V訊號量

目錄

  • 1. System V IPC
    • 概述
    • IPC鍵和ftok函式
    • ipc_perm結構
    • 建立與開啟IPC物件
    • ipcs和ipcrm命令
  • 2. System V訊號量
    • 計數訊號量集
    • semget
    • semop
    • semctl
  • 3. 測試程式
    • 程式碼實現
      • semcreate.c
      • semrmid.c
      • semsetvalues.c
      • semgetvalues.c
      • semops.c
    • 執行測試

1. System V IPC

概述

以下三種類型的IPC合稱為System V IPC:

  • System V訊號量
  • System V訊息佇列
  • System V共享記憶體

System V IPC在訪問它們的函式和核心為它們維護的資訊上有一些類似點,主要包括:

  • IPC鍵和ftok函式
  • ipc_perm結構
  • 建立或開啟時指定的使用者訪問許可權
  • ipcs和ipcrm命令

下表彙總了所有System V IPC函式。

  訊號量 訊息佇列 共享記憶體
標頭檔案 sys/sem.h sys/msg.h sys/shm.h
建立或開啟IPC的函式 semget msgget shmget
控制IPC操作的函式 semctl msgctl shmctl
IPC操作函式 semop msgsnd
msgrcv
shmat
shmdt

IPC鍵和ftok函式

三種類型的System V IPC都使用IPC鍵作為它們的標識,IPC鍵是一個key_t型別的整數,該型別在sys/types.h中定義。
IPC鍵通常是由ftok函式賦予的,該函式把一個已存在的路徑名pathname和一個非0整數id組合轉換成一個key_t值,即IPC鍵。

#include <sys/ipc.h>

//成功返回IPC鍵,失敗返回-1
key_t ftok(const char *pathname, int id);

引數說明:

  • pathname在是程式執行期間必須穩定存在,不能反覆建立與刪除
  • id不能為0,可以是正數或者負數

ipc_perm結構

核心給每個IPC物件維護一個資訊結構,即struct ipc_perm結構,該結構及System V IPC函式經常使用的常值定義在sys/ipc.h標頭檔案中。

struct ipc_perm
{
    uid_t   uid;   //owner's user id
    gid_t   gid;   //owner's group id
    uid_t   cuid;  //creator's group id
    gid_t   cgid;  //creator's group id
    mode_t  mode;  //read-write permissions
    ulong_t seq;   //slot usage sequence number
    key_t   key;   //IPC key
};

建立與開啟IPC物件

建立或開啟一個IPC物件使用相應的xxxget函式,它們都有兩個共同的引數:

  • 引數key,key_t型別的IPC鍵
  • 引數oflag,用於指定IPC物件的讀寫許可權(ipc_perm.mode),並選擇是建立一個新的IPC物件還是開啟一個已存在的IPC物件

對於引數key,應用程式有兩種選擇:

  • 呼叫ftok,給它傳pathname和id
  • 指定key為IPC_PRIVATE,這將保證會建立一個新的、唯一的IPC物件,但該標誌不能用於開啟已存在的IPC物件,只能是新建

對於引數oflag,如上所述,它包含讀寫許可權、建立或開啟這兩方面資訊:

  • 可以指定IPC_CREAT標誌,其含義和Posix IPC的O_CREAT一樣
  • 還可以設定為下表所示的常值來指定讀寫許可權

ipcs和ipcrm命令

  • 由於System V IPC的三種類型不是以檔案系統路徑名標識的,因此無法使用ls和rm命令檢視與刪除它們
  • ipcs和ipcrm分別用於檢視與刪除系統中的System V IPC
usage : ipcs -asmq -tclup 
    ipcs [-s -m -q] -i id
    ipcs -h for help.
usage: ipcrm [ [-q msqid] [-m shmid] [-s semid]
          [-Q msgkey] [-M shmkey] [-S semkey] ... ]

2. System V訊號量

計數訊號量集

我們已經知道了Posix訊號量採用計數訊號量,System V訊號量在此基礎上增加了一級複雜度,它採用計數訊號量集,計數訊號量集是由一個或多個計數訊號量構成的集合。
對於系統中的每個訊號量集,核心都維護一個struct semid_ds資訊結構,它定義在sys/sem.h標頭檔案中。

struct semid_ds
{
    struct ipc_perm  sem_perm;
    struct sem       *sem_base;  //指向訊號量集的指標
    ushort           sem_nsems;  //訊號量集中的訊號量個數
    time_t           sem_otime;  //上一次呼叫semop的時間
    time_t           sem_ctime;  //建立時間或上一次以IPC_SET呼叫semctl的時間
};

其中,sem_base是指向訊號量集的指標,訊號量集中的每個成員都對應一個struct sem結構:

struct sem
{
    ushort_t  semval;  //訊號量的值
    short     sempid;  //上一次成功呼叫semop,或以SETVAL、SETALL呼叫semctl的程序ID
    ushort_t  semncnt; //等待semval變為大於當前值的執行緒數
    ushort_t  semzcnt; //等待semval變為0的執行緒數
};

semget

semget用於建立一個新的訊號量集或開啟一個已存在的訊號量集。

  • nsems引數指定集合中的訊號量個數,如果是開啟一個已存在的訊號量集,就把該引數設為0
  • oflag引數可設定為IPC_CREAT,以及SEM_R和SEM_A指定的訪問許可權,如果是開啟一個已存在的訊號量集,就把該引數設為0
  • 成功時返回一個稱為訊號量識別符號的非負整數,semop和semctrl函式將使用它
//成功返回訊號量識別符號,失敗返回-1
int semget(key_t key, int nsems, int oflag);

當實際操作為建立一個新的訊號量集時,相應semid_ds結構中與集合中每個訊號量關聯的struct sem結構並不初始化,而是在以SETVAL或SETALL命令呼叫semctrl時初始化的。
也就是說,建立一個新的System V訊號量集(semget)並將它初始化(semctl)需要兩次函式呼叫,這是System V訊號量的一個致命缺陷。

semop

使用semget開啟一個訊號量集後,對其中一個或多個訊號量值的操作就使用semop函式。

//成功返回0,失敗返回-1
int semop(int semid, struct sembuf *ops, size_t nops);
  • semid指定待操作的訊號量集
  • nops為集合中的訊號量個數
  • ops指向一個struct sembuf型別的結構陣列,該陣列中的每個元素給目標訊號量集中某個特定的訊號量指定sem_op操作,這個特定的訊號量由sem_num指定

我們只保證sembuf含有以下三個成員,不保證這三個成員的順序,也不保證還有其他成員,因此sembuf陣列元素必須採用如ops[0].sem_num = 0所示的方法進行初始化。

struct sembuf
{
    unsigned short sem_num;  /* semaphore number */
    short          sem_op;   /* semaphore operation */
    short          sem_flg;  /* operation flags */
};

sem_op指定的操作有三類:

  • sem_op > 0:操作內容為semval += sem_op,這對應於釋放某個訊號量控制的資源
  • sem_op = 0:呼叫者阻塞等待直到semval變為0
  • sem_op < 0:呼叫者阻塞等待直到semval >= abs(sem_op),呼叫者阻塞等待直到semval>這對應於分配資源

sem_flg可設定的值有:0、IPC_NOWAIT、SEM_UNDO,一般使用0,對於其餘兩個值:

  • IPC_NOWAIT用於給訊號量集中某個特定訊號量設定非阻塞標誌
  • SEM_UNDO為System V訊號量特有的復舊機制

semctl

semctl函式對一個訊號量集執行各種控制操作。

//成功根據cmd返回相應的非負值,失敗返回-1
int semctl(int semid, int semnum, int cmd, ... /* union semun arg */);
  • semid指定待控制的訊號量集
  • semnum指定訊號量集內的某個成員,僅在cmd為GETVAL、SETVAL、GETNCNT、GETZCNT和GETPID時使用
  • cmd指定控制命令
  • arg是可選的,取決於cmd的具體值

第四個引數arg是可選的,取決於cmd的值,當需要用到該引數時,應用程式必須按如下結構定義union semun:

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) */
};

定義如下術語用於後續cmd說明:

  • semval:訊號量的當前值
  • sempid:上一次成功呼叫semop,或以SETVAL、SETALL呼叫semctl的程序ID
  • semncnt:等待semval變為大於當前值的執行緒數
  • semzcnt:等待semval變為0的執行緒數

System V支援下列cmd值,除非特殊說明,否則成功時返回值均為0。

cmd 說 明
GETVAL 把semval的當前值作為函式返回值返回,它是一個unsigned short型別的整數
SETVAL 把semval的值設為arg.val
GETPID 把sempid的當前值作為函式返回值返回
GETNCNT 把semncnt的當前值作為函式返回值返回
GETZCNT 把semzcnt的當前值作為函式返回值返回
GETALL 返回訊號量集內每個成員的semval值,這些值通過arg.array返回,arg.array需由呼叫者分配記憶體
SETALL 設定訊號量集內每個成員的semval值,這些值通過arg.array指定,此時第二個引數semnum設為0即可
IPC_STAT 返回訊號量集當前的semid_ds結構,該結構通過arg.buf返回,arg.buf需由呼叫者分配記憶體,可以用該命令獲得訊號量集中的訊號量個數
IPC_SET 設定訊號量集對應semid_ds結構中的sem_perm.uid、sem_perm.gid和sem_perm.mode,設定的值來自arg.buf
IPC_RMID 刪除由semid指定的訊號量集,此時第二個引數semnum設為0即可

其中,前五個命令針對的都是訊號量集semid中由semnum指定的訊號量。

3. 測試程式

程式碼實現

semcreate.c

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

#define FTOK_FILE       "/home/delphi/ftok.file"
#define FTOK_ID         1
#define SEM_MODE        0666

/*
#define SEM_MODE_OWNER  SEM_R | SEM_A
#define SEM_MODE_GROUP  (SEM_R >> 3) | (SEM_A >> 3)
#define SEM_MODE_OTHER  (SEM_R >> 6) | (SEM_A >> 6)
#define SEM_MODE        (SEM_MODE_OWNER | SEM_MODE_GROUP | SEM_MODE_OTHER)
*/

int main()
{
    int nsems = 3;
    int oflag = IPC_CREAT | SEM_MODE;
    key_t key = ftok(FTOK_FILE, FTOK_ID);
    int semid = semget(key, nsems, oflag);

    if (semid >= 0)
    {
        printf("semcreate success, semid = %d\n", semid);
    }

    return 0;
}

這裡遇到個問題,SEM_MODE一開始是按註釋部分定義的,但man semget給出的三個標頭檔案都已經包含了,編譯時卻報錯,提示SEM_R和SEM_A未定義,不知道為什麼,只能用0666定義了。

semrmid.c

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

#define FTOK_FILE       "/home/delphi/ftok.file"
#define FTOK_ID         1

int main()
{
    key_t key = ftok(FTOK_FILE, FTOK_ID);
    int semid = semget(key, 0, 0);
    semctl(semid, 0, IPC_RMID);

    return 0;
}

semsetvalues.c

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

#define FTOK_FILE       "/home/delphi/ftok.file"
#define FTOK_ID         1

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) */
};

int main(int argc, char **argv)
{
    key_t key;
    int semid;
    int nsems;
    unsigned short *semvals;
    union semun arg;
    struct semid_ds seminfo;
    int i;

    /* 開啟semcreate建立的訊號量集 */
    key = ftok(FTOK_FILE, FTOK_ID);
    semid = semget(key, 0, 0);

    /* 獲得訊號量集中的訊號量個數 */
    arg.buf = &seminfo;
    semctl(semid, 0, IPC_STAT, arg);
    nsems = arg.buf->sem_nsems;

    /* 設定訊號量集中每個訊號量的值 */
    semvals = (unsigned short *)calloc(nsems, sizeof(unsigned short));

    for (i = 0; i < nsems; i++)
    {
        semvals[i] = atoi(argv[i + 1]); //通過命令列引數分別指定集合中每個訊號量的值
    }

    arg.array = semvals;
    semctl(semid, 0, SETALL, arg);

    return 0;
}

semgetvalues.c

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

#define FTOK_FILE       "/home/delphi/ftok.file"
#define FTOK_ID         1

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) */
};

int main(int argc, char **argv)
{
    key_t key;
    int semid;
    int nsems;
    unsigned short *semvals;
    union semun arg;
    struct semid_ds seminfo;
    int i;

    /* 開啟semcreate建立的訊號量集 */
    key = ftok(FTOK_FILE, FTOK_ID);
    semid = semget(key, 0, 0);

    /* 獲得訊號量集中的訊號量個數 */
    arg.buf = &seminfo;
    semctl(semid, 0, IPC_STAT, arg);
    nsems = arg.buf->sem_nsems;

    /* 獲得訊號量集中每個訊號量的值 */
    semvals = (unsigned short *)calloc(nsems, sizeof(unsigned short));
    arg.array = semvals;
    semctl(semid, 0, GETALL, arg);

    for (i = 0; i < nsems; i++)
    {
        printf("semvals[%d] = %d\n", i, semvals[i]);
    }

    return 0;
}

semops.c

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

#define FTOK_FILE       "/home/delphi/ftok.file"
#define FTOK_ID         1

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) */
};

int main(int argc, char **argv)
{
    key_t key;
    int semid;
    int nsems;
    union semun arg;
    struct semid_ds seminfo;
    struct sembuf *semops;
    int i;

    /* 開啟semcreate建立的訊號量集 */
    key = ftok(FTOK_FILE, FTOK_ID);
    semid = semget(key, 0, 0);

    /* 獲得訊號量集中的訊號量個數 */
    arg.buf = &seminfo;
    semctl(semid, 0, IPC_STAT, arg);
    nsems = arg.buf->sem_nsems;

    /* 對訊號量集中的所有訊號量進行相同的semop操作 */
    semops = (struct sembuf *)calloc(nsems, sizeof(struct sembuf));

    for (i = 0; i < nsems; i++)
    {
        semops[i].sem_num = i;
        semops[i].sem_op = atoi(argv[1]); //操作型別由命令列引數指定,>0, 0, <0
        semops[i].sem_flg = 0;
    }

    semop(semid, semops, nsems);

    return 0;
}

執行測試

  • 執行semcreate,通過執行前後的ipcs -s,可以確認訊號量集建立成功

  • 執行semsetvalues,將三個訊號量的值分別設為1、2、3
  • 然後執行semgetvalues,列印資訊和我們設定的值一致,符合預期

  • 執行semops,並指定sem_op > 0
  • 然後執行semgetvalues,列印資訊顯示每個訊號量的值都增加了1,符合預期

  • 執行semrmid,通過執行前後的ipcs -s,可以確認訊號量集已經從系統刪除

相關推薦

Linux程序間通訊(IPC)程式設計實踐(十)System V訊號---PV操作經典題目

//P原語     //P(semaphore *S)     wait(semaphore *S)       {           -- S->value;           if (S->value < 0)           {   

System V訊號-semget()、semop()和semctl()

簡單介紹 這是我開這個欄目的第一篇文章,順序也不是按照《Unix網路程式設計》(以下簡稱網編)的章節順序往下寫的,可能文章會提及一些前面章節的概念,遇到的話,我會做一些引導,讀者也可以自己找找相關的內容或書籍檢視。 訊號量是一種用於提供不同程序間或一個給定程

System V 訊號(三)之用訊號解決哲學家進餐問題

一.哲學家就餐問題           5個哲學家,5個筷子。5個哲學家圍坐在一張桌子上,筷子放在分別放在每個哲學家的兩旁。如果所有哲學家在某個時刻同時拿起左邊的筷子,那麼右邊的筷子就都被其他的哲學家拿了,造成大家都無法吃飯。但是大家都不想放下左邊的筷子(規則是先拿起左邊

System V 訊號

 訊號量概念: 1.二值訊號量:值為0或者1。若互斥鎖就是這種 2.計數訊號量:值為0~N之間的訊號量 3.計數訊號量集:就是一個或多個訊號量構成一個集合 System V訊號量就是計數訊號量集,而Posix訊號量則是單個計數訊號量 ==================

System V訊號

目錄 1. System V IPC 概述 IPC鍵和ftok函式 ipc_perm結構 建立與開啟IPC物件 ipcs和ipcrm命令 2.

Linux中的System V訊號

> 在程序同步,併發執行時,保證按序地訪問共享資源是十分重要的。因此引入了臨界區的概念,一次只能有一個執行緒進入臨界區完成他的指令。而訊號量(semaphore)的作用,類似於一個交通訊號燈,它負責程序協作,因此訊號量又稱為訊號燈。 在Linux系統中,它提供兩種訊號量: - **核心訊號量**,由核心控

Linux程式設計學習筆記----System V程序間通訊(訊號)

關於System V Unix System V,是Unix作業系統眾多版本中的一支。它最初由AT&T開發,在1983年第一次釋出,因此也被稱為AT&T System V。一共發行了4個System V的主要版本:版本1、2、3和4。System V Rel

【C語言】【unix c】訊號集(system v ipc)

二、訊號量集(system v ipc) 訊號量集就是陣列,數組裡的每個元素都是訊號量的型別 1、獲取鍵值 ftok(3) 2、使用鍵值獲取訊號量集的id semget(2) #in

程序間通訊之——訊號(一)(system V

system v訊號量又被稱為system v訊號量集。它的本質就是一個計數器,用來為多個程序共享的資料結構提供受控訪問。訊號量支援的操作有:使用最廣泛的訊號量為二元訊號量,它控制單個資源,對於這種訊號量而言,它只有兩種合法值: 0 和 1 ,對應一個可用的資源。若當前有資源

System V 信號使用相關函數

mode 可選參數 訪問 訪問權限 給定 素數 per 分配 絕對值 System V 信號量 在提到Posix 信號量時,指的是二值信號量或計數信號量,而System V信號量指的是入了計數信號量集 二值信號量:其值為0或1,類似於互斥鎖,資源被鎖住時為0,資源可用為1計

【作業系統】訊號與P、V操作

知識點: 訊號量機制主要有整形訊號量、記錄性訊號量、訊號量集機制。 訊號量是一個整形變數,根據控制物件的不同賦不同的值。訊號量可分為公用訊號量和私用訊號量兩類。 公用訊號量:實現程序間的互斥,初值=1或資源的數目 私用訊號量:實現程序間的同步,初值=0或某個整數 訊

臨界資源、臨界區、訊號、P,V操作

一、資源:Linux上有硬體資源和軟體資源之分。程式會受到資源限制的影響,可能在這幾方面的資源限制受到影響:1.硬體方面的物理性限制(記憶體);2.系統策略的限制(允許使用的CPU時間);3.具體實現的限制(整數的長度、檔名中所允許的最大字元數)。二、臨界資源:臨界資源是一

訊號P,V操作

訊號量是最早出現的用來解決程序同步與互斥問題的機制(也可實現程序通訊),包括一個稱為信 號量的變數及對它進行的兩個原語操作。訊號量為一個整數,我們設這個訊號量為:sem。很顯然,我們規定在sem大於等於零的時候代表可供併發程序使用的 資源實體數,sem小於零的時候,表示正在等待使用臨界區的程序的個數。根據這個

IPC- Posix與system v

同步 linu 一個 tin 減少 ces 鏈接 emctl dia 一、功能上的區別 posix和system v有什麽區別/?現在在應用時應用那一標準浮雲484212 | 瀏覽 243 次 2014-11-06 10:362014-11-19 22:36 最佳答案

system v 共享內存區

include system #include<sys/shm.h> int shmget(key_t key,size_t size,int oflag); 返回:成功則為共享內存區對象,出錯為-1 key 的值可以是ftok的返回值,也可以是IPC_PRIVA

system v 共享內存

print usr ftok 新的 byte ipc 共享 err turn #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h

pause、jobs、setitimer(2)、system v ipc(day12)

ssa 周期 types.h ros system signed job 功能 chl 一、pause(2)的使用 #include <unistd.h> int pause(void); 功能:等待信號的到來 返回值: -1 錯誤 errno被設置 只

System V IPC 之共享內存

應用 創建者 CP 機構 ons 最大 基本概念 返回 time_t IPC 是進程間通信(Interprocess Communication)的縮寫,通常指允許用戶態進程執行系列操作的一組機制: 通過信號量與其他進程進行同步 向其他進程發送消息或者從其他進程接收消息

System V IPC 之消息隊列

指南 oid 消息 如果 bye time_t 信號 lag 一個 消息隊列和共享內存、信號量一樣,同屬 System V IPC 通信機制。消息隊列是一系列連續排列的消息,保存在內核中,通過消息隊列的引用標識符來訪問。使用消息隊列的好處是對每個消息指定了特定消息類型,接收

Systemd初始化進程/RHEL 6系統中System V init命令與RHEL 7系統中systemctl命令的對比

-i 公司 ystemd n-2 lis files merge ont -c Linux操作系統的開機過程是這樣的,即從BIOS開始,然後進入Boot Loader,再加載系統內核,然後內核進行初始化,最後啟動初始化進程。初始化進程作為Linux系統的第一個進程,它需要完