1. 程式人生 > >semget函式 semopt 採用訊號量處理多程序互斥同步

semget函式 semopt 採用訊號量處理多程序互斥同步

採用訊號量處理多程序互斥同步

       訊號量與訊息類似,也是程序間通訊的一種方法。我們在這裡講的訊號量,實際上是一個包含訊號量元素陣列的訊號量集。訊號量元素與E.W.Dijkstra提出的整數訊號量相對應。在一個單系統呼叫中,程序可在完整的訊號量集上操作。

       訊號量集的內部表示和各自訊號量元素不是直接可以訪問的,但每個訊號量元素必須包括下列各項:

l         一個標識訊號量元素的非負整數

l         最後操作訊號量元素的程序ID

l         等待訊號量元素值加1的程序數

l         等待訊號量元素值等於0的程序數

訊號量操作允許一個程序阻塞直到訊號量元素值為0或者直到它變為正數。每個元素具有兩個相關聯的佇列:一個是等待訊號量元素值加1的程序佇列,另一個是等待訊號量元素值等於0的程序佇列。

1、  semget函式

系統呼叫semget用來建立一個訊號量集並將每個元素初始化為0

#include<sys/ipc.h>

#include<sys/sem.h>

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

函式semget有3個引數。第1個引數為標識訊號量集的關鍵字,程式指定關鍵字的方法有三種:使用IPC_PRIVATE讓系統產生一個關鍵字、挑選一個隨機數,或者是使用ftok從檔案路徑名中產生一個關鍵字。第2個引數為訊號量集中的元素個數。第3個引數為訊號量存取權標誌與建立標誌,該引數的低9位是訊號量的存取權標誌;建立標誌有2個,IPC_CTEAT和IPC_EXCL。IPC_CREAT表示如果訊號量集不存在,則建立它; IPC_EXCL表示只有在訊號量集不存在的情況下,新的訊號量集才會被建立,否則semget返回-1,並設定errno。如果這兩個標誌聯合使用,則功能如同O_EXCL標誌於open。

函式semget呼叫成功時返回一個用於以後semctl等操作的整數控制代碼,失敗時返回-1。

在指定訊號量集的關鍵字時,我們可以使用ftok根據檔案路徑名產生一個關鍵字。

#include<sys/ipc.h>

key_t ftok(const char * pathname, int proj_id);

函式ftok的第1個引數為檔案路徑名,該檔案必須存在且程序對該檔案有訪問權。第2個引數為程式設計師指定的一個整數ID。該函式成功呼叫時返回一個關鍵字,如呼叫失敗,則返回-1。

2、  semctl函式

系統呼叫semctl可以對訊號量進行很多控制。

#include<sys/ipc.h>

#include<sem.h>

int semctl(int semid, int semnum, int cmd, /*union senum arg*/…);

函式的第1個引數為訊號量集的控制代碼。第2個引數指定訊號量集中的元素。第3個引數為執行的控制命令,常見的命令如下表:

命令名稱
   

表示的含義

GETVAL
   

獲得一個指定訊號量的值

GETPID
   

獲得操作此元素的最後程序的ID

GETNCNT
   

獲得等待元素增1的程序數

GETZCNT
   

獲得等待元素變為0的程序數

SETVAL
   

設定一個指定訊號量元素的值為arg.val

IPC_RMID
   

刪除一個訊號量

IPC_SET
   

設定訊號量的許可權

函式semctl在出現錯誤時返回-1並設定erro。當呼叫成功時,其返回值決定於引數cmd,當cmd為GETVAL、GETPID、GETNCNT或GETZCNT時,函式返回相應的值,當cmd為別的值時,返回0。

union senum定義可直接包含在程式中,因為系統的標頭檔案中並沒有定義它。它的定義如下:

union senum

{

int val;

struct semid_ds *buf;

ushort *array;

};

union senum中提到的semid_ds結構定義如下:

struct semid_ds

{

struct ipc_perm sem_perm;    /*operation permission struct*/

time_t sem_otime;   /*last semop() time*/

time_t sem_ctime;   /*last time changed by semctl()*/

struct sem*sembase;             /*ptr to first semaphore in array*/

struct sem_queue *sem_pending;   /*pending operations*/

struct sem_queue *sem_pending_last;   /*last pending operation*/

struct sem_undo *undo;  /*undo requests on this array*/

unsigned short int sem_nsems;      /*number of semaphores in set*/

};

3、  semop函式

系統呼叫semop可以對訊號量增1、減1或測試其是否為0。

#include <sys/ipc.h>

#include <sys/sem.h>

int sempo(int semid, struct sembuf *sops, unsigned int nsops);

函式semop的第1個引數為semget返回的控制代碼,第2個引數指向元素運算元組,第3個引數指定在陣列中元素操作的個數。

函式成功呼叫時返回0,失敗時返回-1。如果時被訊號中斷,則返回-1,同時設定errno為EINTR。

結構體sembuf的定義如下:

struct sembuf

{

  short int sem_num;  //訊號量元素個數

  short int sem_op;   //訊號量元素上的操作

  short int sem_flg;  //操作選項

}

在結構sembuf中,sem_num表示訊號量元素的個數,sem_op表示在訊號量元素上執行的特別操作,sem_flg表示操作選項的標誌。如果sem_op為正數,semop函式將該值加到相應的訊號量元素中,並喚醒所有等待元素增1的程序。如果sem_op為0而訊號量的值不為0, semop將阻塞呼叫程序並增加那個元素的等零程序個數。如果sem_op為負數,semop將該值加到相應的訊號量元素中(只要結果不為負數),如結果為負數,semop將阻塞程序等待訊號量元素值增加;如值為0,semop將喚醒等零程序。

上面的描述假設sem_flg為0。如果sem_flg&IPC_NOWAIT為真,呼叫不會阻塞,而是返回-1並設定error為 EAGAIN。如果sem_flg&SEM_UNDO為真,函式也將為程序修改訊號量的調整值。這個調整值允許程序在退出時恢復它在訊號量上的作用。

下面的程式給出了關於訊號量集的系統呼叫semget,semctl和semop的基本用法。

#include <stdio.h>

#include <stdlib.h>

#include <sys/sem.h>

#include <sys/ipc.h>

main()

{

int semid,pid,I,j;

static struct sembuf lock={0,-1,SEM_UNDO};

static struct sembuf unlock={0,1,SEM_UNDO|IPC_NOWAIT};

 

if((semid=semget(998,1,IPC_CREAT|IPC_EXCL|0666))==-1)

{

               printf(“error:semget!/n”);

               exit(1);

}

 

if(semctl(semid,0,SETVAL,1)==-1)

{

printf(“error:semctl!/n”);

exit(1);

}

 

       setbuf(stdout,(char *)NULL);

       for(i=0;i<3;i++)

       {

              if(fork()==0)

              {

                     if((semid=semget(998,1,0))==-1)

                     {

                            printf(“error:semget!/n”);

                            exit(1);

}

 

for(j=0;j<3;j++)

{

       sleep(i);

       if(semop(semid,&lock,1)==-1)

       {

              printf(“error:semop!/n”);

              exit(1);

}//if lock

 

printf(“process %d get into critical section!/n”,getpid());

sleep(1);

printf(“process %d left critical section!/n”,getpid());

if(semop(semid,&unlock,1)==-1)

{

       printf(“error:semop!/n”);

       exit(1);

}

}//for j

exit(0);

}//if fork

}//for i

 

for(i=0;i<3;i++)

               wait(NULL);

 

if(semctl(semid,0,IPC_RMID,0)==-1)

{

        printf(“error:semctl!/n”);

        exit(1);

}

exit(0);

}