System V IPC(2)-信號量

来源:http://www.cnblogs.com/yuuyuu/archive/2016/01/16/5134782.html
-Advertisement-
Play Games

一.概述 System V信號量與System V消息隊列不同。它不是用來在進程間傳遞數據。它主要是來同步進程的動作。1.一個信號量是一個由內核維護的整數。其值被限製為大於或等於0。2.可以在信號量上加上或減去一個數量。3.當一個減操作把信號量減到小於...


一.概述                                                   

 System V信號量與System V消息隊列不同。它不是用來在進程間傳遞數據。它主要是來同步進程的動作

1.一個信號量是一個由內核維護的整數。其值被限製為大於或等於0

2.可以在信號量上加上或減去一個數量。

3.當一個減操作把信號量減到小於0時,內核會阻塞調用進程。直到另一操作把信號恢復,阻塞才會解除。

4.常用的信號量是二進位信號量。即操作0和1來控制臨界區

二.函數介面                                           

1.創建或打開一個信號量

1 #include <sys/sem.h>
2 
3 int semget(key_t key, int nsems, int semflg);

key和semflg跟消息隊列一樣,這裡不再介紹,上面已給出消息隊列的文章連接。

nsems:指定信號量數目,一般都是1。

2.控制信號量

1 #include <sys/sem.h>
2 
3 int semctl(int semid, int semnum, int cmd, ...);

semid:semget()返回的信號量標識符。

semnum:信號量編號,如果是成組的信號量,就要用到它,否則是0。

cmd:控制信號量的命令。SETVAL初始化一個值,IPC_RMID刪除信號量。

第四個參數是一個union semun結構。如果有的Linux版本頭文件沒有這個結構,需要自己定義:

1 union semun {
2                int              val;    /* Value for SETVAL */
3                struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
4                unsigned short  *array;  /* Array for GETALL, SETALL */
5                struct seminfo  *__buf;  /* Buffer for IPC_INFO
6                                            (Linux-specific) */
7  };

3.改變信號量的值

1 #include <sys/sem.h>
2 
3 int semop(int semid, struct sembuf *sops, size_t nsops);

sops:指向一個sembuf結構,該結構成員如下:

unsigned short sem_num:信號量編號,如果不是一組信號,一般都為0。
short sem_op:對信號量加減操作,如:-1,+1,1。
short sem_flg:通常設置為SEM_UNDO。如果進程終止時沒有釋放該信號量,內核會釋放它。

三.簡單例子                                            

我們封裝一個簡單的二進位信號量,寫2個小程式,一個只列印基數,一個只列印偶數,通過二進位信號量來控制它們按順序列印1-10。

1.封裝二進位信號量

 1 /**
 2  * @file binary_sem.h
 3  */
 4 
 5 #ifndef _BINARY_SEM_H_
 6 #define _BINARY_SEM_H_
 7 
 8 union semun {
 9     int              val;    /* Value for SETVAL */
10     struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
11     unsigned short  *array;  /* Array for GETALL, SETALL */
12     struct seminfo  *__buf;  /* Buffer for IPC_INFO
13                                 (Linux-specific) */
14 };
15 
16 /* 設置信號量 */
17 int sem_set(int sem_id);
18 /* 增大信號量 */
19 int sem_up(int sem_id);
20 /* 減小信號量 */
21 int sem_down(int sem_id);
22 /* 刪除信號量 */
23 int sem_delete(int sem_id);
24 
25 #endif
 1 /**
 2  * @file binary_sem.c
 3  */
 4 
 5 #include <stdio.h>
 6 #include <stdlib.h>
 7 #include <string.h>
 8 #include <sys/sem.h>
 9 
10 #include "binary_sem.h"
11 
12 static union semun sem_union;
13 static struct sembuf sem_buf;
14 
15 
16 /* 設置信號量 */
17 int sem_set(int sem_id)
18 {
19     sem_union.val = 1;
20     if (semctl(sem_id, 0, SETVAL, sem_union) == -1)
21         return -1;
22     return 0;
23 }
24 
25 /* 改變信號量為1 */
26 int sem_up(int sem_id)
27 {
28     sem_buf.sem_num = 0;
29     sem_buf.sem_flg = SEM_UNDO;
30     sem_buf.sem_op = 1;
31     if (semop(sem_id, &sem_buf, 1) == -1)
32         return -1;
33     return 0;
34 }
35 
36 /* 信號量減1 */
37 int sem_down(int sem_id)
38 {
39     sem_buf.sem_num = 0;
40     sem_buf.sem_flg = SEM_UNDO;
41     sem_buf.sem_op = -1;
42     if (semop(sem_id, &sem_buf, 1) == -1)
43         return -1;
44     return 0;
45 }
46 
47 /* 刪除信號量 */
48 int sem_delete(int sem_id)
49 {
50     if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
51         return -1;
52     return 0;
53 }

2.列印基數的程式

 1 /**
 2  * @file sem1.c
 3  */
 4 
 5 #include <stdio.h>
 6 #include <string.h>
 7 #include <stdlib.h>
 8 #include <sys/sem.h>
 9 #include <unistd.h>
10 
11 #include "binary_sem.h"
12 
13 #define SEM_KEY 7788
14 
15 void err_exit(const char *err_msg)
16 {
17     printf("error: %s\n", err_msg);
18     exit(1);
19 }
20 
21 int main(int argc, const char *argv[])
22 {
23     int sem_id, i;
24 
25     /* 創建信號 */
26     if ((sem_id = semget(SEM_KEY, 1, 0666 | IPC_CREAT)) == -1)
27         err_exit("semget()");
28 
29     /* 初始化信號為1 */
30     if (sem_set(sem_id) == -1)
31         err_exit("sem_set()");
32 
33     for (i = 1; i < 10; i += 2)
34     {
35         /* 信號減一 */
36         if (sem_down(sem_id) == -1)
37             err_exit("sem_down()");
38 
39         sleep(3);
40         printf("%s:%d\n", argv[0], i);
41 
42         /* 信號加一 */
43         if (sem_up(sem_id) == -1)
44             err_exit("sem_up()");
45 
46         sleep(1);
47     }
48     return 0;
49 }

對信號量加減操作之間的代碼就是臨界區。程式第39行的sleep()是為了有足夠的時間來允許第二個程式,46行的sleep()是儘量讓其他程式來獲取信號量。

3.只列印偶數的程式

 1 /**
 2  * @file sem2.c
 3  */
 4 
 5 #include <stdio.h>
 6 #include <string.h>
 7 #include <stdlib.h>
 8 #include <unistd.h>
 9 #include <sys/sem.h>
10 
11 #include "binary_sem.h"
12 
13 #define SEM_KEY 7788
14 
15 void err_exit(const char *err_msg)
16 {
17     printf("error: %s\n", err_msg);
18     exit(1);
19 }
20 
21 int main(int argc, const char *argv[])
22 {
23     int sem_id, i;
24 
25     /* 創建信號 */
26     if ((sem_id = semget(SEM_KEY, 1, 0666 | IPC_CREAT)) == -1)
27         err_exit("semget()");
28 
29     for (i = 2; i <= 10; i += 2)
30     {
31         /* 信號減一 */
32         if (sem_down(sem_id) == -1)
33             err_exit("sem_down()");
34 
35         printf("%s:%d\n", argv[0], i);
36 
37         /* 信號加一 */
38         if (sem_up(sem_id) == -1)
39             err_exit("sem_up()");
40 
41         sleep(1);
42     }
43     return 0;
44 }

四.實驗                                                  

1.為了測試方便,上面2個程式的key統一用一個值為7788的巨集來指定。

2.兩個程式執行的流程:sem1初始化一個為1的信號量,進入迴圈後對信號執行減一,並進入臨界區。休眠3秒的時間我執行第二個程式sem2,sem2進入迴圈後也執行減一操作,但此時信號已被sem1減為0,此時內核會阻塞sem1的操作,因為再減就小於0了。回到sem1,3秒過後,列印基數,並把信號重置為1,離開臨界區,休眠1秒。此時內核發現信號是1,可以執行減操作,內核會對sem2放行,sem2進入臨界區列印偶數。剛剛休眠1秒的sem1再次進入臨界區,發現信號量被sem2減到了0,此時被內核阻塞,直到sem2重置信號量。如此反覆!!!

最後:其實System V信號量的語義和API都比較複雜,有機會再探討比它簡單的POSIX信號量。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • js二進位和十進位轉換代碼:十進位和二進位的轉換是在編碼中是時常用到的,下麵就是一段這樣的代碼實例,希望能夠給需要的朋友帶來幫助。代碼實例如下:螞蟻部落十進位:二進位:以上代碼實現了二進位和十進位之間的轉換,其實非常的簡單,因為js本身就提供了這樣的函數。相關閱讀:1.isNaN()函數可以參閱ja...
  • ---------------轉載註明出處----------由於本地推送消息和遠程推送消息有部分機制不一樣,所有此demo寫本地推送,關於遠程推送我會在後面的博客上補上.[1]-------------什麼是推送消息? 我就以一張圖解釋------------[2]-----------IOS程式...
  • 在文章《iOS程式員從小白到大神必讀資料彙總(一)》裡面介紹了很多iOS入門學習的資料,今天小編就發幾篇技術進階的文章,快來看看吧!一、iOS後臺模式開髮指南 這個教程會教你在什麼時候怎麼去用最常用的一些後臺操作二、iOS核心高級動畫技巧 這是一篇在github上很受歡迎的對iOS開發高級動畫技巧的...
  • Android中viewPager的一兩點使用 viewPager是谷歌官方提供的一種方便實現頁面滑動效果的控制項,可以直接使用也可以和fragment聯合使用。這裡只簡單說下直接使用。 使用viewPager的步驟如下: (1)在佈局中放入viewPager的控制項 (2)設置載入到viewP...
  • UITableView在IOS開發中占據非常重要的位置,必須熟練掌握。學習UITableView之前,先瞭解一下一些基本概念:UITableView繼承於UIScrollView,是可以進行垂直滾動的控制項UITableView的每一條數據對應的單元格叫做Cell,是UITableViewCell的一...
  • T-SQL的回車和換行符(SQL)sql server中的回車換行字元是 char(13)+char(10)回車:char(13)換行:char(10)實例1:DECLARE @c NVARCHAR(100)SET @c='2006-01-12|2006-02-13|2006-03-15|2006-...
  • --做測試的時候使用。--缺點:結果集的數量,類型都要一樣。INSERT INTO Student ( StudentID, StudentName, StduentAge, StudentBirthday, StudentGender, StudentPhone, StudentEmail, S....
  • 今天測試密鑰登入linux系統時 出現如下問題:root@compute01:~#ssh [email protected] -p 80 -i alickicxxxxxxx.key@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ....
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...