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); }