信號燈(semaphore),也叫信號量,它是不一樣進程間或一個給定進程內部不一樣線程間同步的機制。linux
信號燈種類:編程
1. posix 有名信號燈 (見 posix信號量的應用--生產者-消費者)函數
2. posix 基於內存的信號燈(無名信號燈 見 posix信號量的應用--生產者-消費者) 線程
3. System V 信號燈 (IPC對象,如下主要說明);unix
信號燈:code
1. 二值信號燈:值爲 0 或 1。與互斥鎖相似,資源可用時值爲1,不可用時值爲 0;對象
2. 計數信號燈:值在 0 到 n 之間。用來統計資源,其值表明可用資源數;隊列
等待操做時等待信號燈的值變爲大於0,而後將其減一;而釋放操做則相反,用來喚醒等待資源的進程或者線程;進程
詳情見《unix環境高級編程》15.8小節ip
在Linux 系統中,使用信號量一般分爲如下幾個步驟:
若是不須要信號量,則從系統中刪除它,此時使用semctl() 函數的IPC_RMID 操做。此時須要注意,在程序中不該該出現對已經被刪除的信號量的操做;
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> /*建立或打開信號量集 key :和信號燈集關聯的key 值 nsems:信號燈集中包含的信號燈數目 semflg:信號燈集的訪問權限,一般爲IPC_CREAT|0666 返回值:成功,信號燈集ID 出錯,-1 */ int semget(key_t key, int nsems, int semflg ); /*信號量集的控制 semid:信號燈集ID semnum:要修改的信號燈編號 union semun { int val; // Value for SETVAL struct semid_ds *buf; // Buffer for IPC_STAT, IPC_SET unsigned short *array; // Array for GETALL, SETALL struct seminfo *__buf; // Buffer for IPC_INFO(Linux-specific) }; cmd :GETVAL:獲取信號燈的值 SETVAL:設置信號燈的值 IPC_RMID:從系統中刪除信號燈集合 返回值:成功,0 出錯,-1 */ int semctl(int semid, int semnum, int cmd, union semun arg); /*對信號量集的操做 semid:信號燈集ID struct sembuf 結構體每個元素表示一個操做; struct sembuf { unsigned short sem_num; //要操做的信號燈的編號 short sem_op; // 0: 等待,知道信號燈的值變爲0 // 1: 釋放資源,V操做 // -1: 分配資源,P操做 short sem_flg; //0,IPC_NOWAIT,SEM_UNDO } nops:要操做的信號燈的個數 返回值:成功,0 出錯,-1 */ int semop(int semid,struct sembuf *opsptr,size_t nops);
仍是以前POSIX信號量的生產者和消費者,使用的是system V的信號量實現
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ipc.h> #include <sys/sem.h> #include <signal.h> #define MYFIFO "myfifo" #define BUFFER_SIZE 3 #define UNIT_SIZE 5 #define RUN_TIME 30 #define DELAY_TIME_LEVELS 5 #define MUTEX 0 #define FULL 1 #define AVAIL 2 union semun { int val; // value for SETVAL struct semid_ds *buf; // buffer for IPC_STAT, IPC_SET unsigned short *array; // array for GETALL, SETALL struct seminfo *__buf; // buffer for IPC_INFO }; void *producer(void *arg);//生產者 void *customer(void *arg);//消費者 int fd, semid; time_t end_time; int p(int semid, int semnum) { struct sembuf sops = {semnum, -1, SEM_UNDO}; return semop(semid, &sops, 1); } int v(int semid, int semnum) { struct sembuf sops = {semnum, +1, SEM_UNDO}; return semop(semid, &sops, 1); } void * signal_set(int signo, void (*func)(int)) { int ret; struct sigaction sig; struct sigaction osig; sig.sa_handler = func; sigemptyset(&sig.sa_mask); sig.sa_flags = 0; #ifdef SA_RESTART sig.sa_flags |= SA_RESTART; #endif ret = sigaction(signo, &sig, &osig); if (ret < 0) return SIG_ERR; else return osig.sa_handler; } void sigint(int sig) { unlink(MYFIFO); semctl(semid, 0, IPC_RMID, 0); semctl(semid, 1, IPC_RMID, 0); semctl(semid, 2, IPC_RMID, 0); exit(sig); } int main(int argc, const char *argv[]) { int ret, key; union semun arg; pthread_t thrd_prd_id,thrd_cst_id; signal_set(SIGINT, sigint);//使用信號處理異常,防止程序異常退出,某些資源沒有回收 signal_set(SIGTERM, sigint); signal_set(SIGQUIT, sigint); signal_set(SIGKILL, sigint); srand(time(NULL)); end_time = time(NULL) + RUN_TIME; //建立管道 if ((mkfifo(MYFIFO, 0644) < 0) && (errno != EEXIST)) { fprintf(stderr, "mkfifo error!"); exit(-1); } //打開管道 fd = open(MYFIFO, O_RDWR); if (fd == -1) { fprintf(stderr, "open fifo error"); goto err1; } //系統建立IPC通信(消息隊列、信號量、共享內存)時必須指定一個ID值。一般狀況下,該id值經過ftok函數獲得。 key = ftok("/tmp", 0x66); if (key < 0) { fprintf(stderr, "ftok key error"); goto err1; } //建立了三個信號量 semid = semget(key, 3, IPC_CREAT | 0600); if (semid == -1) { fprintf(stderr, "create semget error"); goto err1; } //對信號量設置初始值 arg.val = 1; ret = semctl(semid, MUTEX, SETVAL, arg); if (ret < 0) { fprintf(stderr, "ctl sem error"); semctl(semid, MUTEX, IPC_RMID, arg); goto err2; } arg.val = BUFFER_SIZE; ret = semctl(semid, AVAIL, SETVAL, arg); if (ret < 0) { fprintf(stderr, "ctl sem error"); semctl(semid, AVAIL, IPC_RMID, arg); goto err2; } arg.val = 0; ret = semctl(semid, FULL, SETVAL, arg); if (ret < 0) { fprintf(stderr, "ctl sem error"); semctl(semid, FULL, IPC_RMID, arg); goto err2; } //取信號量的值 ret = semctl(semid, 0, GETVAL, arg); printf("after semctl setval sem[0].val = [%d]\n", ret); ret = semctl(semid, 1, GETVAL, arg); printf("after semctl setval sem[1].val = [%d]\n", ret); ret = semctl(semid, 2, GETVAL, arg); printf("after semctl setval sem[2].val = [%d]\n", ret); //建立兩個線程 ret = pthread_create(&thrd_prd_id, NULL, producer, NULL); if (ret != 0) { fprintf(stderr, "producer pthread_create error"); goto err2; } ret = pthread_create(&thrd_cst_id, NULL, customer, NULL); if (ret != 0) { fprintf(stderr, "customer pthread_create error"); goto err2; } pthread_join(thrd_prd_id, NULL); pthread_join(thrd_cst_id, NULL); close(fd); err2: semctl(semid, 0, IPC_RMID, 0); semctl(semid, 1, IPC_RMID, 0); semctl(semid, 2, IPC_RMID, 0); err1: unlink(MYFIFO); return 0; } void *producer(void *arg) { int real_write; int delay_time; while (time(NULL) < end_time) { delay_time = rand()%DELAY_TIME_LEVELS; sleep(delay_time); //p操做 if (p(semid, AVAIL) < 0) { fprintf(stderr, "p operate error"); kill(0, SIGQUIT); } if (p(semid, MUTEX) < 0) { fprintf(stderr, "p operate error"); kill(0, SIGQUIT); } printf("\nproducer have delayed %d seconds\n", delay_time); //生產者寫入數據 執行的操做 if (-1 == (real_write = write(fd, "hello", UNIT_SIZE))) { if (errno == EAGAIN) { printf("The buffer is full, please wait for reading!\n"); } } else { printf("producer writes %d bytes to the FIFO\n", real_write); } //v操做 if (v(semid, FULL) < 0) { fprintf(stderr, "v operate error"); kill(0, SIGQUIT); } if (v(semid, MUTEX) < 0) { fprintf(stderr, "v operate error"); kill(0, SIGQUIT); } } pthread_exit(NULL); } void *customer(void *arg) { unsigned char read_buffer[UNIT_SIZE]; int real_read; int delay_time; while (time(NULL) < end_time) { delay_time = rand()%DELAY_TIME_LEVELS; sleep(delay_time); if (p(semid, FULL) < 0) { fprintf(stderr, "p operate error"); kill(0, SIGQUIT); } if (p(semid, MUTEX) < 0) { fprintf(stderr, "p operate error"); kill(0, SIGQUIT); } memset(read_buffer, 0, UNIT_SIZE); printf("\nCustomer have delayed %d seconds\n", delay_time); //消費 操做 if (-1 == (real_read = read(fd, read_buffer, UNIT_SIZE))) { if (errno == EAGAIN) { printf("The buffer is empty, please wait for writing!\n"); } } else { printf("customer reads %d bytes from the FIFO\n", real_read); } if (v(semid, AVAIL) < 0) { fprintf(stderr, "v operate error"); kill(0, SIGQUIT); } if (v(semid, MUTEX) < 0) { fprintf(stderr, "v operate error"); kill(0, SIGQUIT); } } pthread_exit(NULL); }