1. 程式人生 > >Linux 系統應用程式設計——程序間通訊(下)

Linux 系統應用程式設計——程序間通訊(下)

        在前面,我們學習了傳統的程序間通訊方式——無名管道(pipe)、有名管道(fifo)和訊號(signal)。

        下面我們來學習 System V  IPC 物件

1、共享記憶體(share memory);

2、訊號燈(semaohore);

3、訊息佇列(message queue);

IPC物件是活動在核心級別的一種程序間通訊的工具。存在的IPC物件通過它的識別符號來引用和訪問,這個識別符號是一個非負整數,它唯一的標識了一個IPC物件,這個IPC物件可以是訊息佇列或訊號量或共享儲存器中的任意一種型別。

     Linux系統中識別符號被宣告成整數,所以可能存在的最大識別符號為65535。當時對於IPC物件,其識別符號(id)

與檔案描述符有所不同,使用open函式開啟一個檔案時,返回的檔案描述符的值為當前程序最小可用的檔案描述符陣列的下標。IPC物件是系統全域性的流水號,其刪除或建立時相應的識別符號的值會不斷增加到最大的值,歸零迴圈分配使用。

     IPC的識別符號只解決了內部訪問一個IPC物件的問題,如何讓多個程序都訪問某一個特定的IPC物件還需要一個外部鍵(key),一個IPC物件都與一個鍵相關聯。這樣就解決了多程序在一個IPC物件上匯合的問題。IPC物件時需要指定一個鍵值,型別為key_t,在<sys/types.h>中定義為一個長整型。鍵值到識別符號的轉換是由系統核心來維護的,這裡呼叫IPC物件的建立函式(semget msgget shmget )

實現key 值到 id 的轉換。

      從上圖中我們可以看到得到這個鍵值 key 有兩種方法:

1)通用方法:呼叫ftok()函式
       函式ftok可以使用兩個引數生成一個鍵值,函式原型如下:

#include <sys/ipc.h>
key_t ftok( const char *path, int id );

       函式中引數path是一個檔名。函式中進行的操作是,取該檔案的stat結構的st_dev成員和st_ino成員的部分值,然後與引數ID的第八位結合起來生成一個鍵值。由於只是使用st_dew和st_ino的部分值,所以會丟失資訊,不排除兩個不同檔案使用同一個ID,得到同樣鍵值的情況。
系統為每一個IPC物件儲存一個ipc_perm結構體,該結構說明了IPC物件的許可權和所有者,每一個版本的核心各有不用的ipc_perm結構成員。

檔案<sys/ipc.h> 中對其定義:

struct ipc_perm 
{
	ey_t key;
	id_t uid;
	id_t gid;
	id_t cuid;
	id_t cgid;
	nsigned short mode;
	nsigned short seq;
};

2)父子程序之間:

      Key 為IPC_PRIVATE,父子程序之間key值為IPC_PRIVATE。

      當有了一個IPC物件的鍵值,如何讓多個程序知道這個鍵,可以有多種實現的辦法:

1) 、使用檔案來做中間的通道,建立IPC物件程序,使用鍵IPC_PRIVATE成功建立IPC物件之後,將返回的識別符號儲存在一個檔案中。其他程序通過讀取這個識別符號來引用IPC物件通訊。

2)、定義一個多個程序都認可的鍵,每個程序使用這個鍵來引用IPC物件,值得注意的是,建立IPC物件的程序中,建立IPC物件時如果該鍵值已經與一個IPC物件結合,則應該刪除該IPC物件,再建立一個新的IPC物件。

3)、多程序通訊中,對於指定鍵引用一個IPC物件而言,可能不具有拓展性,並且在該鍵值已經被一個IPC物件結合的情況下。所以必須刪除這個存在物件之後再建立一個新的。這有可能影響到其他正在使用這個物件的程序。函式ftok可以在一定程度上解決這個問題。

但IPC物件存在一些問題,主要集中在以下幾點:

1)、過於繁雜的程式設計介面,比起使用其他通訊方式,IPC所要求的程式碼量要明顯增多。

2)、IPC不使用通用的檔案系統,這也是飽受指責的原因。所以不能使用標準I/O操作函式來讀寫IPC物件。為此不得不新增加一些函式來支援必要的一些操作(例如msgget msgrev msgctl等)並且對於不同型別的IPC物件都有一系列特定的操作函式。由於IPC不使用檔案描述符,所以不能使用多路I/O監控函式select及poll函式來操作IPC物件。

3)、缺少的資源回收機制。由於IPC物件在使用過程中並不儲存引用計數,所以當出現一個程序建立了IPC物件然後退出時,則這個物件只有在出現後面幾種情況才會被釋放或者刪除,即由某一個程序讀出訊息,或者IPC的所有者或超級使用者刪除了這個物件。這也是IPC相對於管道或FIFO所欠缺的資源回收機制。

下面是檔案物件和IPC物件的對比

一、共享記憶體

       共享記憶體可以說是Linux 下最快速、最有效的程序間通訊方式。兩個不同程序A 、B 共享記憶體的意思是,同一塊實體記憶體被對映到程序A 、B 各自的程序地址空間,程序A 可以即時看到程序B 對共享記憶體中資料的更新;反之,程序B 也可以即時看到程序A對共享記憶體中資料的更新。

       這裡簡單說下對映的概念:

       Linux系統會為每個程序分配 4GB 的虛擬地址空間,一定情況下,需要將虛擬記憶體轉換成實體記憶體,這就需要記憶體對映。為什麼我們需要使用虛擬地址呢?最主要的原因是不同PC的實體記憶體會不一樣,如果直接使用實體地址,會造成程式的移植性很差,另外虛擬地址訪問記憶體有以下優勢:

1、程式可以使用一系列相鄰的虛擬地址來訪問實體記憶體中不相鄰的大記憶體緩衝區。

2、程式可以使用一系列虛擬地址來訪問大於可用實體記憶體的記憶體緩衝區。當實體記憶體的供應量變小時,記憶體管理器會將實體記憶體頁(通常大小為 4 KB)儲存到磁碟檔案。資料或內碼表會根據需要在實體記憶體與磁碟之間移動。

3、不同程序使用的虛擬地址彼此隔離。一個程序中的程式碼無法更改正在由另一程序或作業系統使用的實體記憶體。

      程序可用的虛擬地址範圍稱為該程序的“虛擬地址空間”。每個使用者模式程序都有其各自的專用虛擬地址空間。對於 32 位程序,虛擬地址空間通常為 4 GB,範圍從 0x00000000 至 0xFFFFFFFF。

1、共享記憶體的概念

      共享記憶體從字面意義解釋就是多個程序可以把一段記憶體對映到自己的程序空間,以此來實現資料的共享及傳輸,這也是所有程序間通訊方式最快的一種,共享記憶體是存在於核心級別的一種資源。

      在Shell 中可以使用ipcs 命令檢視當前系統IPC中的狀態,在檔案系統中/proc目錄下有對其描述的相應檔案

 ipcs  -m ,其中 -m 是 memory 的意思 。

      在系統核心為一個程序分配記憶體地址時,通過分頁機制可以讓一個程序的實體地址不連續,同時也可以讓一段記憶體同時分配給不同的程序。共享記憶體機制就是通過該原理實現的,共享記憶體機制只是提供資料的傳送,如何控制伺服器端和客戶端的讀寫操作互斥,這就需要一些其他的輔助工具,例如訊號量。

      採用共享記憶體通訊的一個顯而易見的好處就是效率高,因為程序可以直接讀寫記憶體,而不需要任何資料的拷貝。對於像管道和訊息佇列等通訊方式,則需要在核心和使用者控制元件進行四次資料的拷貝,而共享記憶體只拷貝兩次資料:一次從輸入檔案到共享區,另一次從共享記憶體區到輸出檔案。實際上,程序之間在共享記憶體時,並不總是讀寫少量資料後就解除對映,有新的通訊時,再重新建立共享記憶體區域。而是保持共享區域,知道通訊完畢為止,這樣,資料內同一直儲存在共享記憶體中,並沒有寫回檔案。共享記憶體中的內容往往是在接觸對映時才寫回檔案的。因此,採用共享記憶體的通訊方式效率是最高的。

     共享記憶體最大不足之處在意,由於多個程序對同一塊記憶體區域具有訪問的許可權,各個程序之間的同步問題顯得尤為重要。必須控制同一時刻只有一個程序對共享記憶體區域寫入資料,否則會造成資料的混亂。同步控制問題可以由訊號量來解決;

     對於每一個共享儲存段,核心會為其維護一個shmid_ds型別的結構體,其定義在標頭檔案<sys/shm.h>中,其定義如下:

struct shmid_ds
{
	struct ipc_perm shm_perm   //Operation permission structure.
	size_t shm_segsz  //Size of segment in bytes.
	pid_t  shm_lpid   //Process ID of last shared memory operation.
	pid_t  shm_cpid   //Process ID of creator.
	shmatt_t shm_nattch //Number of current attaches.
	time_t shm_atime  //Time of last shmat              
	time_t shm_dtime  //Time of last shmdt
	time_t shm_ctime  //Time of last change by shmctl
 }

2、共享記憶體的相關操作

1)建立或開啟共享記憶體

     要使用共享記憶體,首先要建立一個共享記憶體區域,建立共享記憶體的函式呼叫如下:

所需標頭檔案

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/shm.h>

函式原型 int shmget(key_t, int size ,int shmflg );
函式引數

Key:IPC_PRIVATE 或 ftok 的返回值

size:共享記憶體區大小

shmflag :同open函式的許可權位,也可用8進製表示法

函式返回值

成功:共享記憶體段識別符號

出錯:-1

        shmget函式除了可以用於建立一個新的共享記憶體外,也可以用於開啟一個已存在的共享記憶體。其中,引數key表示索要建立或開啟的共享記憶體的鍵值。size表示共享記憶體區域的大小,只在建立一個新的共享記憶體時生效。引數shmflag 表示呼叫函式的操作型別,也可用於設定共享記憶體的訪問許可權,兩者通過邏輯或表示.引數key 和 flag 決定了呼叫函式 shmget 的作用,相應的約定如下:

1)當 key  為 IPC_PRIVATE 時,建立一個新的共享記憶體,此時引數 flag 的取值無效;

2)當 key 不為 IPC_PRIVATE時,且flag 設定了IPC_CREAT 位,而沒有設定 IPC_EXCL 位,則執行操作由key取值決定。如果key 為核心中每個已存在的共享記憶體的鍵值。則執行開啟這個鍵的操作;反之,則執行建立共享記憶體的操作;

3)當 key 不為 IPC_PRIVATE時,且flag 設定了IPC_CREAT 位和 IPC_EXCL 位,則只執行建立共享記憶體的操作。引數key的取值應與核心中已存在的任何共享記憶體的鍵值都不相同,否則函式呼叫失敗,返回EEXIST錯誤,所以一般典型的呼叫程式碼首先會檢查是否返回該錯誤,如果確實返回該錯誤,那麼只要呼叫開啟共享記憶體的函式就可以了(即將flag 設定為 IPC_CREAT,而不設定IPC_EXCL);

2)附加

    當一個共享記憶體建立或開啟後,某個程序如果要使用該共享記憶體則必須將此記憶體區附加到它的地址空間,附加操作的系統呼叫函式如下:

所需標頭檔案

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/shm.h>

函式原型 void *shmat (int shmid, const void *shaddr, int shmflg);
函式引數

shmid :要對映的共享記憶體區標示符

shmaddr:將共享記憶體對映到指定地址(若為NULL,則表示由系統自動完成對映)

shmflg:預設0:共享記憶體只讀

函式返回值

成功:對映後的地址

出錯:-1

        引數shmid 指定要引入的共享記憶體,引數 addr 和 flag 組合說明要引入的地址值,通常將 shmaddr 設定為NULL ,shmflag為0;

3)分離

      當程序對共享記憶體段的操作完成後,應呼叫 shmdt 函式,作用是將指定的共享記憶體段從當前程序空間中脫離出去,函式原型如下:

所需標頭檔案

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/shm.h>

函式原型 int shmdt(const void *shmaddr);
函式引數

shmaddr:共享記憶體對映後的地址

函式返回值

成功:0

出錯:-1

此函式僅用於將共享記憶體區域與程序的地址空間分離,並不刪除共享記憶體本身。引數addr是呼叫 shmat 函式時的返回值。

4)共享記憶體的控制

     由於共享記憶體這一特殊的資源型別,使它不同於普通的檔案,因此,系統需要為其提供專有的操作函式,其函式原型如下:

所需標頭檔案

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/shm.h>

函式原型 int shmctl(int shmid, int cmd, struct shmid_ds *buf);
函式引數

shmid:要操作的共享記憶體標示符

cmd: IPC_STAT (獲取物件屬性)

              IPC_SET(設定物件屬性)

              IPC_RMID(刪除物件)

buf:指定IPC_STAT/ IPC_SET 時用以儲存/設定屬性

函式返回值

成功:0

出錯:-1

下面是一個例項,兩個程序間實現共享記憶體進行通訊:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define BUFFER_SIZE 2048

int main()
{
	pid_t pid;
	int shmid;
	char *shm_addr;
	char flag[] = "WROTE";
	char buffer[BUFFER_SIZE];

	if((shmid = shmget(IPC_PRIVATE,BUFFER_SIZE,0666)) < 0)
	{
		perror("shmget");
		exit(-1);
	}
	else
	{
		printf("Create shared-memory:%d\n",shmid);
	}

	system("ipcs -m");
	pid = fork();
	if(pid < 0)
	{
		perror("fork error");
		exit(-1);
	}
	else if(pid == 0)
	{
		shm_addr = shmat(shmid,0,0);
		if(shm_addr == (void *)-1)
		{
			perror("Child:shmat");
			exit(-1);
		}
		else
		{
			printf("Child:Attach shared-memory:%p \n",shm_addr);
		}

		system("ipcs -m");

		while(strncmp(shm_addr,flag,strlen(flag)))
		{
			printf("Child:wait for enable data...\n");
			sleep(5);
		}

		strcpy(buffer,shm_addr + strlen(flag));
		printf("Child:shared-memory:%s\n",buffer);

		if((shmdt(shm_addr)) < 0)
		{
			perror("shmdt");
			exit(-1);
		}
		else
		{
			printf("Child: Deattach shared-memory\n");
		}
		system("ipcs -m");

		if(shmctl(shmid,IPC_RMID,NULL) == -1)
		{
			perror("Child:shmctl(IPC_RMID)");
			exit(-1);
		}
		else
		{
			printf("Delete shmared-memory\n");
		}

		system("ipcs -m");
	}
	else
	{
		if((shm_addr = shmat(shmid,0,0)) == (void *)-1)
		{
			perror("parent:shmat");
			exit(-1);
		}
		else
		{
			printf("Parent:Attach shmared-memory:%p\n",shm_addr);
		}

		sleep(1);
		printf("\nInput some string:\n");
		fgets(buffer,BUFFER_SIZE,stdin);
		strncpy(shm_addr + strlen(flag),buffer,strlen(buffer));
		strncpy(shm_addr,flag,strlen(flag));

		if((shmdt(shm_addr)) < 0)
		{
			perror("Parent:shmdt");
			exit(-1);
		}
		else
		{
			printf("Parent : Deattach shared-memory\n");
		}

		system("ipcs -m");
		waitpid(pid,NULL,0);
		printf("Finsihed\n");
	}
	
	return 0;
}

執行結果如下:

[email protected]:~/qiang/shm$ ./shm 
Create shared-memory:196613

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x00000000 0          fs         700        76800      2          dest         
0x00000000 32769      fs         700        1843200    2          dest         
0x00000000 65538      fs         700        20196      2          dest         
0x00000000 98307      fs         700        17028      2          dest         
0x00000000 131076     fs         700        19800      2          dest         
0x00000000 196613     fs         666        2048       0                       

Parent:Attach shmared-memory:0xb773d000
Child:Attach shared-memory:0xb773d000 

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x00000000 0          fs         700        76800      2          dest         
0x00000000 32769      fs         700        1843200    2          dest         
0x00000000 65538      fs         700        20196      2          dest         
0x00000000 98307      fs         700        17028      2          dest         
0x00000000 131076     fs         700        19800      2          dest         
0x00000000 196613     fs         666        2048       2                       

Child:wait for enable data...

Input some string:
xiaoqiang
Parent : Deattach shared-memory

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x00000000 0          fs         700        76800      2          dest         
0x00000000 32769      fs         700        1843200    2          dest         
0x00000000 65538      fs         700        20196      2          dest         
0x00000000 98307      fs         700        17028      2          dest         
0x00000000 131076     fs         700        19800      2          dest         
0x00000000 196613     fs         666        2048       1                       

Child:shared-memory:xiaoqiang

Child: Deattach shared-memory

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x00000000 0          fs         700        76800      2          dest         
0x00000000 32769      fs         700        1843200    2          dest         
0x00000000 65538      fs         700        20196      2          dest         
0x00000000 98307      fs         700        17028      2          dest         
0x00000000 131076     fs         700        19800      2          dest         
0x00000000 196613     fs         666        2048       0                       

Delete shmared-memory

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x00000000 0          fs         700        76800      2          dest         
0x00000000 32769      fs         700        1843200    2          dest         
0x00000000 65538      fs         700        20196      2          dest         
0x00000000 98307      fs         700        17028      2          dest         
0x00000000 131076     fs         700        19800      2          dest         

Finsihed
[email protected]:~/qiang/shm$

通過結果,不斷的呼叫 system ,可以看到共享記憶體區的變化;

二、訊號量

       訊號燈(semaphore),也叫訊號量,它是不同程序間或一個給定程序內部不同執行緒間同步的機制。

訊號燈種類:1)posix 有名訊號燈 2)posix 基於記憶體的訊號燈(無名訊號燈) 3)System V 訊號燈 (IPC物件);

訊號燈:

1)二值訊號燈:值為 0 或 1。與互斥鎖類似,資源可用時值為1,不可用時值為 0;

2)計數訊號燈:值在 0 到 n 之間。用來統計資源,其值代表可用資源數;

等待操作時等待訊號燈的值變為大於0,然後將其減一;而釋放操作則相反,用來喚醒等待資源的程序或者執行緒;

 

事實上,在訊號量的實際應用中,是不能單獨定義一個訊號量的,而只能定義一個訊號量集,其中包含一組訊號量,同一訊號量集中的訊號量使用同一個引用ID,這樣的設定是為了多個資源和同步操作的需要。每個訊號量集都有一個與之對應結構,一種記錄了訊號量集的各種資訊,該結構的定義如下:

struct semid_ds
{           
	struct ipc_perm  sem_perm  //Operation permission structure.
	unsigned short   sem_nsems //Number of semaphores in set.
	time_t           sem_otime //Last semop            
	time_t           sem_ctime //Last time changed by semctl
}

sem結構記錄一個訊號量的資訊,其定義如下:

struct sem
{          
	unsigned short  semval   //Semaphore value.
	pid_t           sempid   //Process ID of last operation.
	unsigned short  semncnt  //Number of processes waiting for semval to become greater than current value.
	unsigned short  semzcnt  //Number of processes waiting for semval to become 0.
}

下面是訊號量操作有關的函式呼叫:

函式說明:

在Linux 系統中,使用訊號量通常分為以下幾個步驟:

1)建立訊號量或獲得系統已存在的訊號量,此時需要呼叫 semget() 函式。不同程序通過使用同一個訊號鍵值來獲得同一個訊號量;

2)初始化訊號量,此時使用 senctl() 函式 SETVAL 操作。當使用二維訊號量時,通常將訊號量初始化為1;

3)進行訊號量的PV操作,此時呼叫 semop() 函式。這一步是實現程序之間的同步和互斥的核心工作部分;

4)如果不需要訊號量,則從系統中刪除它,此時使用semctl() 函式的IPC_RMID 操作。此時需要注意,在程式中不應該出現對已經被刪除的訊號量的操作;

下面是具體說明:

1、建立或開啟訊號量集

使用函式 semget 可以建立或者獲得一個訊號量集ID,函式原型如下:

所需標頭檔案

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>

函式原型 int semget(key_t key, int nsems, int semflg );
函式引數

key :和訊號燈集關聯的key 值

nsems:訊號燈集中包含的訊號燈數目

semflg:訊號燈集的訪問許可權,通常為IPC_CREAT|0666

函式返回值

成功:訊號燈集ID

出錯:-1

此函式可以用於建立或開啟一個訊號量集。其中,引數key 表示要建立或開啟的訊號量集對於的鍵值。引數 nsems 表示建立的訊號量集中包含的訊號量的個數,此引數只在建立一個新的訊號量集時有效。引數flag表示呼叫函式的操作型別,也可以用於設定訊號量集的訪問許可權,兩者通過邏輯或表示。呼叫函式semget 的作用由引數key和flag 決定。

     另外,當semget 成功建立一個新的訊號量集時,它相應的semid_ds結構被初始化。ipc_perm 結構中成員被設定成相應的值 ,sem_nsems設定為函式引數nsems的值,sem_otime被設定為0,sem_ctime 設定為系統當前時間。

2、對訊號量集的操作

函式semop 用以操作一個訊號量集,函式原型如下:

所需標頭檔案

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>

函式原型 int semop(int semid,struct sembuf  *opsptr,size_t nops);
函式引數

semid:訊號燈集ID

struct sembuf 結構體每一個元素表示一個操作;

nops:要操作的訊號燈的個數

函式返回值

成功:0

出錯:-1


此函式是一個原子操作,一旦執行就將執行陣列中所有的操作;

結構體sembuf 用來說明所要執行的操作,其定義如下:

struct sembuf
{
	unsigned short sem_num; //要操作的訊號燈的編號
	short sem_op;   //  0: 等待,知道訊號燈的值變為0
                        //  1: 釋放資源,V操作 
                        // -1: 分配資源,P操作
	short sem_flg; //0,IPC_NOWAIT,SEM_UNDO
}


3、訊號量集的控制

和共享記憶體的控制一樣,訊號量集也有自己的專屬控制函式 semctl ,函式原型如下:

所需標頭檔案

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>

函式原型 int semctl(int semid, int semnum, int cmd, union semun arg);
函式引數

semid:訊號燈集ID

semnum:要修改的訊號燈編號

cmd :GETVAL:獲取訊號燈的值

             SETVAL:設定訊號燈的值

             IPC_RMID:從系統中刪除訊號燈集合

函式返回值

成功:0

出錯:-1

引數cmd 定義函式所要進行的操作,其取值及表達的意義與引數arg 的設定有關,最後一個引數arg 是一個聯合體(union),其定義如下:

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

下面是個應用例項:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define DELAY_TIME 3

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 init_sem(int sem_id,int init_value);
int del_sem(int sem_id);
int sem_p(int sem_id);
int sem_v(int sem_id);

int init_sem(int sem_id,int init_value)
{
	union semun sem_union;
	sem_union.val = init_value;
	if(semctl(sem_id, 0, SETVAL, sem_union) == -1)
	{
		perror("Initialize semaphore");
		exit(-1);
	}

	return 0;
}

int del_sem(int sem_id)
{
	union semun sem_union;
	if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
	{
		perror("Delete semaphore");
		return -1;
	}
}

int sem_p(int sem_id)
{
	struct sembuf sem_b;
	sem_b.sem_num = 0;
	sem_b.sem_op = -1;
	sem_b.sem_flg = SEM_UNDO;

	if(semop(sem_id,&sem_b,1) == -1)
	{
		perror("P operation");
		return -1;
	}

	return 0;
}

int sem_v(int sem_id)
{
	struct sembuf sem_b;
	sem_b.sem_num = 0;
	sem_b.sem_op = 1;
	sem_b.sem_flg = SEM_UNDO;

	if(semop(sem_id,&sem_b,1) == -1)
	{
		perror("V operation");
		return -1;
	}

	return 0;
}

int main()
{
	pid_t pid;
	int sem_id;

	sem_id = semget(ftok(".",'a'), 1, 0666|IPC_CREAT);
	init_sem(sem_id,0);

	pid = fork();
	if(pid < 0)
	{
		perror("fork fails");
		exit(-1);
	}
	else if(pid == 0)
	{
		printf("Child process will wait for some seconds...\n");
		sleep(DELAY_TIME);
		printf("The returned value is %d in the child process(PID = %d)\n",
				pid,getpid());
		sem_v(sem_id);
	}
	else
	{
		sem_p(sem_id);
		printf("The returned value is %d in the father process(PID = %d)\n",
				pid,getpid());
		sem_v(sem_id);
		del_sem(sem_id);
	}

	return 0;
}

執行結果如下:

[email protected]:~/qiang/sem$ ./sem 
Child process will wait for some seconds...
The returned value is 0 in the child process(PID = 2882)
The returned value is 2882 in the father process(PID = 2880)
[email protected]:~/qiang/sem$ 

相關推薦

Linux 系統應用程式設計——程序通訊

        在前面,我們學習了傳統的程序間通訊方式——無名管道(pipe)、有名管道(fifo)和訊號(signal)。         下面我們來學習 System V  IPC 物件: 1、共享記憶體(share memory); 2、訊號燈(semaohore);

Linux 系統應用程式設計——程序通訊

       現在再Linux應用較多的程序間通訊方式主要有以下幾種: 1)無名管道(pipe)及有名管道(fifo):無名管道可用於具有親緣關係程序間的通訊;有名管道除具有管道相似的功能外,它還允許無親緣關係程序使用; 2)訊號(signal):訊號是在軟體層次上對中斷機

Linux程序通訊IPC方式總結

程序間通訊概述 程序通訊的目的 資料傳輸  一個程序需要將它的資料傳送給另一個程序,傳送的資料量在一個位元組到幾M位元組之間 共享資料  多個程序想要操作共享資料,一個程序對共享資料 通知事件 一個程序需要向另一個或一組程序傳送訊息,通知它(它們)

Linux 多工程式設計——程序通訊:無名管道(PIPE)

管道的概述 管道也叫無名管道,它是是 UNIX 系統 IPC(程序間通訊) 的最古老形式,所有的 UNIX 系統都支援這種通訊機制。 無名管道有如下特點: 1、半雙工,資料在同一時刻只能在一個方向上流動。 2、資料只能從管道的一端寫入,從另一端讀出。 3、寫入管道中的資料遵循先入先出

Linux 多工程式設計——程序通訊:訊號中斷處理

什麼是訊號? 訊號是 Linux 程序間通訊的最古老的方式。訊號是軟體中斷,它是在軟體層次上對中斷機制的一種模擬,是一種非同步通訊的方式 。訊號可以導致一個正在執行的程序被另一個正在執行的非同步程序中斷,轉而處理某一個突發事件。 “中斷”在我們生活中經常遇到,譬如,我正在房間裡打遊戲,

Linux 多工程式設計——程序通訊概述

程序是一個獨立的資源分配單元,不同程序(這裡所說的程序通常指的是使用者程序)之間的資源是獨立的,沒有關聯,不能在一個程序中直接訪問另一個程序的資源(例如開啟的檔案描述符)。 但是,程序不是孤立的,不同的程序需要進行資訊的互動和狀態的傳遞等,因此需要程序間通訊( IPC:Inter Process

Linux 程序通訊訊號量

1 訊號量概述 訊號量和其他IPC不同,並沒有在程序之間傳送資料,訊號量用於多程序在存取共享資源時的同步控制就像交通路口的紅路燈一樣,當訊號量大於0,表示綠燈允許通過,當訊號量等於0,表示紅燈,必須停下來等待綠燈才能通過。 程序間的互斥關係與同步關係存在的根源在於臨界資

Linux 程序通訊IPC的特性

1.識別符號和鍵 每個核心中的IPC結構(訊息佇列、訊號量或共享儲存段)都用一個非負整數的識別符號 (identifier)加以引用。 例如,要向一個訊息佇列傳送訊息或者從一個訊息佇列取訊息,只需要知道其佇列識別符號。 當一個IPC結構被建立,然後又被刪除時,與這種結構

Linux 程序通訊共享記憶體

可以說, 共享記憶體是一種最為高效的程序間通訊方式, 因為程序可以直接讀寫記憶體, 不需要任何資料的複製。 為了在多個程序間交換資訊, 核心專門留出了一塊記憶體區, 這段記憶體區可以由需要訪問的程序將其對映到自己的私有地址空間。 因此, 程序就可以直接讀寫這一記憶體區而不需要

linux程序通訊IPC小結

linux IPC型別 1、匿名管道 2、命名管道 3、訊號 4、訊息佇列 5、共享記憶體 6、訊號量 7、Socket 1、匿名管道 過程: 1、管道實質是一個核心緩衝區,先進先出(佇列)讀取緩衝區記憶體資料 2、一個數據只能讀一次,讀完後在緩衝區就不存在

Linux程序通訊訊號量

雖然本文是記錄使用訊號量保證程序的同步與互斥的,但是其實也可以看做是程序之間的通訊問題,為了與前面的保持一致,所以還是叫做 Linux程序間通訊了 (強迫症...) 訊號量 基本概念 程序間通訊的方式有管道、訊息佇列、共享記憶體這些都是程序間的資訊通訊,而訊號量可以理解為程序使用的臨界資源的狀態說明,訊

Linux環境程序通訊: 共享記憶體(轉)

轉自http://www.ibm.com/developerworks/cn/linux/l-ipc/part5/index2.html, 作者:鄭彥興系統呼叫mmap()通過對映一個普通檔案實現共享記憶體。系統V則是通過對映特殊檔案系統shm中的檔案實現程序間的共享記憶體通訊。也就是說,每個共享記憶體區域對

Linux環境程序通訊: 共享記憶體(轉)

轉自http://www.ibm.com/developerworks/cn/linux/l-ipc/part5/index1.html, 作者:鄭彥興採用共享記憶體通訊的一個顯而易見的好處是效率高,因為程序可以直接讀寫記憶體,而不需要任何資料的拷貝。對於像管道和訊息佇列等通訊方式,則需要在內 核和使用者空間

Linux環境程序通訊: 訊號(轉)

訊號本質訊號是在軟體層次上對中斷機制的一種模擬,在原理上,一個程序收到一個訊號與處理器收到一箇中斷請求可以說是一樣的。訊號是非同步的,一個程序不必通過任何操作來等待訊號的到達,事實上,程序也不知道訊號到底什麼時候到達。訊號是程序間通訊機制中唯一的非同步通訊機制,可以看作是非同步通知,通知接收訊號的程序有哪些事

Linux 環境程序通訊 套介面(轉)

轉自https://www.ibm.com/developerworks/cn/linux/l-ipc/part6/, 作者:鄭彥興一個套介面可以看作是程序間通訊的端點(endpoint),每個套介面的名字都是唯一的(唯一的含義是不言而喻的),其他程序可以發現、連線並且 與之通訊。通訊域用來說明套介面通訊的協

Linux環境程序通訊 訊息佇列(轉)

轉自http://www.ibm.com/developerworks/cn/linux/l-ipc/part3/, 作者:鄭彥興訊息佇列(也叫做報文佇列)能夠克服早期unix通訊機制的一些缺點。作為早期unix通訊機制之一的訊號能夠傳送的資訊量有限,後來雖然 POSIX 1003.1b在訊號的實時性方面作了

深刻理解Linux程序通訊IPC(轉)

序linux下的程序通訊手段基本上是從Unix平臺上的程序通訊手段繼承而來的。而對Unix發展做出重大貢獻的兩大主力 AT&T的貝爾實驗室及BSD(加州大學伯克利分校的伯克利軟體釋出中心)在程序間通訊方面的側重點有所不同。前者對Unix早期的程序間通訊手 段進行了系統的改進和擴充,形成了“system

Linux環境程序通訊 訊號燈(轉)

轉自http://www.ibm.com/developerworks/cn/linux/l-ipc/part4/, 作者:鄭彥興訊號燈與其他程序間通訊方式不大相同,它主要提供對程序間共享資源訪問控制機制。相當於記憶體中的標誌,程序可以根據它判定是否能夠訪問某些共享資源,同時,程序也可以修改該標誌。除了用於訪

Linux環境程序通訊: 訊號(轉)

從訊號傳送到訊號處理函式的執行完畢對於一個完整的訊號生命週期(從訊號傳送到相應的處理函式執行完畢)來說,可以分為三個重要的階段,這三個階段由四個重要事件來刻畫:訊號誕生;訊號在程序中註冊完畢;訊號在程序中的登出完畢;訊號處理函式執行完畢。相鄰兩個事件的時間間隔構成訊號生命週期的一個階段。 下面闡述四個事件的實

Linux環境程序通訊 管道及有名管道(轉)

管道是Linux支援的最初Unix IPC形式之一,具有以下特點:管道是半雙工的,資料只能向一個方向流動;需要雙方通訊時,需要建立起兩個管道;只能用於父子程序或者兄弟程序之間(具有親緣關係的程序);單獨構成一種獨立的檔案系統:管道對於管道兩端的程序而言,就是一個檔案,但它不是普通的檔案,它不屬於某種檔案系統,