【Linux】程序間通訊-訊號量詳解及程式設計例項
前面一篇文章執行緒同步之訊號量同步 講的是執行緒之間的訊號量,這篇講的更加具有通用性,能夠實現程序之間的同步。
訊號量概述
訊號量定義:
它是一個特殊變數,只允許對它進行等待和傳送訊號這兩種操作。
- P(訊號量變數sv):等待。如果sv大於0,減小sv。如果sv為0,掛起這個程序的執行。
- V(訊號量變數sv):傳送訊號。如果有程序被掛起等待sv,使其恢復執行。如果沒有進行被掛起等待sv,增加sv。
PV操作虛擬碼
semaphore sem_id = 1;
loop{
P(sem_id);
臨界區程式碼;
V(sem_id);
非臨界區程式碼;
}
訊號量機制及相關介面
Linux系統中的訊號量介面經過了精心設計,提供了比通常所需更多的機制。所有的Linux訊號量函式都是針對成組的通用訊號量進行操作,而不只是針對一個二進位制訊號量。但是在絕大多數情況下,使用一個單個訊號量就足夠了,所以在這裡只討論單個訊號量的使用。
semget函式
用於建立一個新的訊號量或者是取得一個已有的訊號量的鍵。
所需包含的標頭檔案:
#include <sys/sem.h>
它通常依賴於另兩個標頭檔案:
#include <sys/types.h>
#include <sys/ipc.h>
一般情況下,這兩個標頭檔案都會被它自動包含。功能描述
函式原型:int semget(key_t key,int nsems,int semflg);
功能描述
獲取與某個鍵關聯的訊號量集標識。訊號量集被建立的情況有兩種:
1.如果鍵的值是IPC_PRIVATE。
2.或者鍵的值不是IPC_PRIVATE,並且鍵所對應的訊號量集不存在,同時標誌中指定IPC_CREAT。
當呼叫semget建立一個訊號量時,他的相應的semid_ds結構被初始化。ipc_perm中各個量被設定為相應
值:
sem_nsems被設定為nsems所示的值;
sem_otime被設定為0;
sem_ctime被設定為當前時間
引數解釋
key:所建立或開啟訊號量集的鍵值。需要是唯一的非零整數。
nsems:建立的訊號量集中的訊號量的個數,該引數只在建立訊號量集時有效。幾乎總是取值為1.
flag:呼叫函式的操作型別,也可用於設定訊號量集的訪問許可權,兩者通過or表示
返回值說明:
如果成功,則返回訊號量集的IPC識別符號(一個正數)。
如果失敗,則返回-1,errno被設定成以下的某個值
EACCES:沒有訪問該訊號量集的許可權
EEXIST:訊號量集已經存在,無法建立
EINVAL:引數nsems的值小於0或者大於該訊號量集的限制;或者是該key關聯的訊號量集已存在,並且nsems
大於該訊號量集的訊號量數
ENOENT:訊號量集不存在,同時沒有使用IPC_CREAT
ENOMEM :沒有足夠的記憶體建立新的訊號量集
ENOSPC:超出系統限制
semop函式
用於改變訊號量的值。
#include <sys/sem.h>
int semop( int semid, struct sembuf semoparray[], size_t nops );
引數解釋:
引數semid是一個通過semget函式返回的一個訊號量識別符號
引數nops標明瞭引數semoparray所指向陣列中的元素個數
引數semoparray是一個struct sembuf結構型別的陣列指標,
結構sembuf來說明所要執行的操作,其定義如下:
struct sembuf{
unsigned short sem_num;
short sem_op;
short sem_flg;
}
在sembuf結構中,
sem_num是相對應的訊號量集中的某一個資源,所以其值是一個從0到相應的訊號量集的資源總數(ipc_perm.sem_nsems)之間的整數。除非使用一組訊號了,否則它的取值一般為0.
sem_op的值是一個整數,是訊號量在一次操作中需要改變的數值(可以是非1的數值)。通常只會用到兩個值:1----P操作,-1---V操作。
sem_flg說明函式semop的行為。通常被設定為SEM_UNDO。它將使得作業系統跟著當前程序對這個訊號量的修改情況,如果這個程序在沒有釋放該訊號量的情況下終止,作業系統將自動釋放該程序持有的訊號量。
注意:
semop呼叫的一切動作都是一次性完成的,這是為了避免出現因使用多個訊號量而可能發生的競爭現象。
semctl函式
原型:
int semctl(int semid,int semnum,int cmd,union semun);
返回值:
如果成功,則為一個正數。
如果失敗,則為-1:
errno=EACCESS(許可權不夠)
EFAULT(arg指向的地址無效)
EIDRM(訊號量集已經刪除)
EINVAL(訊號量集不存在,或者semid無效)
EPERM(EUID沒有cmd的權利)
ERANGE(訊號量值超出範圍)
引數解釋:
sem_id是由semget返回的訊號量識別符號。
sem_num與前面一個函式相同。
cnd:表示將要採取的動作。最常用的兩個值如下:
- SETVAL:用來把訊號量初始化為一個已知的值。這個值通過union semun中的val成員設定。其作用是在訊號量第一次使用之前對它進行設定。
- IPC_RMID:用於刪除一個無需繼續使用的訊號量標誌符。
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;
訊號量的使用
雖然上述函式呼叫看似很複雜,但是我們可以用這些介面來建立一個簡單的PV型別的介面,然後用這個簡單的介面來進行訊號量相關操作。
下面的程式使用上述介面實現了P、V操組以及設定訊號量、刪除訊號量的操作。
然後利用這些新的函式介面實現了兩個程式例項訪問臨界區的功能。
在這裡同時訪問臨界區的是一個程式的兩個不同例項,並且使用引數個數的不同來進行區別。其中一個需要完成訊號量的建立及其刪除的額外操作。
兩個程式在進入臨界區和離開臨界區的時候分別都會輸出兩個不同的字元,以此來進行區分。可以發現,兩個不同的字元是成對出現的。因為同一時刻只有一個程序可以進入臨界區。
完整程式碼:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/sem.h>//包含訊號量定義的標頭檔案
//聯合型別semun定義
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
};
//函式宣告
//函式:設定訊號量的值
static int set_semvalue(void);
//函式:刪除訊號量
static void del_semvalue(void);
//函式:訊號量P操作
static int semaphore_p(void);
//函式:訊號量V操作
static int semaphore_v(void);
static int sem_id;//訊號量ID
int main(int argc,char *argv[])
{
int i;
int pause_time;
char op_char = 'O';
srand((unsigned int)getpid());
//建立一個新的訊號量或者是取得一個已有訊號量的鍵
sem_id = semget((key_t)1234,1,0666 | IPC_CREAT);
//如果引數數量大於1,則這個程式負責建立訊號和刪除訊號量
if(argc > 1)
{
if(!set_semvalue())
{
fprintf(stderr,"failed to initialize semaphore\n");
exit(EXIT_FAILURE);
}
op_char = 'X';//對程序進行標記
sleep(5);
}
//迴圈:訪問臨界區
for(i = 0;i < 10;++i)
{
//P操作,嘗試進入緩衝區
if(!semaphore_p())
exit(EXIT_FAILURE);
printf("%c",op_char);
fflush(stdout);//重新整理標準輸出緩衝區,把輸出緩衝區裡的東西列印到標準輸出裝置上
pause_time = rand() % 3;
sleep(pause_time);
printf("%c",op_char);
fflush(stdout);
//V操作,嘗試離開緩衝區
if(!semaphore_v())
exit(EXIT_FAILURE);
pause_time = rand() % 2;
sleep(pause_time);
}
printf("\n %d - finished \n",getpid());
if(argc > 1)
{
sleep(10);
del_semvalue();//刪除訊號量
}
}
//函式:設定訊號量的值
static int set_semvalue(void)
{
union semun sem_union;
sem_union.val = 1;
if(semctl(sem_id,0,SETVAL,sem_union))
return 0;
return 1;
}
//函式:刪除訊號量
static void del_semvalue(void)
{
union semun sem_union;
if(semctl(sem_id,0,IPC_RMID,sem_union))
fprintf(stderr,"Failed to delete semaphore\n");
}
//函式:訊號量P操作:對訊號量進行減一操作
static int semaphore_p(void)
{
struct sembuf sem_b;
sem_b.sem_num = 0;//訊號量編號
sem_b.sem_op = -1;//P操作
sem_b.sem_flg = SEM_UNDO;
if(semop(sem_id,&sem_b,1) == -1)
{
fprintf(stderr,"semaphore_p failed\n");
return 0;
}
return 1;
}
//函式:訊號量V操作:對訊號量進行加一操作
static int semaphore_v(void)
{
struct sembuf sem_b;
sem_b.sem_num = 0;//訊號量編號
sem_b.sem_op = 1;//V操作
sem_b.sem_flg = SEM_UNDO;
if(semop(sem_id,&sem_b,1) == -1)
{
fprintf(stderr,"semaphore_v failed\n");
return 0;
}
return 1;
}
執行結果:
相關推薦
【Linux】程序間通訊-訊號量詳解及程式設計例項
前面一篇文章執行緒同步之訊號量同步 講的是執行緒之間的訊號量,這篇講的更加具有通用性,能夠實現程序之間的同步。 訊號量概述 訊號量定義: 它是一個特殊變數,只允許對它進行等待和傳送訊號這兩種操作。 P(訊號量變數sv):等待。如果sv大於0,減小sv。如果sv為0,掛起這
程序間通訊--訊號量詳解及程式設計例項
訊號量概述 訊號量定義: 它是一個特殊變數,只允許對它進行等待和傳送訊號這兩種操作。 P(訊號量變數sv):等待。如果sv大於0,減小sv。如果sv為0,掛起這個程序的執行。V(訊號量變數sv):傳送訊號。如果有程序被掛起等待sv,使其恢復執行。如果沒有進行被掛起等待sv
程序間通訊-訊號量詳解及程式設計例項
前面一篇文章執行緒同步之訊號量同步 講的是執行緒之間的訊號量,這篇講的更加具有通用性,能夠實現程序之間的同步。 訊號量概述 訊號量定義: 它是一個特殊變數,只允許對它進行等待和傳送訊號這兩種操作。 P(訊號量變數sv):等待。如果sv大於0,減小sv。如果sv為0,掛
【Linux】程序間通訊之訊息佇列、訊號量和共享儲存
訊息佇列、訊號量和共享儲存是IPC(程序間通訊)的三種形式,它們功能不同,但有相似之處,下面先介紹它們的相似點,然後再逐一說明。 1、相似點 每個核心中的IPC結構(訊息佇列、訊號量和共享儲存)都用一個非負整數的識別符號加以引用,與檔案描述符不同,當一個
【Linux】程序間通訊(IPC)之訊號量詳解與測試用例
學習環境centos6.5 Linux核心2.6 程序間通訊概述 1. 程序通訊機制 一般情況下,系統中執行著大量的程序,而每個程序之間並不是相互獨立的,有些程序之間經常需要互相傳遞訊息。但是每個程序在系統中都有自己的地址空間,作業系統通過頁表
【Linux】程序間通訊
1.程序間通訊的目的 資料傳輸,一個程序需要將它的資料傳送給另一個程序 資源共享:多個程序之間共享同樣的資源 通知事件:一個程序需要向另一個或一組程序傳送資訊,通知發生了某種事件(如程序終止時要通知父程序) 程序控制:有些程序希望完全控制另一個程序的執行(如
【Linux】程序間通訊-命名管道FIFO
命名管道概述 如果我們要在不相關的程序間交換資料,那麼使用FIFO檔案將會十分方便。 FIFO檔案通常也稱為命名管道(named pipe)。命名管道是一種特殊型別的檔案,它在檔案系統中以檔名的形式存在。 建立命名管道 建立命名管道一般有兩種方式: 命令列方式 一個比較舊
【Linux】程序間通訊(IPC)之訊息佇列詳解及測試用例
學習環境 Centos6.5 Linux 核心 2.6 什麼是訊息佇列? 訊息佇列是SystemV版本中三種程序通訊機制之一,另外兩種是訊號量和共享儲存段。訊息佇列提供了程序間傳送資料塊的方法,而且每個資料塊都有一個型別標識。訊息佇列是基於訊息的,而管
linux的程序間通訊——訊號量
訊號量的本質是一種資料操作鎖,它本⾝身不具有資料交換的功能,而是通過控制其他的通訊資源(檔案,外部裝置)來實現程序間通訊,它本身只是一種外部資源的標識。訊號量在此過程中負責資料操作的互斥、同步等功能。當請求一個使⽤用訊號量來表⽰示的資源時,程序需要先讀取訊號量的
【Linux】程序間通訊之訊息佇列
在上一篇部落格裡,我們學習了程序間通訊的一種方式,那就是管道,今天我們繼續學習另一種方式訊息佇列。 訊息佇列 一. 什麼是訊息佇列? 訊息佇列是訊息的連結串列,存放在核心中並由訊息佇列識別符號表示。 訊息佇列提供了一個從一個程序向另一個程
【Linux】程序間通訊(IPC)之共享記憶體詳解與測試用例
學習環境centos6.5 Linux核心2.6 什麼是共享記憶體 共享記憶體允許兩個或更多程序訪問同一塊記憶體。當一個程序改變了這塊記憶體中的內容的的時候,其他程序都會察覺到這個更改。 效率: 因為所有程序共享同一塊記憶體,共享記憶體在各種程序
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
linux程序間通訊-訊號量
引入訊號量之前,先介紹幾個概念: 1.臨界資源:同一時刻只允許一個程序或執行緒訪問的資源。(有時候是有限個程序或執行緒訪問),就比如一支筆一次只能是一個人使用。 這裡的臨界資源在c語言或c++中的表
php程序間通訊--訊號量
訊號量是什麼? 訊號量 : 又稱為訊號燈、旗語 用來解決程序(執行緒同步的問題),類似於一把鎖,訪問前獲取鎖(獲取不到則等待),訪問後釋放鎖。 舉一個生活中的例子:以一個停車場的運作為例。簡單起見,假設停車場只有三個車位,一開始三個車位都是空的。這時如果同時來了五輛車,看門人允許
【轉】程序間通訊的方式(8種)
程序間通訊的方式——訊號、管道、訊息佇列、共享記憶體 多程序: 首先,先來講一下fork之後,發生了什麼事情。 由fork建立的新程序被稱為子程序(child process)。該函式被呼叫一次,但返回兩次。兩次返回的區別是子程序的返回值是0,而父程序的返回值則是新程序(子程序)的程序
【Linux】程序間關係與守護程序
程序間關係 程序組/作業/會話 程序組 程序組是一個或多個程序的集合,通常它們與一組作業相關聯,可以接受來自同一終端的各種訊號。 每個程序除了有一個程序ID之外,還屬於一個程序組。 每個程序組都有唯一的程序組ID(整數,也可以存放在pid_t型別
【作業系統】程序間通訊(C#)
程序間通訊命名管道程序間通訊的一種方式,Pipes:管道,分為無名管道:在父子程序間交換資料;有名管道:可在不同主機間交換資料,分為伺服器方和客戶方,在Win9X下只支援有名管道客戶。命名管道的命名命名管道是一個有名字的,單向或雙向的通訊管道。管道的名稱有兩部分組成:計算機名
【Linux】程序間的關係以及終端的概念
程序間關係 程序組 每個程序都有自己的程序ID,除此之外,每個程序也都屬於一個程序組。 程序組是一個程序或者多個程序的集合。 通常他們和同一個作業有聯絡,可以接受來自同一個終端的訊號。 每一個程序組都
【Linux】條件變數與訊號量
1.定義訊號量 sem_t sem; 2.初始化訊號量 int sem_init(sem_t *sem, int pshared, unsigned int value);
Linux環境程序間通訊(一) 管道及有名管道(轉)
管道是Linux支援的最初Unix IPC形式之一,具有以下特點:管道是半雙工的,資料只能向一個方向流動;需要雙方通訊時,需要建立起兩個管道;只能用於父子程序或者兄弟程序之間(具有親緣關係的程序);單獨構成一種獨立的檔案系統:管道對於管道兩端的程序而言,就是一個檔案,但它不是普通的檔案,它不屬於某種檔案系統,