1. 程式人生 > >System V 訊號量

System V 訊號量



訊號量概念:

1.二值訊號量:值為0或者1。若互斥鎖就是這種

2.計數訊號量:值為0~N之間的訊號量

3.計數訊號量集:就是一個或多個訊號量構成一個集合

System V訊號量就是計數訊號量集,而Posix訊號量則是單個計數訊號量

=======================================================

系統中核心維護著每個訊號量集,其維護的資訊結構如下:

struct semid_ds{

    struct  ipc_perm  sem_perm;//操作許可權結構

    struct  sem*       sem_base;//指向訊號量集合的指標

    ushort                  sem_nsems;//訊號量集合中訊號量個數

    time_t                  sem_otime;//上一次semop()操作時間

    time_t                  sem_ctime;//建立時間或者上一次IPC設定的時間

};

struct sem{

    ushort_t    semval;//訊號量值

    shortsempid;//上次成功操作semop的程序ID

    ushort_t    semncnt;//等待semval大於當前值的執行緒數

    ushort_t    semzcnt;//等待semval等於0的執行緒數

};

*注意這個結構體是由核心維護的,應用程式編寫用不到這個結構體

======================================================

函式:

1.semget函式:建立一個訊號量集或者訪問一個已經存在的訊號量集

    int semget(key_t key,int nsems,int oflag);

    返回一個訊號量集的標示符,若出錯返回-1.

    引數nsems指定建立集合中訊號量的個數,若僅僅是訪問該訊號量集,則指定為0。在建立完成之後不能對其進行修改。

    oflag為IPC_CREAT,建立訊號量集,還可與IPC_EXCL一起用,在一起用時,若該集合已經建立,則返回一個錯誤EEXIST。

    IPC_CREAT常與SEM_R(讀)、SEM_A(寫)一起用,指定該訊號集可進行讀寫操作。(SEM_R和SEM_A可能沒有定義,需要自己進行定義,其值為0400、0200)

2.semop函式:對訊號量進行操作

    int semop(int semid, struct sembuf* optstr, size_t nops);

    optstr為指向下面的結構陣列的指標:

    struct sembuf{

         short  sem_num;//訊號集的第幾個訊號量,從0開始

         short  sem_op;//訊號量運算元,其數可大於0、等於0,還可以小於0

         short  sem_flg;//操作標誌

    };

    * 該結構體僅能保證有上述的成員,它還可能包含其他的成員。

    nops指示optstr陣列中有幾個成員。

    關於semop的值:

    2.1  大於0   其值加到semval上。表示釋放其控制的資源。

           (若指定了SEM_UNDO,則從相應訊號量的semadj值中減去sem_op的值)

    2.2  等於0   表示呼叫者希望semval為0,若為0,則立即返回,否則阻塞等待。

           若指定了IPC_NOWAIT標誌,不為0的時候也會立即返回

    2.3  小於0   表示希望等待到semval變為大於或等於sem_op的絕對值。 

3.semctl函式:對訊號量進行各種控制操作

     int  semctl(int sem_id, int semnum, int cmd, /*union semun arg*/);//第四個引數是根據第三引數的情況進行可選的

     union semun{//該聯合體需要自己在程式中定義

          int                                val;//訊號量值,僅在SETVAL時使用

          struct    semid_ds   *buf;//僅在IPC_SET和IPC_STAT使用,注意此處的結構體非前面介紹的核心管理的結構體,有一些不一樣

          ushort                        *array;//僅在SETALL和GETALL中使用,訊號量值陣列指標,設定和獲取訊號量集的訊號量值

     };

     semnum標示訊號集中的某個成員(從0開始)。

     cmd值:

     GETVAL:返回當前訊號量值semval

     SETVAL:設定當前訊號量值semval

     GETPID:獲取上次操作的程序PID

     GETNCNT:獲取正在等待semval大於當前值的執行緒數

     GETZCNT:獲取正在等待semval等於0的執行緒數

     GETALL:獲取指定訊號集內每個成員的semval值。通過arg.arrag指標返回

     SETALL:設定訊號集每個成員semval的值

     IPC_RMID:刪除訊號量集(注意是刪除集合,不是刪除其中的某個訊號量成員)

     IPC_SET:設定訊號量集的許可權

     IPC_STAT:返回訊號集當前訊號量的狀態

=========================================================

=========================================================

/**********************************
*Date:Fri Apr 11 15:54:50 CST 2014
*System V訊號量的使用
***********************************/
#include <iostream>
extern "C"{
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/sem.h>
    #include <sys/time.h>
}
using namespace std;
//需要自己定義此聯合體
union semun {
    int               val;
    struct semid_ds   *buf;
    unsigned short    *array;
    struct seminfo    *__buf;
};
#define SEM_KEY   "SEM_KEY"
#define NUM_SEM    2
#define SEM_R      0400 //使用者讀
#define SEM_A      0200 //使用者寫
//獲取時間
string to_timeStr(time_t otime)
{
    struct tm* time = localtime(&otime);
    int yy = time->tm_year + 1900;
    int mm = time->tm_mon  + 1;
    int dd = time->tm_mday;
    int HH = time->tm_hour;
    int MM = time->tm_min;
    int ss = time->tm_sec;
    char strBuf[25] = {0};
    sprintf(strBuf,"%04d/%02d/%02d %02d:%02d:%02d",yy,mm,dd,HH,MM,ss);
    return strBuf;
}
int main(int argc,char* argv[])
{
    key_t key = ftok(SEM_KEY,0);
    if(key == -1){
        cerr << "failed to ftok,error:" << strerror(errno) << endl;
        return -1;
    }
    int semfd = semget(key,NUM_SEM,IPC_CREAT|SEM_R|SEM_A);//建立一個訊號集,該訊號集有兩個訊號量,訊號量可進行讀寫操作
    if(semfd == -1){
        cerr << "failed to create sem,error:" << strerror(errno) << endl;
        return -1;
    }
    else{
        cout << "success to create sem" << endl;
    }    
    //對訊號量集進行操作
    struct sembuf* buf = NULL;
    buf = (struct sembuf*)malloc(sizeof(struct sembuf)*NUM_SEM);//分配對訊號集進行操作的結構體空間
    //第一個訊號量操作結構體
    buf[0].sem_num = 0;
    buf[0].sem_op = 1;
    buf[0].sem_flg = IPC_NOWAIT;//不進行等待
    //第二個訊號量操作結構體
    buf[1].sem_num = 1;
    buf[1].sem_op = 2;
    buf[1].sem_flg = IPC_NOWAIT;//不進行等待
    int res = semop(semfd,buf,2);//陣列buf有兩個元素
    if(res == -1){
        cerr << "failed tp sem op,error:" << strerror(errno) << endl;
        return -1;
    }
    else{
        cout << "success to operator sem" << endl;
    }
    //對訊號量進行控制
    struct semid_ds semInfo;
    union semun arg;
    arg.buf = &semInfo;

    res = semctl(semfd,0,IPC_STAT,arg);//獲取第一個訊號量(下標從0開始)
    if(res == -1){
        cerr << "failed to get IPC stat,error:" << strerror(errno) << endl;
        return -1;
    }
    else{
        cout << "1st->nsems:" << arg.buf->sem_nsems << endl;
        cout << "1st->otime:" << to_timeStr(arg.buf->sem_otime) << endl;
    }
    res = semctl(semfd,1,IPC_STAT,arg);//獲取第二個訊號量
    if(res == -1){
        cerr << "failed to get IPC stat,error:" << strerror(errno) << endl;
        return -1;
    }
    else{
        cout << "2nd->nsems:" << arg.buf->sem_nsems << endl;
        cout << "2nd->otime:" << to_timeStr(arg.buf->sem_otime) << endl;
    }    
    //刪除訊號量集
    res = semctl(semfd,0,IPC_RMID);//注意IPC_RMID刪除的是訊號量集
    if(res == -1){
        cerr << "failed to remove sem,error:" << strerror(errno) << endl;
    }
    else{
        cout << "success to remove sem" << endl;
    }
    return 0;
}