Linux學習筆記(15)-信號量


 

  在多線程或者多進程編程中,有一個非常需要關注的東西,那就是同步以及互斥問題。

  同步是指多個進程之間的協作,而互斥是指多個進程之間,為了爭奪有限的資源,而進行的競爭。

  理論很高端,但經過自己幾天的學習,發現操作系統中,線程的信號量還是比較簡單易懂的……

  ————————————————————————————————————————

  信號量是用來解決線程間同步或互斥的一種機制,也是一個特殊的變量,變量的值代表着當前可以利用的資源。

  如果等於0,那就意味着現在沒有資源可用。

  根據信號量的值可以將信號量分為二值信號量和計數信號量:

  (計數信號量)就像一間公共廁所,里面一共有十個坑(最大是32767),算是十個資源。在同一時間可以容納十個人,當滿員的時候,外面的人必須等待里面的人出來,釋放一個資源,然后才能在進一個,當他進去之后,廁所又滿員了,外面的人還得繼續等待……

  (二值信號量)就像自己家的衛生間,一般只有一個馬桶,在同一時間只能有一個人來用。

  信號量只能進程兩個原子操作,P操作和V操作,

  概念:

  原子操作,就是不能被更高等級中斷搶奪優先的操作。

  由於操作系統大部分時間處於開中斷狀態,所以,一個程序在執行的時候可能被優先級更高的線程中斷。

  而有些操作是不能被中斷的,不然會出現無法還原的后果,這時候,這些操作就需要原子操作。就是不能被中斷的操作。

  P操作:如果有可用的資源(信號量>0),那么占用一個資源(信號量-1)。如果沒有可用的資源(信號量=0),則進程被阻塞,直到系統重新給他分配資源。

  V操作:如果在該信號量的等待隊列中有進程在等待該資源,則喚醒一個進程,否則釋放一個資源(信號量+1)

  

  POSIX提供兩種信號量,有名信號量無名信號量,有名信號量一般是用在進程間同步,無名信號量一般用在線程間同步。

  兩種信號量的操作流程,大概有下面的幾點不同:

  

  主要在於兩種信號量初始化和銷毀的方式不同。

  對了,還有一點是非常需要注意的,和在操作共享內存時需要連接庫一樣,在編譯信號量的時候,也需要加上-pthread參數

  ————————————————————————————————————————————————————————————

  首先先來學習有名信號量

  創建有名信號量:

  創建或者打開一個信號量,需要使用sem_open()函數,函數原形如下:

  sem_t sem_open(const char * name, int oflag, mode_t mode, unsigned int value)

  返回值sem_t 是一個結構,如果函數調用成功,則返回指向這個結構的指針,里面裝着當前信號量的資源數。

  參數name,就是信號量的名字,兩個不同的進程通過同一個名字來進行信號量的傳遞。

  參數oflag,當他是O_CREAT時,如果name給出的信號量不存在,那么創建,此時必須給出mode和vaule。當他是O_EXCL時,好像沒有啥太重要的意義。

  參數mode,很好理解,用來指定信號量的權限。

  參數vaule,則是信號量的初始值。

 

  關閉有名信號量:

  關閉有名信號量所使用的函數是sem_close(sem_t *sem)

  這個函數只有一個參數,意義也非常明顯,就是指信號量的名字。

  

  信號量操作:

  前面已經說過,在使用信號量時,有兩個非常重要的操作

  P操作:使用的函數是sem_wait(sem_t *sem)

  如果信號量的值大於零,sem_wait函數將信號量減一,並且立即返回。如果信號量的值小於零,那么該進程會被阻塞在原地。

  V操作:使用的函數是sem_post(sem_t *sem)

  當一個進程使用完某個信號量時,他應該調用sem_post函數來告訴系統收回資源。

  sem_post函數和sem_wait函數的功能剛好相反,他會把指定的信號量加一

 

  刪除有名信號量:

  當使用完有名信號后,需要調用函數sem_unlink來釋放資源。

  函數原形:int sem_unlink(const char *name)

  ——————————————————————————————————————————————————————————

  實戰演練!!!!

  需求:創建兩個進程,A進程打印A,然后等待B進程打印B,在B進程打印完了后,A進程在打印C。

  A進程代碼如下:

  

#include<stdio.h>
#include<stdlib.h>
#include<semaphore.h>
#include<errno.h>
#include<sys/stat.h>
#include<fcntl.h>

#define SEM_NAME "name"

int main()
{
    sem_t *sem_test;

    sem_test = sem_open("ni", O_CREAT, 0644, 0);
    if(sem_test < 0)
    {
        printf("A進程創建信號量失敗!errno=%d\n",errno);
        exit(-1);
    }

    printf("進程A進入等待……\n");
    printf("A\n");
    sem_wait(sem_test);
    printf("C\n");
    sem_post(sem_test);
    printf("A進程執行完畢!\n");

   sem_close(sem_test);
   sem_unlink("ni");

    return 0;
}

  B進程代碼如下:

  

#include<stdio.h>
#include<stdlib.h>
#include<semaphore.h>
#include<errno.h>
#include<sys/stat.h>
#include<fcntl.h>

#define SEM_NAME "name"

int main()
{
    sem_t *sem_test;

    sem_test = sem_open("ni",0);

    if(sem_test < 0)
    {
        printf("B進程創建信號量失敗!errno=%d\n",errno);
        exit(-1);
    }

    printf("B\n");
    sem_post(sem_test);
    printf("B進程執行完畢!\n");
    sem_close(sem_test);
    sem_unlink("ni");
    return 0;
}

 

  現在進程編譯(一定要記得在編譯選項后加上-pthread哦!!)

   

  代碼執行結果!!

   

  執行得很成功!!

  值得一提的是,如果在執行中出現了段錯誤 (核心已轉儲)這種錯誤信息的話,最好是去/dev/shm/下看一下,看看是否有個黃色的文件,權限被設置的奇高!

  我就遇到了這樣的問題。

  哎!雖然整篇文章就這么短短的幾十行,但我可是足足奮斗了將近五個小時才搞懂!!

  明天~繼續加油!!

 

  

       

 

 

  

  

  

  

  

  


免責聲明!

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



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