下面的例子清晰地了说明如何使用一个信号量实现临界资源互斥,两个信号量实现互斥同步。
-
信号量接口封装
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; }