欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Linux進程間通信——使用信號量

 更新時間:2017年01月22日 09:52:46   作者:ljianhui  
這篇文章主要介紹了詳解Linux進程間通信——使用信號量,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

一、什么是信號量

為了防止出現(xiàn)因多個程序同時訪問一個共享資源而引發(fā)的一系列問題,我們需要一種方法,它可以通過生成并使用令牌來授權(quán),在任一時刻只能有一個執(zhí)行線程訪問代碼的臨界區(qū)域。臨界區(qū)域是指執(zhí)行數(shù)據(jù)更新的代碼需要獨占式地執(zhí)行。而信號量就可以提供這樣的一種訪問機制,讓一個臨界區(qū)同一時間只有一個線程在訪問它,也就是說信號量是用來調(diào)協(xié)進程對共享資源的訪問的。

信號量是一個特殊的變量,程序?qū)ζ湓L問都是原子操作,且只允許對它進行等待(即P(信號變量))和發(fā)送(即V(信號變量))信息操作。最簡單的信號量是只能取0和1的變量,這也是信號量最常見的一種形式,叫做二進制信號量。而可以取多個正整數(shù)的信號量被稱為通用信號量。這里主要討論二進制信號量。

二、信號量的工作原理

由于信號量只能進行兩種操作等待和發(fā)送信號,即P(sv)和V(sv),他們的行為是這樣的:

P(sv):如果sv的值大于零,就給它減1;如果它的值為零,就掛起該進程的執(zhí)行

V(sv):如果有其他進程因等待sv而被掛起,就讓它恢復運行,如果沒有進程因等待sv而掛起,就給它加1.

舉個例子,就是兩個進程共享信號量sv,一旦其中一個進程執(zhí)行了P(sv)操作,它將得到信號量,并可以進入臨界區(qū),使sv減1。而第二個進程將被阻止進入臨界區(qū),因為當它試圖執(zhí)行P(sv)時,sv為0,它會被掛起以等待第一個進程離開臨界區(qū)域并執(zhí)行V(sv)釋放信號量,這時第二個進程就可以恢復執(zhí)行。

三、Linux的信號量機制

Linux提供了一組精心設(shè)計的信號量接口來對信號進行操作,它們不只是針對二進制信號量,下面將會對這些函數(shù)進行介紹,但請注意,這些函數(shù)都是用來對成組的信號量值進行操作的。它們聲明在頭文件sys/sem.h中。

1、semget函數(shù)

它的作用是創(chuàng)建一個新信號量或取得一個已有信號量,原型為:

int semget(key_t key, int num_sems, int sem_flags); 

第一個參數(shù)key是整數(shù)值(唯一非零),不相關(guān)的進程可以通過它訪問一個信號量,它代表程序可能要使用的某個資源,程序?qū)λ行盘柫康脑L問都是間接的,程序先通過調(diào)用semget函數(shù)并提供一個鍵,再由系統(tǒng)生成一個相應(yīng)的信號標識符(semget函數(shù)的返回值),只有semget函數(shù)才直接使用信號量鍵,所有其他的信號量函數(shù)使用由semget函數(shù)返回的信號量標識符。如果多個程序使用相同的key值,key將負責協(xié)調(diào)工作。

第二個參數(shù)num_sems指定需要的信號量數(shù)目,它的值幾乎總是1。

第三個參數(shù)sem_flags是一組標志,當想要當信號量不存在時創(chuàng)建一個新的信號量,可以和值IPC_CREAT做按位或操作。設(shè)置了IPC_CREAT標志后,即使給出的鍵是一個已有信號量的鍵,也不會產(chǎn)生錯誤。而IPC_CREAT | IPC_EXCL則可以創(chuàng)建一個新的,唯一的信號量,如果信號量已存在,返回一個錯誤。

semget函數(shù)成功返回一個相應(yīng)信號標識符(非零),失敗返回-1.

2、semop函數(shù)

它的作用是改變信號量的值,原型為:

int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops); 

sem_id是由semget返回的信號量標識符,sembuf結(jié)構(gòu)的定義如下:

struct sembuf{ 
  short sem_num;//除非使用一組信號量,否則它為0 
  short sem_op;//信號量在一次操作中需要改變的數(shù)據(jù),通常是兩個數(shù),一個是-1,即P(等待)操作, 
          //一個是+1,即V(發(fā)送信號)操作。 
  short sem_flg;//通常為SEM_UNDO,使操作系統(tǒng)跟蹤信號, 
          //并在進程沒有釋放該信號量而終止時,操作系統(tǒng)釋放信號量 
}; 

3、semctl函數(shù)

該函數(shù)用來直接控制信號量信息,它的原型為:

int semctl(int sem_id, int sem_num, int command, ...); 

如果有第四個參數(shù),它通常是一個union semum結(jié)構(gòu),定義如下:

union semun{ 
  int val; 
  struct semid_ds *buf; 
  unsigned short *arry; 
}; 

前兩個參數(shù)與前面一個函數(shù)中的一樣,command通常是下面兩個值中的其中一個

  • SETVAL:用來把信號量初始化為一個已知的值。p 這個值通過union semun中的val成員設(shè)置,其作用是在信號量第一次使用前對它進行設(shè)置。
  • IPC_RMID:用于刪除一個已經(jīng)無需繼續(xù)使用的信號量標識符。

四、進程使用信號量通信

下面使用一個例子來說明進程間如何使用信號量來進行通信,這個例子是兩個相同的程序同時向屏幕輸出數(shù)據(jù),我們可以看到如何使用信號量來使兩個進程協(xié)調(diào)工作,使同一時間只有一個進程可以向屏幕輸出數(shù)據(jù)。注意,如果程序是第一次被調(diào)用(為了區(qū)分,第一次調(diào)用程序時帶一個要輸出到屏幕中的字符作為一個參數(shù)),則需要調(diào)用set_semvalue函數(shù)初始化信號并將message字符設(shè)置為傳遞給程序的參數(shù)的第一個字符,同時第一個啟動的進程還負責信號量的刪除工作。如果不刪除信號量,它將繼續(xù)在系統(tǒng)中存在,即使程序已經(jīng)退出,它可能在你下次運行此程序時引發(fā)問題,而且信號量是一種有限的資源。

在main函數(shù)中調(diào)用semget來創(chuàng)建一個信號量,該函數(shù)將返回一個信號量標識符,保存于全局變量sem_id中,然后以后的函數(shù)就使用這個標識符來訪問信號量。

源文件為seml.c,代碼如下:

#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; 
 
  //創(chuàng)建信號量 
  sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT); 
 
  if(argc > 1) 
  { 
    //程序第一次被調(diào)用,初始化信號量 
    if(!set_semvalue()) 
    { 
      fprintf(stderr, "Failed to initialize semaphore\n"); 
      exit(EXIT_FAILURE); 
    } 
    //設(shè)置要輸出到屏幕中的信息,即其參數(shù)的第一個字符 
    message = argv[1][0]; 
    sleep(2); 
  } 
  for(i = 0; i < 10; ++i) 
  { 
    //進入臨界區(qū) 
    if(!semaphore_p()) 
      exit(EXIT_FAILURE); 
    //向屏幕中輸出數(shù)據(jù) 
    printf("%c", message); 
    //清理緩沖區(qū),然后休眠隨機時間 
    fflush(stdout); 
    sleep(rand() % 3); 
    //離開臨界區(qū)前再一次向屏幕輸出數(shù)據(jù) 
    printf("%c", message); 
    fflush(stdout); 
    //離開臨界區(qū),休眠隨機時間后繼續(xù)循環(huán) 
    if(!semaphore_v()) 
      exit(EXIT_FAILURE); 
    sleep(rand() % 2); 
  } 
 
  sleep(10); 
  printf("\n%d - finished\n", getpid()); 
 
  if(argc > 1) 
  { 
    //如果程序是第一次被調(diào)用,則在退出前刪除信號量 
    sleep(3); 
    del_semvalue(); 
  } 
  exit(EXIT_SUCCESS); 
} 
 
static int set_semvalue() 
{ 
  //用于初始化信號量,在使用信號量前必須這樣做 
  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) 
    fprintf(stderr, "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) 
  { 
    fprintf(stderr, "semaphore_p failed\n"); 
    return 0; 
  } 
  return 1; 
} 
 
static int semaphore_v() 
{ 
  //這是一個釋放操作,它使信號量變?yōu)榭捎?,即發(fā)送信號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) 
  { 
    fprintf(stderr, "semaphore_v failed\n"); 
    return 0; 
  } 
  return 1; 
} 

運行結(jié)果如下:

 

注:這個程序的臨界區(qū)為main函數(shù)for循環(huán)不的semaphore_p和semaphore_v函數(shù)中間的代碼。

例子分析 :同時運行一個程序的兩個實例,注意第一次運行時,要加上一個字符作為參數(shù),例如本例中的字符‘O',它用于區(qū)分是否為第一次調(diào)用,同時這個字符輸出到屏幕中。因為每個程序都在其進入臨界區(qū)后和離開臨界區(qū)前打印一個字符,所以每個字符都應(yīng)該成對出現(xiàn),正如你看到的上圖的輸出那樣。在main函數(shù)中循環(huán)中我們可以看到,每次進程要訪問stdout(標準輸出),即要輸出字符時,每次都要檢查信號量是否可用(即stdout有沒有正在被其他進程使用)。

所以,當一個進程A在調(diào)用函數(shù)semaphore_p進入了臨界區(qū),輸出字符后,調(diào)用sleep時,另一個進程B可能想訪問stdout,但是信號量的P請求操作失敗,只能掛起自己的執(zhí)行,當進程A調(diào)用函數(shù)semaphore_v離開了臨界區(qū),進程B馬上被恢復執(zhí)行。然后進程A和進程B就這樣一直循環(huán)了10次。

五、對比例子——進程間的資源競爭

看了上面的例子,你可能還不是很明白,不過沒關(guān)系,下面我就以另一個例子來說明一下,它實現(xiàn)的功能與前面的例子一樣,運行方式也一樣,都是兩個相同的進程,同時向stdout中輸出字符,只是沒有使用信號量,兩個進程在互相競爭stdout。它的代碼非常簡單,文件名為normalprint.c,代碼如下:

#include <stdio.h> 
#include <stdlib.h> 
 
int main(int argc, char *argv[]) 
{ 
  char message = 'X'; 
  int i = 0;  
  if(argc > 1) 
    message = argv[1][0]; 
  for(i = 0; i < 10; ++i) 
  { 
    printf("%c", message); 
    fflush(stdout); 
    sleep(rand() % 3); 
    printf("%c", message); 
    fflush(stdout); 
    sleep(rand() % 2); 
  } 
  sleep(10); 
  printf("\n%d - finished\n", getpid()); 
  exit(EXIT_SUCCESS); 
} 

運行結(jié)果如下:

 

例子分析:

從上面的輸出結(jié)果,我們可以看到字符‘X'和‘O'并不像前面的例子那樣,總是成對出現(xiàn),因為當?shù)谝粋€進程A輸出了字符后,調(diào)用sleep休眠時,另一個進程B立即輸出并休眠,而進程A醒來時,再繼續(xù)執(zhí)行輸出,同樣的進程B也是如此。所以輸出的字符就是不成對的出現(xiàn)。這兩個進程在競爭stdout這一共同的資源。通過兩個例子的對比,我想信號量的意義和使用應(yīng)該比較清楚了。

六、信號量的總結(jié)

信號量是一個特殊的變量,程序?qū)ζ湓L問都是原子操作,且只允許對它進行等待(即P(信號變量))和發(fā)送(即V(信號變量))信息操作。我們通常通過信號來解決多個進程對同一資源的訪問競爭的問題,使在任一時刻只能有一個執(zhí)行線程訪問代碼的臨界區(qū)域,也可以說它是協(xié)調(diào)進程間的對同一資源的訪問權(quán),也就是用于同步進程的。

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Linux修改用戶所屬組的方法

    Linux修改用戶所屬組的方法

    在本篇文章里小編給大家整理的是關(guān)于Linux修改用戶所屬組的方法,有需要的朋友們參考下。
    2020-02-02
  • Linux如何修改hosts文件并刷新DNS生效hosts文件

    Linux如何修改hosts文件并刷新DNS生效hosts文件

    這篇文章主要介紹了Linux如何修改hosts文件并刷新DNS生效hosts文件問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • 在Linux分區(qū)或邏輯卷中創(chuàng)建文件系統(tǒng)的方法

    在Linux分區(qū)或邏輯卷中創(chuàng)建文件系統(tǒng)的方法

    這篇文章主要給大家介紹了關(guān)于如何在Linux分區(qū)或邏輯卷中創(chuàng)建文件系統(tǒng)的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用Linux具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-04-04
  • 解決hadoop啟動報錯ERROR: Attempting to operate on hdfs namenode as root的方法

    解決hadoop啟動報錯ERROR: Attempting to operate 

    這篇文章主要介紹了解決hadoop啟動報錯ERROR: Attempting to operate on hdfs namenode as root的方法,hadoop-3.1.0啟動hadoop集群時還有可能可能會報如下錯誤,需要的朋友可以參考下
    2023-03-03
  • Linux中如何查看已掛載的文件系統(tǒng)類型詳解

    Linux中如何查看已掛載的文件系統(tǒng)類型詳解

    這篇文章主要給大家介紹了關(guān)于在Linux中如何查看已掛載的文件系統(tǒng)類型的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2018-11-11
  • Linux 下的五種 IO 模型詳細介紹

    Linux 下的五種 IO 模型詳細介紹

    這篇文章主要介紹了Linux 下的五種 IO 模型詳細介紹的相關(guān)資料,需要的朋友可以參考下
    2016-10-10
  • 如何配置apache虛擬主機的實例小結(jié)

    如何配置apache虛擬主機的實例小結(jié)

    如果你是第一次配置apache虛擬主機,那么通過閱讀這篇文章你將會了解到如何實現(xiàn)apache虛擬主機配置。其實要配置好一臺虛擬主機沒有想象中那么難
    2014-01-01
  • linux下搭建hadoop環(huán)境步驟分享

    linux下搭建hadoop環(huán)境步驟分享

    linux下搭建hadoop環(huán)境步驟,需要的朋友可以參考下
    2013-01-01
  • Apache 解決80端口占用問題

    Apache 解決80端口占用問題

    今天小編發(fā)現(xiàn)一個很棘手的問題,在安裝mongodb后發(fā)現(xiàn)apache無法啟動問題,今天小編給大家?guī)砹薃pache 解決80端口占用問題 ,感興趣的朋友一起看看吧
    2018-03-03
  • CentOS上安裝Jenkins的方法

    CentOS上安裝Jenkins的方法

    本篇文章主要介紹了CentOS上安裝 Jenkins的方法,Jenkins是開源的,使用Java編寫的持續(xù)集成的工具,在Centos上可以通過yum命令行直接安裝。
    2017-03-03

最新評論