下面的例子清晰地了說明如何使用一個信號量實現臨界資源互斥,兩個信號量實現互斥同步。
-
信號量接口封裝
sem.h#ifndef _SEM_H #define _SEM_H int get_sem(int key, int nsems); int del_sem(int semid); int set_sem_val(int semid, int semnum, int sem_val); int sem_P(int semid, int semnum); int sem_V(int semid, int semnum); #endif
sem.c
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include "sem.h" union semun { int val; struct semid_ds *buf; unsigned short *array; }; /** * 函數名: get_sem * 描述: 創建信號量,返回信號量標識符 * 參數: int key -- 鍵值,可有ftok函數獲得 * int nsems -- 創建該鍵值對應下的信號量集數目 * 返回值: 成功 -- 創建後的信號量標識符 * 失敗 -- -1,並設置errno */ int get_sem(int key, int nsems){ return semget(key, nsems, IPC_CREAT|0600); } /** * 函數名: del_sem * 描述: 刪除信號量標識符下的信號量集,參數semnum被忽略 * 參數: int semid -- 信號量標識符 * 返回值: 0 -- 成功 * -1 -- 失敗,並設置errno */ int del_sem(int semid){ return semctl(semid, 0 ,IPC_RMID); } /** * 函數名: set_sem_val * 描述: 設置信號量值 * 參數: int semid -- 信號量標識符 * int semnum -- 信號量編號 * int sem_val -- 信號量對應編號下的值 * 返回值: 0 -- 成功 * -1 -- 失敗,並設置errno */ int set_sem_val(int semid, int semnum, int sem_val){ union semun sem_arg; sem_arg.val=sem_val; /*信號量值*/ return semctl(semid, semnum, SETVAL, sem_arg); } /** * 函數名: sem_P * 描述: 對應信號量編號下的P操作 * 參數: int semid -- 信號量標識符 * int semnum -- 信號量編號 * 返回值: 0 -- 成功 * -1 -- 失敗,並設置errno */ int sem_P(int semid, int semnum){ struct sembuf sops; sops.sem_num=semnum; /*P操作對應的信號量編號*/ sops.sem_op=-1; /*P操作*/ sops.sem_flg= SEM_UNDO; /*系統自動釋放進程中沒有釋放的信號量*/ return semop(semid, &sops, 1); /*操作個數*/ } /** * 函數名: sem_V * 描述: 對應信號量編號下的V操作 * 參數: int semid -- 信號量標識符 * int semnum -- 信號量編號 * 返回值: 0 -- 成功 * -1 -- 失敗,並設置errno */ int sem_V(int semid, int semnum){ struct sembuf sops; sops.sem_num=semnum; /*V操作對應的信號量編號*/ sops.sem_op=1; /*V操作*/ sops.sem_flg= SEM_UNDO; /*系統自動釋放進程中沒有釋放的信號量*/ return semop(semid, &sops, 1); /*操作個數*/ }
-
互斥操作例子(使用一個信號量實現):
在本程序中,fprintf(stderr,“aaa”);與fprintf(stderr,“bbb”);表示同一個臨界資源,父子進程對這個資源互斥操作。本程序不能確定輸出的順序,可能輸出aaabbbaaa…也可能輸出bbbaaabbb…。#include <stdio.h> #include <stdlib.h> #include "sem.h" #include <sys/types.h> #include <unistd.h> #include <sys/ipc.h> #include <signal.h> #define handle_error(msg) \ do { perror(msg); exit(EXIT_FAILURE); } while(0) int semid; void sighandler(int signo){ if(SIGINT==signo){ printf("SIGINT\n"); del_sem(semid); exit(EXIT_SUCCESS); } } int main(void){ pid_t ret_fk; if((semid=get_sem(IPC_PRIVATE, 1))==-1){ /*創建本進程私有信號量*/ handle_error("get_sem"); } if(set_sem_val(semid, 0, 1)==-1){ handle_error("set_sem_val"); } if((ret_fk=fork())==-1){ handle_error("fork"); } if(0==ret_fk){ /*在子進程中*/ while(1){ sem_P(semid, 0); fprintf(stderr,"bbb"); sleep(1); sem_V(semid, 0); } }else{ /*在父進程中*/ if(-1==signal(SIGINT, sighandler)){ /*註冊信號中斷退出函數*/ handle_error("signal"); } while(1){ sem_P(semid, 0); fprintf(stderr,"aaa"); sleep(1); sem_V(semid, 0); } } return 0; }
-
互斥同步操作例子:
在互斥的基礎上再加一個信號量實現同步,也就是要使用兩個信號量實現互斥和同步。
同步方式1:輸出順序:aaabbbaaa…#include <stdio.h> #include <stdlib.h> #include "sem.h" #include <sys/types.h> #include <unistd.h> #include <sys/ipc.h> #include <signal.h> #define handle_error(msg) \ do { perror(msg); exit(EXIT_FAILURE); } while(0) int semid; void sighandler(int signo){ if(SIGINT==signo){ printf("SIGINT\n"); del_sem(semid); exit(EXIT_SUCCESS); } } int main(void){ pid_t ret_fk; if((semid=get_sem(IPC_PRIVATE, 2))==-1){ /*創建本進程私有信號量,信號量集爲2個信號量*/ /* * 信號量編號0: * 信號量編號1: */ handle_error("get_sem"); } if(set_sem_val(semid, 0, 1)==-1){ /*初始化信號量編號0*/ handle_error("set_sem_val"); } if((ret_fk=fork())==-1){ handle_error("fork"); } if(0==ret_fk){ /*在子進程中*/ while(1){ sem_P(semid, 0); fprintf(stderr,"aaa"); sleep(1); sem_V(semid, 1); } }else{ /*在父進程中*/ if(signal(SIGINT, sighandler)){ /*註冊信號中斷退出函數*/ handle_error("signal"); } while(1){ sem_P(semid, 1); fprintf(stderr,"bbb"); sleep(1); sem_V(semid, 0); } } return 0; }
同步方式2:輸出順序:bbbaaabbb…
#include <stdio.h> #include <stdlib.h> #include "sem.h" #include <sys/types.h> #include <unistd.h> #include <sys/ipc.h> #include <signal.h> #define handle_error(msg) \ do { perror(msg); exit(EXIT_FAILURE); } while(0) int semid; void sighandler(int signo){ if(SIGINT==signo){ printf("SIGINT\n"); del_sem(semid); exit(EXIT_SUCCESS); } } int main(void){ pid_t ret_fk; if((semid=get_sem(IPC_PRIVATE, 2))==-1){ /*創建本進程私有信號量,信號量集爲2個信號量*/ /* * 信號量編號0: * 信號量編號1: */ handle_error("get_sem"); } if(set_sem_val(semid, 0, 1)==-1){ /*初始化信號量編號0*/ handle_error("set_sem_val"); } if((ret_fk=fork())==-1){ handle_error("fork"); } if(0==ret_fk){ /*在子進程中*/ while(1){ sem_P(semid, 1); fprintf(stderr,"aaa"); sleep(1); sem_V(semid, 0); } }else{ /*在父進程中*/ if(signal(SIGINT, sighandler)){ /*註冊信號中斷退出函數*/ handle_error("signal"); } while(1){ sem_P(semid, 0); fprintf(stderr,"bbb"); sleep(1); sem_V(semid, 1); } } return 0; }