訊號量

今天去參加北京市的植樹志願者活動啦!早上起來的挺早的,610就被傑子給叫起來啦,帶著對春天的嚮往,我們坐著不花錢的大巴去做為市領導服務去啦!發了一個小紅帽還有一個紅色的制服。

唉。。。說好了,早上9點多學學校的,結果下午四點到的學校,專業課給耽誤了。不過今天的收穫還是蠻多的,還親自為了後代埋了兩顆小樹苗呢,哈哈。。。

現在我們就談談訊號量吧,再沒有講這個之前,我就預習了一下訊號量,所以待明天老師再一講,我就能更理解它的真正含義和作用啦!

訊號量(s):是一種特殊的變數,它的僅僅取正值,並且允許對訊號量的操作只有兩種:1、等待(wait

                                                  2、傳送訊號(signal

wait操作:如果S大於零,wait就在一個原子操作中測試S,並對其進行減量運算;

如果S等於零,wait就在一個原子操作中測試S,並阻塞呼叫程式。

signal操作:如果有執行緒在訊號量上阻塞,S就等於零,signal就會解除對某個等待執行緒的阻塞;

如果沒有執行緒在訊號量上阻塞,signal就對S進行增量運算。

訊號量分為:核心訊號量和使用者態程序使用訊號量

POSIX訊號量又分為:有名訊號量和無名訊號量、

他們之間的區別近似與有名管道與無名管道。

有名訊號量,其值儲存在檔案中,所以它可以用於執行緒也可以用於程序間的同步。無名訊號量,其值儲存在記憶體中。

下面介紹一下,與訊號量有關的函式吧,主要應用到的函式有:

1.        Semget()

2.        Semop()

3.        Semctl()

其中,semget函式是為了獲取訊號量的標識碼的,然後其他的函式只能通過對semget的返回值對訊號量進行處理。

Semget()函式:

#include<sys/sem.h>

Int semget(key_t _key,int_nsems,int _semflg);

函式的功能:是建立一個訊號量或者是為了獲得一個新的鍵值

返回值:成功的返回訊號量的標識碼,如果函式呼叫失敗返回-1

第一個引數_key,為整型值,是允許其他的程序訪問訊號量的一個整型的變數。所以的訊號都是通過間接的方式獲得的,執行的程式會提供一個訊號的鍵值,系統為每一個鍵值賦予一個訊號量,其他的處理函式只能通過對semget函式的返回值進行處理。

第二個引數_nsems,引數表示訊號量的編號,幾乎總是取值為1.

第三個引數_semflg,訊號量的許可權。

系統呼叫semget()的第一個引數是關鍵字值(一般是由系統呼叫ftok()返回的)。系統核心將此值和系統中存在的其他的訊號量集的關鍵字值進行比較。開啟和存取操作與引數semflg中的內容相關。IPC_CREAT如果訊號量集在系統核心中不存在,則建立訊號量集。IPC_EXCL當和 IPC_CREAT一同使用時,如果訊號量集已經存在,則呼叫失敗。如果單獨使用IPC_CREAT,則semget()要麼返回新建立的訊號量集的標識符,要麼返回系統中已經存在的同樣的關鍵字值的訊號量的識別符號。如果IPC_EXCLIPC_CREAT一同使用,則要麼返回新建立的訊號量集的標識符,要麼返回-1IPC_EXCL單獨使用沒有意義。引數nsems指出了一個新的訊號量集中應該建立的訊號量的個數。下面是一個開啟和建立訊號量的程式:

int mykey=5555;
int semid;
semid=semget(mykey,numsems,IPC_CREAT|0660);

if(semid==-1)

{

    perror("semget :");

      exit(1);

}
需要說明的是,返回一個訊號量的ID號。其他通過對訊號量的ID號進行操作。

semop()函式使用:

函式功能:使用者改變訊號量的值.

返回值:函式呼叫成功返回0,失敗返回-1

引數semid:系統分配給該訊號量的一個ID號,通過semget函式的返回值來獲得,也稱為訊號的標識碼

引數sops:一個指向訊號量結構體陣列的指標,訊號量的結構體至少有3個成員。

其結構如下:

struct sembuf

{

unsigned short sem_num;操作訊號在訊號集中的編號,第一個訊號的編號為0,第二個為1,一次後推

short sem_op;訊號資源,如果有就分配,如果為負值就等待(wait),如果正值就分配訊號資源

short sem_flg;訊號操作標誌,有兩種狀態,一個是SEM_UNDO,另一個是SEM_NOWAIT

}

SEM_NOWAIT//對訊號的操作不能滿足是,semop()函式就會阻塞,並立即返回,同時設定錯誤資訊。

SEM_UNDO//程式結束時(無論是否正常結束),保證訊號值會被重新設為semop()呼叫前的值。這樣做

的目的在於避免在異常情況下結束時未將鎖定資源解鎖,造成該資源永遠鎖定。

下面寫一個小例子宣告一個結構體:

 struct sembuf sem1_opt_wakeup[1]={0,1,SEM_UNDO};

 struct sembuf sem1_opt_wait[1]={1,-1,SEM_UNDO};
semctl()函式:

功能:控制訊號量的資訊

返回值:成功返回0,失敗返回-1

第一個引數semid:訊號量標識碼,還是通過semget來獲得的,是semget函式的一個返回值

第二個引數semnum:訊號量的編號,當用到訊號量陣列的時候,這個編號就會起到作用

第三個引數cmd:為允許的命令,其範圍在:

第四個引數union semun:是一個共同體

結構如下:

union semun{

int val;

struct semid_ds *buf;

unsigned short *array;

下面看一個訊號量的使用圖解:

下面看一個小案例吧,來說明一下訊號量的使用,這裡我們用到了兩個程序,也就是要寫兩個函式,也用到了共享記憶體的知識。

程序1:/*
* filename:shm
*
*/
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/stat.h>
#include<sys/shm.h>
#include<sys/sem.h>
#include<fcntl.h>
#include<stdlib.h>
#include<time.h>
#include<stdio.h>
#include<string.h>
#define SEM_KEY1 5555
union semun
{
 int setval;
 struct semid_ds *buf;
 unsigned short *array;
};
int main()
{
 int shmid,semid;
 int *addr;
 int h=0,w=0;
 int ret;
 union semun sem_args;
 unsigned short array[2]={1,1};
 sem_args.array=array;
 shmid=shmget(ftok("/etc/passwd",1001),getpagesize(),IPC_CREAT|IPC_EXCL|S_IRUSR| S_IWUSR| S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
  if(shmid==-1)
        {
                perror("shmget error:");
                exit(EXIT_FAILURE);
        }
 semid=semget(SEM_KEY1,2,IPC_CREAT|0600);
 if(semid==-1)
        {
                perror("semget error:");
                exit(EXIT_FAILURE);
        }
 ret=semctl(semid,1,SETALL,sem_args);
 if(ret==-1)
 {
  perror("shmctl error:");
  exit(EXIT_FAILURE);
 }
 struct sembuf sem1_opt_wakeup[1]={0,1,SEM_UNDO};
 struct sembuf sem2_opt_wait[1]={1,-1,SEM_UNDO};

 while(1)
 {
  int i=0;
  semop(semid,sem2_opt_wait,1);
  addr=shmat(shmid,0,0);
  printf("please input your infornation:\n");
  scanf("%d%d",addr,addr+1);
  shmdt(addr);
  semop(semid,sem1_opt_wakeup,1);
  printf("退出清輸入1\n");
  scanf("%d",&i);
  if(i==1)
  {
    shmctl(shmid,IPC_RMID,NULL);
                        semctl(semid,IPC_RMID,NULL);
   exit(1);
  }
 }
return 0;
}
程序2:

#include<sys/types.h>
#include<sys/shm.h>
#include<sys/sem.h>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
#define SEM_KEY 5555

int main()
{
 int shmid,semid;
 int *addr;
 int h,w;
 float result,sum;
 struct sembuf sem1_opt_wait[1]={0,-1,SEM_UNDO};
        struct sembuf sem2_opt_wakeup[1]={1,1,SEM_UNDO};

 while(1)
 {
  shmid=shmget(ftok("/etc/passwd",1001),getpagesize(),S_IRUSR| S_IWUSR| S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
  sleep(1);
  if(shmid==-1)
         {
                 perror("shmget error:");
                 exit(EXIT_FAILURE);
         }
  semid=semget(SEM_KEY,2,0600);
  if(semid==-1)
         {
                 perror("semget error:");
                 exit(EXIT_FAILURE);
         }
         addr=shmat(shmid,0,0);
  w=*(addr+1);
  h=*addr;
  sum=h*h/10000;
  result=w/sum;
  
  semop(semid,sem1_opt_wait,1);
   
  if(result>25)
  {
   printf("胖拉!\n");
   *(addr+2)=0;
  }
  else if(result<20)
  {
   printf("瘦啦!\n");
   *(addr+2)=0;
  }
  else
  {
   if(result>5&&result<20)
   {
    printf("正常!\n");
    *(addr+2)=0;
   }
  }
  shmdt(addr);
  semop(semid,sem2_opt_wakeup,1);
  sleep(2);
 }
}

.