1. 程式人生 > >程序間通訊方式——訊號量(Semaphore)

程序間通訊方式——訊號量(Semaphore)

1.訊號量

訊號量本質上是一個計數器(不設定全域性變數是因為程序間是相互獨立的,而這不一定能看到,看到也不能保證++引用計數為原子操作),用於多程序對共享資料物件的讀取,它和管道有所不同,它不以傳送資料為主要目的,它主要是用來保護共享資源(訊號量也屬於臨界資源),使得資源在一個時刻只有一個程序獨享。

2.訊號量的工作原理

由於訊號量只能進行兩種操作等待和傳送訊號,即P(sv)和V(sv),他們的行為是這樣的:

(1)P(sv):如果sv的值大於零,就給它減1;如果它的值為零,就掛起該程序的執行

(2)V(sv):如果有其他程序因等待sv而被掛起,就讓它恢復執行,如果沒有程序因等待sv而掛起,就給它加1.

在訊號量進行PV操作時都為原子操作(因為它需要保護臨界資源)

注:原子操作:單指令的操作稱為原子的,單條指令的執行是不會被打斷的

3.二元訊號量

二元訊號量(Binary Semaphore)是最簡單的一種鎖(互斥鎖),它只用兩種狀態:佔用與非佔用。所以它的引用計數為1。

4.程序如何獲得共享資源

(1)測試控制該資源的訊號量

(2)訊號量的值為正,程序獲得該資源的使用權,程序將訊號量減1,表示它使用了一個資源單位

(3)若此時訊號量的值為0,則程序進入掛起狀態(程序狀態改變),直到訊號量的值大於0,若程序被喚醒則返回至第一步。

注:訊號量通過同步與互斥保證訪問資源的一致性。

5.與訊號量相關的函式

所有函式共用標頭檔案

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

5.1建立訊號量

int semget(key_t key,int nsems,int flags)
                                  //返回:成功返回訊號集ID,出錯返回-1

(1)第一個引數key是長整型(唯一非零),系統建立IPC通訊 ( 訊息佇列、 訊號量和 共享記憶體) 時必須指定一個ID值。通常情況下,該id值通過ftok函式得到,由核心變成識別符號,要想讓兩個程序看到同一個訊號集,只需設定key值不變就可以。

(2)第二個引數nsem指定訊號量集中需要的訊號量數目,它的值幾乎總是1。

(3)第三個引數flag是一組標誌,當想要當訊號量不存在時建立一個新的訊號量,可以將flag設定為IPC_CREAT與檔案許可權做按位或操作。
設定了IPC_CREAT標誌後,即使給出的key是一個已有訊號量的key,也不會產生錯誤。而IPC_CREAT | IPC_EXCL則可以建立一個新的,唯一的訊號量,如果訊號量已存在,返回一個錯誤。一般我們會還或上一個檔案許可權

5.2刪除和初始化訊號量

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

如有需要第四個引數一般設定為union semnu arg;定義如下

union semun{  
    int val;  //使用的值
    struct semid_ds *buf;  //IPC_STAT、IPC_SET 使用的快取區
    unsigned short *arry;  //GETALL,、SETALL 使用的陣列
    struct seminfo *__buf; // IPC_INFO(Linux特有) 使用的快取區 
};  

(1)sem_id是由semget返回的訊號量識別符號

(2)semnum當前訊號量集的哪一個訊號量

(3)cmd通常是下面兩個值中的其中一個
SETVAL:用來把訊號量初始化為一個已知的值。p 這個值通過union semun中的val成員設定,其作用是在訊號量第一次使用前對它進行設定。
IPC_RMID:用於刪除一個已經無需繼續使用的訊號量識別符號,刪除的話就不需要預設引數,只需要三個引數即可。

5.3改變訊號量的值

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

(1)nsops:進行操作訊號量的個數,即sops結構變數的個數,需大於或等於1。最常見設定此值等於1,只完成對一個訊號量的操作

(2)sembuf的定義如下:

struct sembuf{  
    short sem_num;   //除非使用一組訊號量,否則它為0  
    short sem_op;   //訊號量在一次操作中需要改變的資料,通常是兩個數,                                         
                    //一個是-1,即P(等待)操作,  
                    //一個是+1,即V(傳送訊號)操作。  
    short sem_flg; //通常為SEM_UNDO,使作業系統跟蹤訊號量,  
                  //並在程序沒有釋放該訊號量而終止時,作業系統釋放訊號量  
};  

5.4sembuf中sem_flg的設定問題

通常設定為SEM_UNDO,使作業系統跟蹤訊號量, 並在程序沒有釋放該訊號量而終止時,作業系統釋放訊號量 ,例如在二元訊號量中,你不釋放該訊號量 而異常退出,就會導致別的程序一直申請不到訊號量,而一直處於掛起狀態。

是否設定sem_flg為SEM_UNDO的區別
這裡寫圖片描述

6.模擬實現訊號量實現程序間通訊

1.標頭檔案宣告部分comm.h

#ifndef __COMM_H__
#define __COMM_H__
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/wait.h>
#include <error.h>
#define  PATHNAME "."
#define  PROJ_ID  0 
union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short *arry;
    struct seminfo *__buf;
};
//訊號量是建立還是獲取在於semget函式引數flag的設定
static int CommSemid(int nums, int flags);
//建立訊號量
int CreatSemid(int nums);
//獲取已經建立的訊號量
int GetSemid(int nums);
//初始化訊號量
int InitSem(int semid, int which, int _val);
//PV操作在於它_op的值
static int SemPV(int semid, int which, int _op);
//P操作
int P(int semid, int which, int _op);
//V操作
int V(int semid, int which, int _op);
//由於(System V通訊方式)訊號量生命週期隨核心,所以要銷燬訊號量
int Destory(int semid);
#endif

2.函式封裝部分comm.c

#include "comm.h"
//int semget(key_t key, int nsems, int semflg);

static int CommSemid(int nums, int flags)
{
    key_t _key = ftok(PATHNAME, PROJ_ID);
    if (_key>0)
    {
        return semget(_key, nums, flags);
    }
    else
    {
        perror("CommSemid");
        return -1;
    }
}

int CreatSemid(int nums)
{
    return CommSemid(nums, IPC_CREAT | IPC_EXCL | 0666);
}
// int semctl(int semid, int semnum, int cmd, ...);

int GetSemid(int nums)
{
    return CommSemid(nums, IPC_CREAT);
}
int Destory(int semid)
{
    if (semctl(semid, 0, IPC_RMID)>0)
    {

        return 0;
    }
    else
    {
        perror("Destory");
        return -1;
    }
}

int InitSem(int semid, int which, int _val)
{

    union semun _semun;
    _semun.val = _val;
    if (semctl(semid, which, SETVAL, _semun)<0)
    {
        perror("InitSem");
        return -1;
    }
    return 0;
}
static int SemPV(int semid, int which, int _op)
{
    struct sembuf _sf;
    _sf.sem_num = which;
    _sf.sem_op = _op;
    _sf.sem_flg = 0;
    return semop(semid, &_sf, 1);
}

int P(int semid, int which, int _op)
{
    if (SemPV(semid, which, _op)<0)
    {
        perror("P");
        return -1;
    }
    return 0;
}

int V(int semid, int which, int _op)
{
    if (SemPV(semid, which, _op)<0)
    {
        perror("V");
        return -1;
    }
    return 0;

}

3.測試用例Test:

#include "comm.c"
int main()
{
    int semid = CreatSemid(1);
    printf("%d\n", semid);
    InitSem(semid, 0, 1);
    pid_t id = fork();
    if (id == 0)
    {//child
        int semid = GetSemid(0);
        while (1)
        {
            P(semid, 0, -1);
            printf("A");
            fflush(stdout);
            usleep(10000);
            printf("A");
            fflush(stdout);
            usleep(20000);
            V(semid, 0, 1);
        }
    }
    else
    {//father
        while (1)
        {
            P(semid, 0, -1);
            usleep(30000);
            printf("B");
            fflush(stdout);
            usleep(8000);
            printf("B");
            fflush(stdout);
            usleep(20000);
            V(semid, 0, 1);
        }
        if (waitpid(id, NULL, 0) < 0)
        {
            perror("waitpid");
            return -1;
        }

    }
    Destory(semid);
    return 0;
}

執行結果:
這裡寫圖片描述

分析:
如果沒有進行PV操作,打出來AB序列就是沒有規律可言,而現在可以看到顯示器必須顯示兩個B,才會顯示兩個A,這樣反覆下去,這就是訊號量的原子操作,它的執行是不會受其他程序的影響。要釋放自己的訊號量,才能提供給其他程序使用,不然其他需要這個訊號量才能執行的程序會處於掛起狀態。

相關推薦

程序通訊方式——訊號Semaphore

1.訊號量 訊號量本質上是一個計數器(不設定全域性變數是因為程序間是相互獨立的,而這不一定能看到,看到也不能保證++引用計數為原子操作),用於多程序對共享資料物件的讀取,它和管道有所不同,它不以傳送資料為主要目的,它主要是用來保護共享資源(訊號量也屬於臨界資源

程序通訊之——訊號system V

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

程序通訊方式總結——管道

         Linux/Unix系統IPC是各種程序間通訊方式的統稱,但是其中極少能在所有Linux/Unix系統實現中進行移植。隨著POSIX和Open Group(X/Open)標準化的推

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

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

Linux程序通訊訊號(semaphore)、訊息佇列(Message Queue)和共享記憶體(Share Memory)

System V 程序通訊方式:訊號量(semaphore)、訊息佇列(Message Queue)和共享記憶體(Share Memory) 訊號量 訊號量(semaphore)實際是一個整數,它的值由多個程序進行測試(test)和設定(set)。就每個程序所關心的測試和

linux程序通訊訊號(semaphore)

==================================================== 訊號量(semaphore)簡介 當我們在多使用者系統,多程序系統,或是兩者混合的系統中使用執行緒操作編寫程式時,我們經常會發現我們有段臨界程式碼,在此處我們需要

Linux程序通訊——使用訊號

這篇文章將講述別一種程序間通訊的機制——訊號量。注意請不要把它與之前所說的訊號混淆起來,訊號與訊號量是不同的兩種事物。有關訊號的更多內容,可以閱讀我的另一篇文章:Linux程序間通訊——使用訊號。下面就進入訊號量的講解。 一、什麼是訊號量 為了防止出現因多個程式同時訪問一

程序通訊訊號

何為訊號量 訊號量的本質是一種資料操作鎖,它本身不具有資料交換的功能,而是通過控制其他的通訊資源(檔案,外部裝置)來實現程序間通訊,它本身只是一種外部資源的標識。訊號量在此過程中負責資料操作的互斥、同步等功能。 對訊號量的操作 當請求一個使用訊號量來表示的資源時,程序需要

Linux程序通訊訊號 semget()、semop()、semctl()

這篇文章將講述別一種程序間通訊的機制——訊號量。注意請不要把它與之前所說的訊號混淆起來,訊號與訊號量是不同的兩種事物。有關訊號的更多內容,可以閱讀我的另一篇文章:Linux程序間通訊 -- 訊號。下面就進入訊號量的講解。 一、什麼是訊號量 為了防止出現

PYTHON——多執行緒:訊號Semaphore

  訊號量也是一把鎖,用來控制執行緒併發數的。   BoundedSemaphore或Semaphore管理一個內建的計數 器,每當呼叫acquire()時-1,呼叫release()時+1。       計數器不能小於0,當計數器為 0時,acquire()將阻塞執行緒至同

《java併發程式設計實戰》:執行緒同步輔助類之訊號semaphore

1.訊號量的概念: 訊號量是一種計數器,用來保護一個或者多個共享資源的訪問,它是併發程式設計的一種基礎工具,大多數程式語言都提供了這個機制。 2、訊號量控制執行緒訪問流程: 如果執行緒要訪問一個共享資源,它必須先獲得訊號量。如果訊號量的內部計數器大於0,訊號量將減1,然後

Linux程序通訊--mmap共享記憶體

 共享記憶體可以說是最有用的程序間通訊方式,也是最快的IPC形式。兩個不同程序A、B共享記憶體的意思是,同一塊實體記憶體被對映到程序A、B各自的程序地址空間。程序A可以即時看到程序B對共享記憶體中資料的更新,反之亦然。由於多個程序共享同一塊記憶體區域,必然需要某種同步機制,

Linux程序通訊(IPC)程式設計實踐 詳解System V訊息佇列(1)

訊息佇列簡介 訊息佇列提供了一個從一個程序向另外一個程序傳送一塊資料的方法(本機);每個資料塊都被認為是有一個型別,接收者程序接收的資料塊可以有不同的型別值。訊息佇列也有管道一樣的不足:  (1)每

Java併發程式設計實戰--計數訊號Semaphore

計數訊號量(Counting Semaphore)用來控制同時訪問某個特定資源的運算元量,或者同時執行某個指定操作的數量。計數訊號量還可以用來實現某種資源池,或者對容器施加邊界。 Semaphore中管理著一組虛擬的許可(permit),許可的初始數量可通過建

linux程序通訊之訊號燈訊號semaphore

訊號燈通訊,和一般意義的通訊不大一樣,通訊一般是用來收發資料,而訊號燈卻是用來控制多程序訪問共享資源的,利用這一功能,訊號量也就可以用做程序同步(實際上是執行緒間同步)。訊號燈的當前值、有幾個程序在等待當前值變為0等等,這些資訊,可隨時用watch -n 0.1 ipcs -

Linux下程序通訊方式之管道、訊號、共享記憶體、訊息佇列、訊號、套接字

/* 1,程序間通訊 (IPC ) Inter-Process Communication   比較好理解概念的就是程序間通訊就是在不同程序之間傳播或交換資訊。 2,linux下IPC機制的分類:管道、訊號、共享記憶體、訊息佇列、訊號量、套接字 3,這篇主要說說管

程序通訊方式-----訊息佇列

訊息佇列 訊息佇列,是訊息的連結表,存放在核心中。一個訊息佇列由一個識別符號(即佇列ID)來標識。使用者程序可以向訊息佇列新增訊息,也可以向訊息佇列讀取訊息。 同管道檔案相比,訊息佇列中的每個訊息指定特定的訊息型別,接收的時候可以不需要按照佇列次序讀取,可以根據自定義型別

嵌入式Linux併發程式設計,程序通訊方式訊號訊號機制,檢視新號kill -l,常用訊號,發訊號命令kill [-signal] pid、killall [-u user | prog]

1,訊號機制 訊號是在軟體層次上對中斷機制的一種模擬,是一種非同步通訊方式 (一個程序在任何條件下,都可以隨時的接收訊號,不需要其他的處理) Linux核心通過訊號通知使用者程序,不同的訊號型別代表不同的事件 Linux對早期的unix訊號機制進行了擴充

共享記憶體多程序通訊程序同步使用訊號來實現

Linux 環境下C程式設計指南,通過共享記憶體進行程序間通訊的例子,程序間同步使用訊號量來實現。 程式碼 11-5 使用說明:這是一個簡單的伺服器和客戶端程式,如果啟動程式時不帶引數,則執行伺服器程式; 如果帶引數,則執行客戶端程式,所帶引數只有一個,就是伺服器端所顯

Linux 程序通訊方式 pipe函式

Linux 程序間通訊方式有以下幾種: 1-》管道(pipe)和有名管道(fifo). 2-》訊息佇列 3-》共享記憶體 4-》訊號量 5-》訊號(signal) 6-》套接字(sicket) 在這裡我們看一下第一種====管道(pipe)。有名管道(fifo)見其它文章。