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;
    }
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章