Linux進程間通信---信號量


  • 信號量是一個計數器,通常在內核中實現,用於多個進程對共享數據對象的同步訪問。使用信號量的頭文件是#include <sys/sem.h>
  • 信號量的使用規則:
    • 若信號量為正,則進程可使用該資源。
    • 若信號量為0,則進程阻塞等待,並將進程插入等待隊列,直到該信號量的值大於0從等待隊列中執行進程請求。
    • 加鎖操作:如果信號量大於0,則信號量-1;如果信號量為0,則掛起該進程,並將這個進程插入等待隊列。
    • 解鎖操作:如果等待隊列中有進程則喚醒該進程,讓它恢復運行;否則,信號量+1。
  • Linux下使用信號量的常用函數:
    • semget(key, num_sems, sem_flags):創建新的信號量或取得已有的信號量,key表示信號量的鍵值,不相關進程使用同一個key來訪問同一個信號量,num_sems表示信號量個數(一般為1),sem_flags表示信號量訪問權限,用IPC_CREAT與權限位與可保證信號量不存在時新建一個。函數返回一個int類型的數值,表示信號量的標識符。
    • semop(sem_id, sem_opa, num_sem_ops):改變信號量的值,改變操作在sem_opa中,sem_opa是sumbuf結構體對象,使用方法如下:
      struct sembuf
      {  
         short sem_num;   //除非使用一組信號量,否則它為0  
         short sem_op;    //信號量在一次操作中需要改變的數據,通常是兩個數,一個是-1,即P操作(加鎖);一個是+1,即V操作(解鎖)
         short sem_flg;   //通常為SEM_UNDO,使操作系統跟蹤信號,並在進程沒有釋放該信號量而終止時,操作系統釋放信號量  
      };
    • semctl(sem_id, sem_num, command, semun):控制信號量。commond中有:SETVAL初始化信號量為一個值,該值再semun結構體的val字段;IPC_RMID用於刪除一個無需繼續使用的信號量。
  • 信號量的使用實例,同時開兩個進程,每個進程中都用信號量同步臨界區,在臨界區中向屏幕打印字符:
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <sys/sem.h>
     
    union semun
    {
        int val;
        struct semid_ds *buf;
        unsigned short *arry;
    };
     
    static int sem_id = 0;
    static int set_semvalue();
    static void del_semvalue();
    static int semaphore_p();
    static int semaphore_v();
     
    int main(int argc, char *argv[])
    {
        char message = 'X';
        int i = 0;
     
        //創建信號量
        sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT);
     
        if(argc > 1)
        {
            //程序第一次被調用,初始化信號量
            if(!set_semvalue())
            {
                fprintf(stderr, "Failed to initialize semaphore\n");
                exit(EXIT_FAILURE);
            }
            //設置要輸出到屏幕中的信息,即其參數的第一個字符
            message = argv[1][0];
            sleep(2);
        }
        for(i = 0; i < 10; ++i)
        {
            //進入臨界區
            if(!semaphore_p())
                exit(EXIT_FAILURE);
            //向屏幕中輸出數據
            printf("%c", message);
            //清理緩沖區,然后休眠隨機時間
            fflush(stdout);
            //離開臨界區,休眠隨機時間后繼續循環
            if(!semaphore_v())
                exit(EXIT_FAILURE);
            sleep(2);
        }
     
        sleep(3);
        printf("\n%d - finished\n", getpid());
     
        if(argc > 1)
        {
            //如果程序是第一次被調用,則在退出前刪除信號量
            sleep(3);
            del_semvalue();
        }
        exit(EXIT_SUCCESS);
    }
     
    static int set_semvalue()
    {
        //用於初始化信號量,在sem_union的val字段中設置信號量初值。使用信號量之前必須先初始化!
        union semun sem_union;
        sem_union.val = 1;
        if(semctl(sem_id, 0, SETVAL, sem_union) == -1)
            return 0;
        return 1;
    }
     
    static void del_semvalue()
    {
        //刪除信號量
        union semun sem_union;
        if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
            printf("Failed to delete semaphore\n");
    }
     
    static int semaphore_p()
    {
        //對信號量做減1操作,即加鎖 P(sv)
        struct sembuf sem_b;
        sem_b.sem_num = 0;
        sem_b.sem_op = -1;   //P()
        sem_b.sem_flg = SEM_UNDO;
        if(semop(sem_id, &sem_b, 1) == -1)
        {
            printf("semaphore_p failed\n");
            return 0;
        }
        return 1;
    }
     
    static int semaphore_v()
    {
        //這是一個釋放操作,它使信號量變為可用,即解鎖 V(sv)
        struct sembuf sem_b;
        sem_b.sem_num = 0;
        sem_b.sem_op = 1;   //V()
        sem_b.sem_flg = SEM_UNDO;
        if(semop(sem_id, &sem_b, 1) == -1)
        {
            printf("semaphore_v failed\n");
            return 0;
        }
        return 1;
    }
      ps:以上代碼參考自 https://blog.csdn.net/ljianhui/article/details/10243617,運行結果如下:
             

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM