歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Unix知識 >> Unix基礎知識 >> UNIX環境高級編程:system V信號量

UNIX環境高級編程:system V信號量

日期:2017/3/3 15:19:47   编辑:Unix基礎知識

1. 信號量(semaphore)主要用於保護臨界資源。

進程可以根據它判斷是否能訪問某些共享資源。

信號量除了用於訪問控制外,還可用於進程同步,也就是進程間通信。

2. 信號量分類:

a. 二值信號量: 信號量的值只能取0或1,類似於互斥鎖mutex,但兩者又不同:

mutex 與 二值信號量的區別:

信號量強調共享資源,只要共享資源可用,其他進程同樣可以修改信號量的值;

互斥鎖更強調進程,占用資源的進程使用完資源後,必須由進程本身來接鎖。

b. 計數信號量:信號量的值可以取任意非負值。

system V信號量通過定義如下概念給信號量增加了另外一級復雜度。

計數信號量集:一個或多個信號量(構成一個集合),其中每個都是計數信號量。每個集合的信號量數存在一個限制,一般在25個數量級。

3.semget函數(信號量的創建)

semget函數創建一個信號量集或訪問一個已存在的信號量集。

#include <sys/sem.h>  
int senget(key_t key,int nsems,int oflag);

nsems參數指定集合中的信號量數。如果我們不創建一個新的信號量集,而只是訪問一個已存在的集合,那就可以把該參數指定為0。一旦創建完一個信號量集,我們就不能改變其中的信號量數。

oflag值是SEM_R和SEM_A常值得組合。他們還可以與IPC _CREAT或IPC_CREAT | IPC_EXCL按位或。

當實際操作為創建一個新的信號量集時,相應的semid_ds結構的以下成員將被初始化。

(1)sem_perm結構的uid和cuid成員被置為調用進程的有效用戶ID,gid和cgid成員被置為調用進程的有效組ID。

(2)oflag參數中的讀寫權限位存入sem_perm.mode。

(3)sem_otime被置為0,sem_ctime則被置為當前時間。

(4)sem_nsems被置為nsems參數的值。

(5)與該集合中每個信號量關聯的各個sem結構並不初始化。這些結構時在以SET_VAL或SETALL命令調用semctl時初始化的。

4.semop函數(操作信號量)

使用semget打開一個信號量集後,對其中一個或多個信號量的操作就使用semop函數來執行。

#include <sys/sem.h>  
int semop(int semid,struct sembuf *opsptr,size_t nops);

其中opsptr指向一個如下結構的數組:

struct sembuf{  
           unsigned short sem_num;  /* semaphore number */
           short          sem_op;   /* semaphore operation */
           short          sem_flg;  /* operation flags */
};

假定有一個信號量變量sv,

P(sv):用於等待,如果sv大於0,就給它減去1,如果它的值等於0,就掛起該進程的執行

V(sv):用於發送信號,如果有其他進程因等待sv而掛起,就讓它恢復運行,如果沒有進程因等待sv而被掛起,就給它加1

semaphore sv=1;

loop forever{

P(sv);

critical code section;

V(sv);

noncritical code section;

}

查看本欄目更多精彩內容:http://www.bianceng.cn/OS/unix/

信號量函數定義如下所示:

#include<sys/sem.h>

int semctl(int sem_id, int sem_num, int command, ...);//用來直接控制信號量信息

int semget(key_t key, int num_sems, int sem_flags);//創建一個新信號量或取得一個已有信號量的鍵

int semop(int sem_id, struct sembuf *sem_ops, size_t num_sem_ops);//用於改變信號量的值

/* After the #includes, the function prototypes and the global variable, we come to the 
 main function. There the semaphore is created with a call to semget, which returns the 
 semaphore ID. If the program is the first to be called (i.e. it's called with a parameter 
 and argc > 1), a call is made to set_semvalue to initialize the semaphore and op_char is 
 set to X. */
      
#include <unistd.h>  
#include <stdlib.h>  
#include <stdio.h>  
      
#include <sys/sem.h>  
      
#include "semun.h"  
      
static int set_semvalue(void);  
static void del_semvalue(void);  
static int semaphore_p(void);  
static int semaphore_v(void);  
      
static int sem_id;  
      
      
int main(int argc, char *argv[])  
{  
    int i;  
    int pause_time;  
    char op_char = 'O';  
      
    srand((unsigned int)getpid());  
      
    sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT);  
      
    if (argc > 1) {  
        if (!set_semvalue()) {  
            fprintf(stderr, "Failed to initialize semaphore\n");  
            exit(EXIT_FAILURE);  
        }  
        op_char = 'X';  
        sleep(2);  
    }  
      
/* Then we have a loop which enters and leaves the critical section ten times. 
 There, we first make a call to semaphore_p which sets the semaphore to wait, as 
 this program is about to enter the critical section. */
      
    for(i = 0; i < 10; i++) {  
      
 if (!semaphore_p()) exit(EXIT_FAILURE);  
        printf("%c", op_char);fflush(stdout);  
        pause_time = rand() % 3;  
        sleep(pause_time);  
        printf("%c", op_char);fflush(stdout);  
      
/* After the critical section, we call semaphore_v, setting the semaphore available, 
 before going through the for loop again after a random wait. After the loop, the call 
 to del_semvalue is made to clean up the code. */
      
        if (!semaphore_v()) exit(EXIT_FAILURE);  
      
        pause_time = rand() % 2;  
        sleep(pause_time);  
    }  
      
    printf("\n%d - finished\n", getpid());  
      
    if (argc > 1) {  
        sleep(10);  
        del_semvalue();  
    }  
      
    exit(EXIT_SUCCESS);  
}  
      
/* The function set_semvalue initializes the semaphore using the SETVAL command in a 
 semctl call. We need to do this before we can use the semaphore. */
      
static int set_semvalue(void)  
{  
    union semun sem_union;  
      
    sem_union.val = 1;  
    if (semctl(sem_id, 0, SETVAL, sem_union) == -1) return(0);  
    return(1);  
}  
      
/* The del_semvalue function has almost the same form, except the call to semctl uses 
 the command IPC_RMID to remove the semaphore's ID. */
      
static void del_semvalue(void)  
{  
    union semun sem_union;  
      
if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1)  
        fprintf(stderr, "Failed to delete semaphore\n");  
}  
      
/* semaphore_p changes the semaphore by -1 (waiting). */
      
static int semaphore_p(void)  
{  
    struct sembuf sem_b;  
      
    sem_b.sem_num = 0;  
    sem_b.sem_op = -1; /* P() */
    sem_b.sem_flg = SEM_UNDO;  
    if (semop(sem_id, &sem_b, 1) == -1) {  
        fprintf(stderr, "semaphore_p failed\n");  
        return(0);  
    }  
    return(1);  
}  
      
/* semaphore_v is similar except for setting the sem_op part of the sembuf structure to 1, 
 so that the semaphore becomes available. */
      
static int semaphore_v(void)  
{  
    struct sembuf sem_b;  
      
    sem_b.sem_num = 0;  
    sem_b.sem_op = 1; /* V() */
    sem_b.sem_flg = SEM_UNDO;  
    if (semop(sem_id, &sem_b, 1) == -1) {  
        fprintf(stderr, "semaphore_v failed\n");  
        return(0);  
    }  
    return(1);  
}
Copyright © Linux教程網 All Rights Reserved