System V 信號量


System V 信號量在內核中維護,其中包括二值信號量 、計數信號量、計數信號量集。
二值信號量 : 其值只有0、1 兩種選擇,0表示資源被鎖,1表示資源可用;
計數信號量:其值在0 和某個限定值之間,不限定資源數只在0 1 之間;
計數信號量集 :多個信號量的集合組成信號量集
內核維護的信號量集結構信息如下:定義在頭文件<sys/sem.h>

struct semid_ds {
    struct     ipc_perm     sem_perm;
    struct     sem          *sem_base;
    ushort                  sem_nsems;
    time_t                  sem_otime;
    time_t                  sem_ctime;
};

其中ipc_perm 結構是內核給每個進程間通信對象維護的一個信息結構,其成員包含所有者用戶id,所有者組id、創建者及其組id,以及訪問模式等;semid_ds結構體中的sem結構是內核用於維護某個給定信號量的一組值的內部結構,其結構定義:

struct sem {
   int semval;     /* current value */
   int sempid;     /* pid of last operation */
   struct list_head sem_pending; /* pending single-sop operations */
 };

其中senval變量代表當前信號量的值,sempid 為最后一個成功操作該信號量的進程id,該結構體在內核以雙向鏈表進行 維護
semid_ds結構體中的sem_nsems成員代表該信號量標示符的信號量個數
主要函數介紹:

創建一個信號量或訪問一個已經存在的信號量集。
int semget(key_t key, int nsems, int semflg);
該函數執行成功返回信號量標示符,失敗返回-1
參數key是通過調用ftok函數得到的鍵值,nsems代表創建信號量的個數,如果只是訪問而不創建則可以指定該參數為0,我們一旦創建了該信號量,就不能更改其信號量個數,只要你不刪除該信號量,你就是重新調用該函數創建該鍵值的信號量,該函數只是返回以前創建的值,不會重新創建;
semflg 指定該信號量的讀寫權限,當創建信號量時不許加IPC_CREAT ,若指定IPC_CREAT |IPC_EXCL則創建是存在該信號量,創建失敗。

通過semget函數創建一個信號量集程序如下:(semsemget.c)

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <sys/sem.h>
 5 #include <sys/ipc.h>
 6 #define SEM_R    0400   //用戶(屬主)讀
 7 #define SEM_A    0200   //用戶(屬主)寫
 8 #define SVSEM_MODE (SEM_R | SEM_A | SEM_R>>3 | SEM_R>>6)
 9 
10 int main(int argc,char *argv[])
11 {
12     int   c,oflag,semid,nsems;
13     oflag = SVSEM_MODE | IPC_CREAT;   //設置創建模式
14     //根據命令行參數e判斷是否制定了IPC_EXCL模式
15     while((c = getopt(argc,argv,"e"))!= -1)   
16     {
17         switch(c)
18         {
19             case 'e':
20                 oflag |= IPC_EXCL;
21                 break;
22         }
23     }
24     //判斷命令行參數是否合法
25     if (optind != argc -2)
26     {
27         printf("usage: semcreate [-e] <pathname> <nsems>");
28         exit(0);
29     }
30     //獲取信號量集合中的信號量個數
31     nsems = atoi(argv[optind+1]);
32     //創建信號量,通過ftok函數創建一個key,返回信號量 標識符
33     semid = semget(ftok(argv[optind],0),nsems,oflag);
34     exit(0);
35 }

打開一個信號量集后,對其中一個或多個信號量的操作。
int semop(int semid, struct sembuf *sops, unsigned nsops);
該函數執行成功返回0,失敗返回-1;
第一個參數semid 為信號量標示符;nops為第二個參數的操作數組的個數,第二個參數sops為一個結構體數組指針,結構體定義在sys/sem.h中,結構體如下

struct sembuf {
   unsigned short sem_num; /* semaphore index in array */
   short sem_op; /* semaphore operation */
   short sem_flg; /* operation flags */
};

sem_num 操作信號的下標,其值可以為0 到nops
sem_flg為該信號操作的標志:其值可以為0、IPC_NOWAIT 、 SEM_UNDO
0 在對信號量的操作不能執行的情況下,該操作阻塞到可以執行為止;
IPC_NOWAIT 在對信號量的操作不能執行的情況下,該操作立即返回;
SEM_UNDO當操作的進程推出后,該進程對sem進行的操作將被取消;
sem_op取值 >0 則信號量加上它的值,等價於進程釋放信號量控制的資源
sem_op取值 =0若沒有設置IPC_NOWAIT, 那么調用進程將進入睡眠狀態,直到信號量的值為0,否則進程直接返回
sem_op取值 <0則信號量加上它的值,等價於進程申請信號量控制的資源,若進程設置IPC_NOWAIT則進程再沒有可用資源情況下,進程阻塞,否則直接返回。

采用setmop函數對一個信號量執行操作程序如下:(semop.c)

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <sys/sem.h>
 5 #include <sys/ipc.h>
 6 
 7 int main(int argc,char *argv[])
 8 {
 9     int     c,i,flag,semid,nops;
10     struct  sembuf *ptr;
11     flag = 0;
12         //根據命令行參數設置操作模式
13     while( ( c = getopt(argc,argv,"nu")) != -1)
14     {
15         switch(c)
16         {
17             case 'n':
18                 flag |= IPC_NOWAIT;   //非阻塞
19                 break;
20             case 'u':
21                 flag |= SEM_UNDO;   //不可恢復
22                 break;
23         }
24     }
25     if(argc - optind < 2)
26     {
27         printf("usage: semops [-n] [-u] <pathname> operation...");
28         exit(0);
29     } 
30     //打開一個已經存在的信號量集合
31     if((semid = semget(ftok(argv[optind],0),0,0)) == -1)
32     {
33         perror("semget() error");
34         exit(-1);
35     }
36     optind++;  //指向當前第一個信號量的位置
37     nops = argc - optind;   //信號量個數
38     ptr = calloc(nops,sizeof(struct sembuf));
39     for(i=0;i<nops;++i)
40     {
41         ptr[i].sem_num = i;  //信號量變換
42         ptr[i].sem_op = atoi(argv[optind+i]);   //設置信號量的值
43         ptr[i].sem_flg = flag;   //設置操作模式
44     }
45     //對信號量執行操作
46     if(semop(semid,ptr,nops) == -1)  
47     {
48         perror("semop() error");
49         exit(-1);
50     }
51     exit(0);
52 }

對信號量執行各種控制操作。
int semctl(int semid, int semnum, int cmd, ...);
該函數執行成功返回非負值,失敗返回-1
參數semid為信號集的標識符,參數 semnum標識一個特定信號,該參數僅用於 SETVAL、GETVAL、GETPID命令
cmd控制類型,...說明函數參數是可選的,通過該共用體變量semun選擇操作參數,各字段如下:

union semun {
    int val; /* value for SETVAL */
    struct semid_ds __user *buf; /* buffer for IPC_STAT & IPC_SET */
    unsigned short __user *array; /* array for GETALL & SETALL */
    struct seminfo __user *__buf; /* buffer for IPC_INFO */
    void __user *__pad;
 };

IPC_STAT讀取一個信號量集的數據結構semid_ds,並將其存儲在semun中的buf參數中。
IPC_SET設置信號量集的數據結構semid_ds中的元素ipc_perm,其值取自semun中的buf參數。
IPC_RMID將信號量集從系統中刪除
GETALL用於讀取信號量集中的所有信號量的值,存於semnu的array中
SETALL 設置所指定的信號量集的每個成員semval的值
GETPID返回最后一個執行semop操作的進程的PID。
LSETVAL把的val數據成員設置為當前資源數
GETVAL把semval中的當前值作為函數的返回,即現有的資源數,返回值為非負數。

調用semctl函數設置信號量的值程序如下(semsetvalues.c):

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <sys/sem.h>
 5 #include <sys/ipc.h>
 6 
 7 //定義信號量操作共用體結構
 8 union semun
 9 {
10     int                val;
11     struct semid_ds    *buf;
12     unsigned short     *array;
13 };
14 
15 int main(int argc,char *argv[])
16 {
17     int semid,nsems,i;
18     struct semid_ds seminfo;
19     unsigned short *ptr;
20     union semun arg;
21     if(argc < 2)
22     {
23             printf("usage: semsetvalues <pathname>[values ...]");
24             exit(0);
25     }
26     //打開已經存在的信號量集合
27     semid = semget(ftok(argv[1],0),0,0);
28     arg.buf = &seminfo;
29         //獲取信號量集的相關信息
30     semctl(semid,0,IPC_STAT,arg);
31     nsems = arg.buf->sem_nsems;  //信號量的個數
32     if(argc != nsems + 2 )
33     {
34         printf("%s semaphores in set,%d values specified",nsems,argc-2);
35         exit(0);
36     }
37     //分配信號量
38     ptr = calloc(nsems,sizeof(unsigned short));
39     arg.array = ptr;
40     //初始化信號量的值
41     for(i=0;i<nsems;i++)
42         ptr[i] = atoi(argv[i+2]);
43     //通過arg設置信號量集合
44     semctl(semid,0,SETALL,arg);
45     exit(0);
46 }

 調用semctl獲取信號量的值,程序如下(semgetvalues.c):

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <sys/sem.h>
 5 #include <sys/ipc.h>
 6 
 7 union semun
 8 {
 9     int             val;
10     struct semid_ds *buf;
11     unsigned short     *array;
12 };
13 
14 int main(int argc,char *argv[])
15 {
16     int     semid,nsems,i;
17     struct semid_ds seminfo;
18     unsigned short *ptr;
19     union semun arg;
20     if(argc != 2)
21     {
22         printf("usage: semgetvalues<pathname>");
23         exit(0);
24     }
25     //打開已經存在的信號量
26     semid = semget(ftok(argv[1],0),0,0);
27     arg.buf = &seminfo;
28     //獲取信號量集的屬性,返回semid_ds結構
29     semctl(semid,0,IPC_STAT,arg);
30     nsems = arg.buf->sem_nsems; //信號量的數目
31     ptr = calloc(nsems,sizeof(unsigned short));
32     arg.array = ptr;
33     //獲取信號量的值
34     semctl(semid,0,GETALL,arg);
35     for(i=0;i<nsems;i++)
36         printf("semval[%d] = %d\n",i,ptr[i]);
37     exit(0);
38 }

System V 信號量是具有內核的持續性,可以結合上面介紹的三個函數和程序進行簡單的測試。測試結果如下所示:

演示SEM_UNDO屬性,測試結果如下:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM