進程通信2——共享內存,信號量

共享內存:
共享內存是被多個進程共享的一部分物理內存。共享內存是進程間共享數據的一種最快的方法,一個進程向共享內存區域寫入了數據,共享這個內存區域的所有進程就可以立刻看到其中的內容。
創建共享內存分爲兩步:
1、創建共享內存,使用shmget函數
2、映射共享內存,將這段創建的共享內存映射到具體的進程空間去,使用shmat函數。
案例:

typedef struct _shm
{
    int flag;
    char msg[256];
}SHM;

int main()
{
    // 1、創建或者獲取一個共享內存
    int shmid = shmget((key_t)1234, sizeof(SHM), 0666 | IPC_CREAT);
    if (shmid == -1)
    {
        perror ("shmget");
        return -1;
    }

    // 2、將共享內存映射到當前的進程空間
    SHM* pshm = (SHM*)shmat(shmid, NULL, 0);
    if(pshm == (SHM*)-1)
    {
        perror ("shmat");
        return -1;
    }

    strcpy (pshm->msg, "hello");

    // 解除共享內存映射,解除是值當前進程不能再使用共享內存
    shmdt(pshm);


    shmctl(shmid, IPC_RMID, NULL);


    return 0;
}

用函數shmget和函數shmat可以創建一個共享內存並將它映射到當前的進程空間,而函數shmdt則可以解除共享內存的映射,最後的函數shmctl則可以對共享內存進行控制,包括刪除共享內存的功能。

信號量:
爲了防止出現因多個程序同時訪問一個共享資源而引發的一系列問題,我們需要一種方法,它可以通過生成並使用令牌來授權,在任一時刻只能有一個執行進程訪問代碼的臨界區域。臨界區域是指執行數據更新的代碼需要獨佔式地執行。而信號量就可以提供這樣的一種訪問機制,讓一個臨界區同一時間只有一個進程在訪問它,也就是說信號量是用來調協進程對共享資源的訪問的。

信號量是一個特殊的變量,程序對其訪問都是原子操作,且只允許對它進行等待(即P(信號變量))和發送(即V(信號變量))信息操作。最簡單的信號量是隻能取0和1的變量,這也是信號量最常見的一種形式,叫做二進制信號量。而可以取多個正整數的信號量被稱爲通用信號量。這裏主要討論二進制信號量。

由於信號量只能進行兩種操作等待和發送信號,即P(sv)和V(sv),他們的行爲是這樣的:
P(sv):如果sv的值大於零,就給它減1;如果它的值爲零,就掛起該進程的執行
V(sv):如果有其他進程因等待sv而被掛起,就讓它恢復運行,如果沒有進程因等待sv而掛起,就給它加1.

信號量的創建:
int semget(key_t key, int num_sems, int sem_flags);
第一個參數key是整數值(唯一非零),不相關的進程可以通過它訪問一個信號量,它代表程序可能要使用的某個資源,程序對所有信號量的訪問都是間接的,程序先通過調用semget函數並提供一個鍵,再由系統生成一個相應的信號標識符(semget函數的返回值),只有semget函數才直接使用信號量鍵,所有其他的信號量函數使用由semget函數返回的信號量標識符。如果多個程序使用相同的key值,key將負責協調工作。

第二個參數num_sems指定需要的信號量數目,它的值幾乎總是1。

第三個參數sem_flags是一組標誌,當想要當信號量不存在時創建一個新的信號量,可以和值IPC_CREAT做按位或操作。設置了IPC_CREAT標誌後,即使給出的鍵是一個已有信號量的鍵,也不會產生錯誤。而IPC_CREAT | IPC_EXCL則可以創建一個新的,唯一的信號量,如果信號量已存在,返回一個錯誤。

semget函數成功返回一個相應信號標識符(非零),失敗返回-1.
案例:

// 創建一個信號量
int sem_id = semget((key_t)5678, 1, 0666 | IPC_CREAT);

信號量初始化即P,V操作:

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 sem_init(int sem_id)
{
    union semun sem;
    sem.val = 1;
    int ret = semctl(sem_id, 0, SETVAL, sem);
    return ret;
}

// 信號量的 P 操作
int sem_p(int sem_id)
{
    struct sembuf sem;
    sem.sem_num = 0;
    sem.sem_op  = -1;
    sem.sem_flg = SEM_UNDO;
    int ret = semop(sem_id, &sem,1);

    return ret;
}

// 信號量的 v 操作
int sem_v(int sem_id)
{
    struct sembuf sem;
    sem.sem_num = 0;
    sem.sem_op  = 1;
    sem.sem_flg = SEM_UNDO;
    int ret = semop(sem_id, &sem,1);

    return ret;
}

這裏我們用到了一個函數semop來進行信號量的P, V操作。這其中用到了一個結構體sembuf,sembuf結構的定義如下:
struct sembuf{
short sem_num;//除非使用一組信號量,否則它爲0
short sem_op;//信號量在一次操作中需要改變的數據,通常是兩個數,一個是-1,即P(等待)操作,
//一個是+1,即V(發送信號)操作。
short sem_flg;//通常爲SEM_UNDO,使操作系統跟蹤信號,
//並在進程沒有釋放該信號量而終止時,操作系統釋放信號量
};
最後可以銷燬信號量:

int sem_del(int sem_id)
{
    int ret = semctl(sem_id, 0, IPC_RMID);
    return ret;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章