1. 程式人生 > >unix程式設計——訊號量

unix程式設計——訊號量

訊號量我就不介紹了,因為這個之前學習作業系統的時候已經介紹過了

我們引入訊號量的主要目的就是為了解決共享資源在訪問時的同步問題

主要就是PV操作,這兩個字母均來自於希臘文字passerenvrijgeven

LINUX提供了一套操作訊號量的API,都定義在標頭檔案sys/sem.h

訊號量的建立

我們可以使用函式semget來建立訊號量,原型如下:(sem是訊號量semaphore的縮寫)

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);

和之前的訊息佇列的建立函式都是一個德行,存在則訪問,否則建立

引數nsems指定建立的訊號量的個數

引數semflg的低9位確定了訊號量屬組以及其他使用者的訪問許可權,還有一些位指定了訊號量的建立方式,其取值與含義如下所示:

引數 描述
IPC_CREAT 建立訊號量,如果訊號量已經存在,就開啟該訊號量
IPC_EXCL 與巨集IPC_CREAT一起使用,單獨使用無意義,此時只能建立一個核心中不存在的訊號量,如果要建立的訊號量已經存在,則會建立失敗

semget函式的一個例子:

int semid;
semid = semget(0x1234, 10, 0666|IPC_CREAT);

訊號量的修改

函式:semop函式名起的很同意理解semaphore operate

int semop(int sem_id,struct sembuf *sem_opa,size_t num_sem_ops);

sem_id就是我們上面使用semget函式的返回值

sem_opa的型別是一個結構體sembuf

struct sembuf{  
    short sem_num;	//除非使用一組訊號量,否則它為0  
    short sem_op;	//訊號量在一次操作中需要改變的資料,通常是兩個數
					//一個是-1,即P(等待)操作,  
                    //一個是+1,即V(傳送訊號)操作。  
    short sem_flg;	//通常為SEM_UNDO,使作業系統跟蹤訊號,  
                    //並在程序沒有釋放該訊號量而終止時,作業系統釋放訊號量  
}; 

補充一下結構體中sem_op的描述:

  • sem_op的值為正數
    • 該值會加到現有的訊號內含值中,通常用於釋放所控資源的使用權
  • sem_op的值為負數
    • 而其絕對值又大於訊號的現值,操作將會阻塞,直到訊號值大於或等於sem_op的絕對值,通常用於獲取資源的使用權
  • sem_op的值為0
    • 如果沒有設定IPC_NOWAIT,則呼叫該操作的程序或者執行緒將暫時睡眠,直到訊號量的值為0
  • 否則
    • 程序或者執行緒不會睡眠,函式返回錯誤EAGAIN**

訊號量的初始化和刪除

函式:semctl–>semaphore control

int semctl(int sem_id,int sem_num,int command,[union semun sem_union]);

引數sem_num是訊號量在訊號量集合中的編號,第一個訊號的編號是0

引數command有兩個取值:

  • SETVAL
    • 初始化訊號量
  • IPC_RMID
    • 刪除訊號量

引數sem_union可選,是一個聯合,結構如下:

union semun{  
	int val;				//一般用到的是val,表示要傳給訊號量的初始值 
 	struct semid_ds *buf;  
 	unsigned short *arry;  
}; 

這裡順便複習一波union和struct的區別

  • union中的成員的記憶體是共享的,這個理解起來也很容易,我們來看一段程式碼:

     union myun  
     {  
          struct { int x; int y; int z; }u;  
          int k;  
     }a;  
     int main()  
     {  
          a.u.x =4;  
          a.u.y =5;  
          a.u.z =6;  
          a.k = 0;  
          printf("%d %d %d\n",a.u.x,a.u.y,a.u.z);  
          return 0;  
     }  
    

    我們在union中嵌入了一個struct,都是int型別,每個變數各佔四個位元組,根據union中成員共享記憶體的特性,他們在記憶體中的排列列順序應該是這樣的

    當我們最後給k賦值為0的時候,相應的,x的值就被覆蓋了,因此最後的輸出應該是0 5 6

    union主要適用於這樣的場景

    • 由多個物件、多個事物,但是我們只需要其中一個,在實際中的應用可能是下面這樣的:(報文格式的設計)

       struct CommuPacket  
       {  
       	int iPacketType;	//報文型別標誌  
       	union      		//每次傳送的是三種報文中的一種,使用union  
       	{  
       		struct structA packetA;  
       		struct structB packetB;  
       		struct structC packetC;  
       	}  
       };  
      

下面貼一個從網上找到的訊號量函式的使用示例:

include<stdio.h>
#include<stdlib.h>
#include<sys/sem.h>
union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
};
int sem_id;
int set_semvalue()
{
    union semun sem_union;    
    sem_union.val = 1;
    if(semctl(sem_id,0,SETVAL,sem_union)==-1)
        return 0;
    return 1;
}
int semaphore_p()
{
    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)
    {
        fprintf(stderr,"semaphore_p failed\n");
        return 0;
    }
    return 1;
}
int semaphore_v()
{
    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)
    {
        fprintf(stderr,"semaphore_v failed\n");
        return 0;
    }
    return 1;
}
void del_semvalue()
{
    //刪除訊號量
    union semun sem_union;
    if(semctl(sem_id,0,IPC_RMID,sem_union)==-1)
        fprintf(stderr,"Failed to delete semaphore\n");
}
int main(int argc,char *argv[])
{
    char message = 'x';
    //建立訊號量
     sem_id = semget((key_t)1234,1,0666|IPC_CREAT);
    if(argc>1)
    {
        //初始化訊號量
        if(!set_semvalue())
        {
            fprintf(stderr,"init failed\n");
            exit(EXIT_FAILURE);
        }
        //引數的第一個字元賦給message
        message = argv[1][0];
    }
    int i=0;
    for(i=0;i<5;i++)
    {
        //等待訊號量
        if(!semaphore_p())
            exit(EXIT_FAILURE);
        printf("%c",message);
        fflush(stdout);
        sleep(1);
        //傳送訊號量
        if(!semaphore_v())
            exit(EXIT_FAILURE);
        sleep(1);
    }
    printf("\n%d-finished\n",getpid());
    if(argc>1)
    {
        //退出前刪除訊號量
        del_semvalue();
    }
    exit(EXIT_SUCCESS);
}

執行結果:

[email protected]:~# ./test.exe  0 & ./test.exe
[1] 20393
x0x0x0x0x0
20394-finished
[email protected]:~#
20393-finished

訊號量的存在使得兩個程序交替列印字元