Linux進程間通信之信號量(semaphore)、消息隊列(Message Queue)和共享內存(Share Memory)


System V 進程通信方式:信號量(semaphore)、消息隊列(Message Queue)和共享內存(Share Memory)

 

信號量

信號量(semaphore)實際是一個整數,它的值由多個進程進行測試(test)和設置(set)。就每個進程所關心的測試和設置操作而言,這兩個操作是不可中斷的,或稱“原子”操作,即一旦開始直到兩個操作全部完成。測試和設置操作的結果是:信號量的當前值和設置值相加,其和或者是正或者為負。根據測試和設置操作的結果,一個進程可能必須睡眠,直到有另一個進程改變信號量的值。

信號量可用來實現所謂的“臨界區”的互斥使用,臨界區指同一時刻只能有一個進程執行其中代碼的代碼段。為了進一步理解信號量的使用,下面我們舉例說明。

假設你有很多相互協作的進程,它們正在讀或寫一個數據文件中的記錄。你可能希望嚴格協調對這個文件的存取,於是你使用初始值為1的信號量,在這個信號量上實施兩個操作,首先測試並且給信號量的值減1,然后測試並給信號量的值加1。當第一個進程存取文件時,它把信號量的值減1,並獲得成功,信號量的值現在變為0,這個進程可以繼續執行並存取數據文件。但是,如果另外一個進程也希望存取這個文件,那么它也把信號量的值減1,結果是不能存取這個文件,因為信號量的值變為-1。這個進程將被掛起,直到第一個進程完成對數據文件的存取。當第一個進程完成對數據文件的存取,它將增加信號量的值,使它重新變為1,現在,等待的進程被喚醒,它對信號量的減1操作將獲得成功。

詳細介紹見 http://www.cnblogs.com/biyeymyhjob/archive/2012/07/21/2602015.html

 

消息隊列

消息隊列也稱為報文隊列,消息隊列是隨內核持續的,只有在內核重起或顯示刪除一個消息隊列時,該消息隊列才會真正刪除 系統中記錄消息隊列的數據結構struct ipc_ids msg_ids位於內核中,系統中所有消息隊列都可以在結構msg_ids中找到訪問入口

消息隊列其實就是一個消息的鏈表,每個消息隊列有一個隊列頭,稱為struct msg_queue,這個隊列頭描述了消息隊列的key值,用戶ID,組ID等信息,但它存於內核中而結構體struct msqid_ds能夠返回或設置消息隊列的信息,這個結構體位於用戶空間中,與msg_queue結構相似消息隊列允許一個或多個進程向它寫入或讀取消息,消息隊列是消息的鏈表。

消息是按消息類型訪問,進程必須指定消息類型來讀取消息,同樣,當向消息隊列中寫入消息時也必須給出消息的類型,如果讀隊列使用的消息類型為0,則讀取隊列中的第一條消息。

內核空間的結構體msg_queue描述了對應key值消息隊列的情況,而對應於用戶空間的msqid_ds這個結構體,因此,可以操作msgid_ds這個結構體來操作消息隊列。

具體詳見 http://www.cnblogs.com/biyeymyhjob/archive/2012/08/04/2623323.html

 

共享內存

共享內存是運行在同一台機器上的進程間通信最快的方式,因為數據不需要在不同的進程間復制。通常由一個進程創建一塊共享內存區,其余進程對這塊內存區進行讀寫。共享內存往往與其它通信機制,如信號量結合使用,來達到進程間的同步及互斥。

函數原型說明見:http://www.cnblogs.com/biyeymyhjob/archive/2012/08/04/2623323.html

程序實例:參見http://blog.csdn.net/yangzhongxuan/article/details/7925750

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <error.h>

#define SIZE 1024

int main()
{
    int shmid ;
    char *shmaddr ;
    struct shmid_ds buf ;
    int flag = 0 ;
    int pid ;

    //0(IPC_PRIVATE):會建立新共享內存對象,IPC_CREAT與IPC對象存取權限(如0600)進行|運算來確定信號量集的存取權限
    shmid = shmget(IPC_PRIVATE, SIZE, IPC_CREAT|0600 ) ;   
    if ( shmid < 0 )
    {
        perror("get shm  ipc_id error") ;
        return -1 ;
    }

    pid = fork() ;
    if ( pid == 0 )
    {
        shmaddr = (char *)shmat( shmid, NULL, 0 ) ;      //直接指定為NULL讓內核自己決定一個合適的地址位置
        if ( (int)shmaddr == -1 )
        {
            perror("shmat addr error") ;
            return -1 ;
        }
        strcpy( shmaddr, "Hi, I am child process!\n") ;

        //與shmat函數相反,shmdt是用來斷開與共享內存附加點的地址,禁止本進程訪問此片共享內存
        shmdt( shmaddr ) ;                               

        return  0;
    } else if ( pid > 0) {
        sleep(3 ) ;

        //完成對共享內存的控制,得到共享內存的狀態,把共享內存的shmid_ds結構復制到buf中
        flag = shmctl( shmid, IPC_STAT, &buf) ;      
        if ( flag == -1 )
        {
            perror("shmctl shm error") ;
            return -1 ;
        }
        printf("shm_segsz =%d bytes\n", buf.shm_segsz ) ;
        printf("parent pid=%d, shm_cpid = %d \n", getpid(), buf.shm_cpid ) ;
        printf("chlid pid=%d, shm_lpid = %d \n",pid , buf.shm_lpid ) ;

        shmaddr = (char *) shmat(shmid, NULL, 0 ) ;
        if ( (int)shmaddr == -1 )
        {
            perror("shmat addr error") ;
            return -1 ;
        }

        printf("%s", shmaddr) ;
        shmdt( shmaddr ) ;
        shmctl(shmid, IPC_RMID, NULL) ;   //IPC_RMID:刪除這片共享內存

    }
    else
    {
        perror("fork error") ;
        shmctl(shmid, IPC_RMID, NULL) ;
    }

    return 0 ;
}


PS:

系統建立IPC通訊(如消息隊列、共享內存時)必須指定一個ID值 。通常情況下,該id值通過ftok函數得到
ftok原型如下:

key_t ftok( char * fname, int id )

參數說明:

                fname就時您指定的文檔名            id是子序號。

返回值:

                在一般的UNIX實現中,是將文檔的索引節點號取出,前面加上子序號得到key_t的返回值

如指定文檔的索引節點號為65538,換算成16進制為0x010002,而您指定的ID值為38,換算成16進制   為 0x26,則最后的key_t返回值為0x26010002。

(查詢文檔索引節點號的方法是: ls -i)

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM