1. 程式人生 > >程序通訊-訊號量semaphore

程序通訊-訊號量semaphore

semaphore:訊號量 標頭檔案:include <sys/sem.h>
int semget(key_t key,int num_sems,int sem_flags);建立或獲取一個訊號量組:若成功返回訊號量集ID,失敗返回-1
功能:建立一個新訊號量或取得一個已有訊號量的鍵。
引數:key:不相關的程序可以通過該整數值訪問同一個訊號量。
         num_sems:指定需要的訊號量的數目。
         sem_flags:一組標誌。
返回:成功時返回一個正數;失敗返回-1.
    
int semop(int semid, struct sembuf semoparray[],size_t numops);對訊號量組進行操作,改變訊號量的值:成功返回0,失敗返回-1 
int semop(int sm_id, struct sembuf *sem_ops, size_t num_sem_ops);  
功能:用於改變訊號量的值。
引數:sm_id:由semget返回的訊號量識別符號。
         sem_ops:指向要操作的結構陣列的指標。
         num_sem_ops:要操作的結構陣列的元素個數。
返回:成功返回0;失敗返回-1.
    
int semctl(int semid, int sem_num, int cmd,  union semun arg);控制訊號量的相關資訊
功能:用來直接控制訊號量資訊。
引數:sem_id:由semget返回的訊號量識別符號。
         sem_num:訊號量編號。
         comd:要採取的操作,一些命令可以查資料。
返回:成功返回0;失敗返回-1.
union semun {
   short val;          //SETVAL用的值
   struct semid_ds* buf; //IPC_STAT、IPC_SET用的semid_ds結構
   unsigned short* array; //SETALL、GETALL用的陣列值
   struct seminfo *buf;   //為控制IPC_INFO提供的快取
};


當semget建立新的訊號量集合,必須指定集合中訊號量的個數(num_sems),通常為1,如果引用一個現有的集合,則將num_sems指定為0 
struct sembuf 
{
    short sem_num; // 訊號量組中對應的序號,0~sem_nums-1
    short sem_op;  // 訊號量值在一次操作中的改變數
    short sem_flg; // IPC_NOWAIT, SEM_UNDO
}


訊號量semaphore,它是一個計數器,訊號量用於實現程序間的互斥與同步,而不是用於儲存程序間通訊資料。
1.特點
訊號量用於程序間同步,若要在程序間傳遞資料需要結合共享記憶體。
訊號量基於作業系統的 PV 操作,程式對訊號量的操作都是原子操作。
每次對訊號量的 PV 操作不僅限於對訊號量值加 1 或減 1,而且可以加減任意正整數。
支援訊號量組。
2.原型
最簡單的訊號量是隻能取0和1的變數,這也是訊號量最常見的一種形式,叫做二值訊號量(Binary Semaphore)。
而可以取多個正整數的訊號量被稱為通用訊號量。
在semctl函式中的命令有多種,這裡就說兩個常用的:
SETVAL:用於初始化訊號量為一個已知的值,所需要的值作為聯合semun的val成員來傳遞,
在訊號量第一次使用之前需要設定訊號量。
IPC_RMID:刪除一個訊號量集合,如果不刪除訊號量,它將繼續在系統中存在,即使程式已經退出,

它可能在你下次執行此程式時引發問題,而且訊號量是一種有限的資源。

#include<stdio.h>
#include<stdlib.h>
#include<sys/sem.h>

//聯合體,用於訊號量semctl初始化
union semun
{
    int              val; /*for SETVAL*/
    struct semid_ds *buf;
    unsigned short  *array;
};

//初始化訊號量
int init_sem(int sem_id, int value)
{
    union semun tmp;
    tmp.val = value;
    if(semctl(sem_id, 0, SETVAL, tmp) == -1)
    {
        perror("Init Semaphore Error");
        return -1;
    }
    return 0;
}

    
//P操作:若訊號量值為1,獲取資源並將訊號量值-1,若訊號量值為0,程序掛起等待
int sem_p(int sem_id)
{
    struct sembuf sbuf;
    sbuf.sem_num = 0; //序號
    sbuf.sem_op = -1; //P操作
    sbuf.sem_flg = SEM_UNDO;

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

  
//V操作:釋放資源並將訊號量值+1,如果有程序正在掛起等待,則喚醒它們
int sem_v(int sem_id)
{
    struct sembuf sbuf;
    sbuf.sem_num = 0; //序號
    sbuf.sem_op = 1;  //V操作
    sbuf.sem_flg = SEM_UNDO;

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

//刪除訊號量集
int del_sem(int sem_id)
{
    union semun tmp;
    if(semctl(sem_id, 0, IPC_RMID, tmp) == -1)
    {
        perror("Delete Semaphore Error");
        return -1;
    }
    return 0;
}


int main()
{
    int sem_id;  // 訊號量集ID
    key_t key;  //ID號
    pid_t pid;
/************************************************************************
系統建立IPC通訊(訊息佇列.訊號量和共享記憶體)時必須指定一個ID值,該id值通過ftok函式得到
原型:key_t ftok(const char * fname, int id)
引數:fname是檔案路徑一般取當前目錄,id是子ID號
返回值:一般將檔案的索引節點取出加上id號為返回值 檔案的索引節點ls -a檢視
eg:如指定檔案的索引節點號為65538,換算成16進製為0x010002,而你指定的ID值為38,換算成16進位制
為0x26,則最後的key_t返回值為0x26010002。
*************************************************************************/
    if((key = ftok(".", 'z')) < 0)// 獲取key值
    {
        perror("ftok error");
        exit(1);
    }

    //建立訊號量集,其中只有一個訊號量
    if((sem_id = semget(key, 1, IPC_CREAT|0666)) == -1)
    {
        perror("semget error");
        exit(1);
    }

    //初始化:初值設為0資源被佔用
    init_sem(sem_id, 0);

    if((pid = fork()) == -1)
        perror("Fork Error");
    else if(pid == 0) //子程序
    {
        sleep(2);
        printf("Process child: pid=%d\n", getpid());
        sem_v(sem_id);  //釋放資源
    }
    else  //父程序
    {
        sem_p(sem_id);   //等待資源
        printf("Process father: pid=%d\n", getpid());
        sem_v(sem_id);   //釋放資源
        del_sem(sem_id); //刪除訊號量集
    }
    return 0;
}