Linux -- 信号量编程接口封装及如何实现互斥与同步

下面的例子清晰地了说明如何使用一个信号量实现临界资源互斥,两个信号量实现互斥同步。

  1. 信号量接口封装
    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);  /*操作个数*/
    	
    }
    
  2. 互斥操作例子(使用一个信号量实现):
      在本程序中,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;
    }
    
  3. 互斥同步操作例子:
      在互斥的基础上再加一个信号量实现同步,也就是要使用两个信号量实现互斥和同步。
    同步方式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;
    }
    
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值