Linux多進程和多線程(七)進程間通信-信號量

進程間通信之信號量

資源競爭

多個進程競爭同一資源時,會發生資源競爭。
資源競爭會導致進程的執行出現不可預測的結果。

臨界資源

不允許同時有多個進程訪問的資源, 包括硬件資源 (CPU、內存、存儲器以及其他外
圍設備) 與軟件資源(共享代碼段、共享數據結構)

臨界區

多個進程共享的資源被稱為臨界資源,
這些資源被保護在一個臨界區中,
只有進入臨界區的進程才能訪問臨界資源。

信號量

信號量是一種進程間通信機制,用于協調對共享資源的訪問。

多進程對stdout資源的競爭

//多進程對stdout資源的競爭#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>int main(){pid_t cpid;cpid = fork();//創建子進程if(cpid < 0){printf("fork error\n");//fork失敗exit(EXIT_FAILURE);//EXIT_FAILURE表示程序運行失敗} else if(cpid == 0){//子進程while(1){printf("------------------------\n");printf("C Start.\n");sleep(1);printf("C End.\n");printf("------------------------\n");}} else{//父進程while(1){printf("------------------------\n");printf("P Start.\n");sleep(1);printf("P End.\n");printf("------------------------\n");}wait(NULL); //等待子進程結束}return 0;
}

代碼的輸出混亂:

------------------------
P Start.
------------------------
C Start.
P End.
------------------------
C End.
------------------------
------------------------
P Start.
------------------------
C Start.
P End.
C End.
------------------------
------------------------

同步和互斥

互斥

互斥是指進程獨占資源,使得其他進程無法訪問該資源。

同步

同步是指進程間通信,用于協調進程的執行。
同步在互斥的基礎上增加了進程對臨界資源的訪問順序
進程主要的同步與互斥手段是信號量

信號量

信號量,由內核維護的整數,其值被限制為大于或等于0;
信號可以執行一下操作:

  • 將信號量設置成一個具體的值;
  • 在信號量當前的基礎上加上一個數值;
  • 在信號量當前值的基礎上減上一個數值;
  • 等待信號量的值為0;

一般信號量分為

  • 二值信號量:一般指的是信號量值為1,可以理解為只對應一個資源
  • 計數信號量:一般指的是值大于等于2,可以理解為對應多個資源

在linux系統中使用ipcs -s 查詢系統中信號量

創建信號量集合

調用 semget() 函數

函數頭文件:

#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>int semget(key_t key, int nsems, int semflg);

函數功能:創建一個信號量集合;

函數參數:

  • key: 信號量集合的鍵值, 用于標識信號量集合;由ftok()函數生成;
  • nsems: 信號量集合中信號量的個數;
  • semflg: 信號量集合的標志位, 用于設置信號量集合的屬性;
    • IPC_CREAT: 如果key對應的信號量集合不存在, 則創建新的信號量集合;
    • IPC_EXCL: 如果key對應的信號量集合已經存在, 則返回-1;
    • 權限標志

函數返回值:

  • 成功: 返回信號量集合的ID;
  • 失敗: 返回-1, 并設置errno;
//多進程對stdout資源的競爭#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/sem.h>#define MSG_PATH "/home/gopher"
#define MSG_ID 88
int main(){key_t key;//通過文件路徑和ID生成key,key= ftok(MSG_PATH,MSG_ID);if(key==-1){printf("ftok()");exit(EXIT_FAILURE);}//創建信號量集合,包含了一個信號量,編號為0int semid=semget(key,1,IPC_CREAT|0666);if(semid==-1){printf("semget()");exit(EXIT_FAILURE);}return 0;
}

創建出一個信號量集合,包含了一個信號量,編號為0

在這里插入圖片描述

初始化信號量

調用 semctl() 函數

函數頭文件:

#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>int semctl(int semid, int semnum, int cmd, ... /* arg */ );

函數功能:對信號量集合中的信號量進行操作;根據cmd 決定當前函數的功能;

函數參數:

  • semid: 信號量集合的ID;
  • semnum: 信號量的編號;編號從0開始;
  • cmd: 信號量操作命令;
    • SETVAL:設置信號量的值。
    • GETPID:返回最后一個執行 semop 操作的進程的PID。
    • GETVAL:返回指定信號量的值。
    • GETALL:返回信號量集中所有信號量的值。
    • GETNCNT:返回正在等待信號量增加的進程數。
    • GETZCNT:返回正在等待信號量變為零的進程數。
    • SETALL:設置信號量集中所有信號量的值。
    • IPC_STAT:獲取信號量集的狀態信息。
    • IPC_SET:設置信號量集的狀態信息。
    • IPC_RMID:刪除信號量集。
  • … :是屬于可變參參數列表,根據不同的命令有不同的參數;

函數返回值:

  • 成功: 根據不同的cmd, 返回不同的結果;

  • GETPID:返回等待最后一個 semop 操作的進程的 PID。

    GETVAL:返回指定信號量的值。
    ls
    GETALL:如果成功,返回 0。

    GETNCNT:返回正在等待增加信號量值的進程數量。

    GETZCNT:返回正在等待信號量值為零的進程數量。

    IPC_STAT:如果成功,返回 0。

    IPC_SET:如果成功,返回 0。

    IPC_RMID:如果成功,返回 0。

    SETVAL:如果成功,返回 0。

    SETALL:如果成功,返回 0。

  • 失敗: 返回-1, 并設置errno;

//多進程對stdout資源的競爭#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/sem.h>#define MSG_PATH "/home/gopher"
#define MSG_ID 88union semun{int val;
};
int main(){key_t key;//通過文件路徑和ID生成key,key= ftok(MSG_PATH,MSG_ID);if(key==-1){printf("ftok()");exit(EXIT_FAILURE);}//創建信號量集合,包含了一個信號量,編號為0int semid=semget(key,1,IPC_CREAT|0666);if(semid==-1){printf("semget()");exit(EXIT_FAILURE);}union semun s;//定義一個聯合體,用于設置信號量的值s.val=1;//設置信號量的值為1int ret=semctl(semid,0,SETVAL,s);//設置semid信號集中的第編號為0的信號量的值為1if(ret==-1){printf("semctl()");exit(EXIT_FAILURE);}return 0;
}

信號量操作

  • 信號量可以進?以下操作:
    • 對信號量的值加 1
    • 對信號量的值減 1
    • 等待信號量的值為 0

調用 semop() 函數

函數頭文件:

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

函數功能:對信號量集合中的信號量進行操作;

函數參數:

  • semid: 信號量集合的ID;
  • sops: 信號量操作結構體指針
  • nsops: 信號量操作結構體的個數;

函數返回值:

  • 成功: 返回 0;
  • 失敗: 返回-1, 并設置errno;

struct sembuf *sops: 信號量操作結構體指針

struct sembuf
{unsigned short int sem_num;//信號量編號,從0開始short int sem_op;	        //信號量操作//-1:占用資源// +1:釋放資源// 0:等待資源short int sem_flg;		//信號量操作標志位//IPC_NOWAIT:非阻塞,在信號量的值為0時,立即返回// SEM_UNDO:在進程終止時,會自動釋放信號量
};

信號量集合刪除

調用 semctl() 函數 ,設置命令為 IPC_RMID

在使用 semctl() 函數刪除信號量集合時,需要注意第三個參數會被忽略

信號量互斥應用

使用信號量實現進程間互斥,同一時間只有一個進程訪問臨界資源

1.創建sem.h

#ifndef _mySEM_H_
#define _mySEM_H_
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>//創建信號量集
int sem_create(int names,unsigned short value[]);
//占用信號量
int sem_p(int semid,int semnum);
//釋放信號量
int sem_v(int semid,int semnum);
//刪除信號量集
int sem_delete(int semid);#endif /* _SEM_H_ */

2.創建sem.c

#include "sem.h"union semun {int              val;    /* Value for SETVAL */struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */unsigned short  *array;  /* Array for GETALL, SETALL */struct seminfo  *__buf;  /* Buffer for IPC_INFO(Linux-specific) */};//創建信號量集
//@param names 信號量集的個數
//@param value 信號量集的初始值
//@return 成功返回信號量集的id,失敗返回-1
int sem_create(int names,unsigned short value[]){key_t key;//創建keykey= ftok(".",88);if (key == -1){perror("ftok");return -1;}//創建信號量集int semid;semid = semget(key,names,IPC_CREAT|0666);//參數:key,信號量集的個數,權限if (semid == -1){perror("semget");return -1;}union semun s; //定義union semuns.array = value;//將value數組賦值給union semun的array成員//初始化信號量集int ret=semctl(semid,0,SETALL,s);//這個操作將value數組中的值設置到信號量集中if (ret == -1){perror("semctl");return -1;}return semid;}//占用信號量
//@param semid 信號量集的id
//@param semnum 信號量的編號
int sem_p(int semid,int semnum){struct sembuf sem_b;//定義一個信號量操作結構體sem_b.sem_num=semnum;//信號量編號sem_b.sem_op= -1;//占用資源sem_b.sem_flg=SEM_UNDO;//在進程終止時,會自動釋放信號量//操作1個信號量,如果操作多個信號量,需要創建sembuf結構體的數組int r= semop(semid,&sem_b,1); //失敗返回-1,并設置errno   return r;
}
//釋放信號量
int sem_v(int semid,int semnum){struct sembuf sem_b;//定義一個信號量操作結構體sem_b.sem_num=semnum;//信號量編號sem_b.sem_op= 1;//釋放資源sem_b.sem_flg=SEM_UNDO;//在進程終止時,會自動釋放信號量int r= semop(semid,&sem_b,1); //操作1個信號量,如果操作多個信號量,需要創建sembuf結構體的數組//失敗返回-1,并設置errno   return r;
}
//刪除信號量集
int sem_delete(int semid){int r= semctl(semid,0,IPC_RMID); //刪除信號量集return r;
}

3.創建main.c

// 多進程對stdout資源的競爭#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include "sem.h"
int main()
{int semid;// 信號量IDunsigned short values[] = {1};// 信號量初始值semid = sem_create(1, values);if(semid == -1 ){printf("sem_create error\n");exit(EXIT_FAILURE);}pid_t cpid;// 子進程IDcpid = fork(); // 創建子進程if (cpid < 0){printf("fork error\n"); // fork失敗exit(EXIT_FAILURE);     // EXIT_FAILURE表示程序運行失敗}else if (cpid == 0){ // 子進程while (1){sem_p(semid,0);printf("------------------------\n");printf("C Start.\n");sleep(1);printf("C End.\n");printf("------------------------\n");sem_v(semid,0);}}else{ // 父進程while (1){sem_p(semid,0);printf("------------------------\n");printf("P Start.\n");sleep(1);printf("P End.\n");printf("------------------------\n");sem_v(semid,0);}wait(NULL); // 等待子進程結束}return 0;
}

4.編譯運行


------------------------
P Start.
P End.
------------------------
------------------------
C Start.
C End.
------------------------
------------------------
P Start.
P End.
------------------------
------------------------
C Start.
C End.
----------

信號量同步應用

同步在互斥的基礎上增加了進程對臨界資源的訪問順序
進程主要的同步與互斥手段是信號量

示例:

創建??進程,輸出 “ABA” 字符串,具體需求如下:
?進程 輸出 A
?進程 輸出 B
?進程 輸出 A ,輸出換?
能夠循環輸出 “ABA” 字符

基本思路:

通過創建?個信號量集合,包含 2 個信號量,?個信號量 編號為 0
(SEM_CONTROL_P)控制?進程的運?與暫停,?個信號量 編號為 1
(SEM_CONTROL_C) 控制?進程的運?與暫停

// 多進程對stdout資源的競爭#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include "sem.h"#define SEM_C = 1
#define SEM_P = 0
// todo 創建一個信號量集合,集合中兩個信號量,信號量0的值是1,信號量1的值是0;
int main()
{int semid;                         // 信號量IDunsigned short values[2] = {1, 0}; // 信號量初始值// todo 創建一個信號量集合,集合中兩個信號量,信號量編號0的值是1,信號量編號1的值是0;semid = sem_create(2, values);if (semid == -1){printf("sem_create error\n");exit(EXIT_FAILURE);}pid_t cpid; // 子進程IDcpid = fork(); // 創建子進程if (cpid < 0){printf("fork error\n"); // fork失敗exit(EXIT_FAILURE);     // EXIT_FAILURE表示程序運行失敗}else if (cpid == 0){ // 子進程while (1){sem_p(semid, 1); //?占用信號量編號1,信號量編號1的值初始是0 ,在這里阻塞,等待父進程操作printf("B");fflush(stdout); // 刷新緩沖sem_v(semid, 0); //!釋放信號量編號0,信號量編號0的值 0=>1,此時父進程不再阻塞,第二次占用0}}else{ // 父進程while (1){//@param semid 信號量集的id//@param semnum 信號量的編號sem_p(semid, 0); //?占用信號量編號0,信號量編號0的值 1=>0printf("A");fflush(stdout);  // 刷新緩沖sem_v(semid, 1); //?釋放信號量編號1,信號量編號1的值 0=>1,此時子進程不再阻塞sem_p(semid, 0); //!第二次占用信號量編號0,信號量編號0的值是0,在這里阻塞,等待子進程的操作printf("A\n");fflush(stdout);  // 刷新緩沖sem_v(semid, 0);sleep(1);}wait(NULL); // 等待子進程結束}return 0;
}
0的值 0=>1,此時父進程不再阻塞,第二次占用0}}else{ // 父進程while (1){//@param semid 信號量集的id//@param semnum 信號量的編號sem_p(semid, 0); //?占用信號量編號0,信號量編號0的值 1=>0printf("A");fflush(stdout);  // 刷新緩沖sem_v(semid, 1); //?釋放信號量編號1,信號量編號1的值 0=>1,此時子進程不再阻塞sem_p(semid, 0); //!第二次占用信號量編號0,信號量編號0的值是0,在這里阻塞,等待子進程的操作printf("A\n");fflush(stdout);  // 刷新緩沖sem_v(semid, 0);sleep(1);}wait(NULL); // 等待子進程結束}return 0;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/40939.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/40939.shtml
英文地址,請注明出處:http://en.pswp.cn/web/40939.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Redis Cluster 模式 的具體實施細節是什么樣的?

概述 參考&#xff1a;What are Redis Cluster and How to setup Redis Cluster locally ? | by Rajat Pachauri | Medium Redis Cluster 的工作原理是將數據分布在多個節點上&#xff0c;同時確保高可用性和容錯能力。以下是 Redis Cluster 運行方式的簡要概述&#xff1a; …

讀書到底有什么意義?從笨小孩到名人的逆襲之路

點擊上方△騰陽 關注 作者 l 騰陽 轉載請聯系授權 讀書到底有什么意義&#xff1f; 有一個鳥語花香的農場里&#xff0c;住著老農夫和他的小孫子。 老農夫經常在清晨會坐在窗邊&#xff0c;捧著厚厚的《圣經》&#xff0c;沉浸在知識的海洋里。 小孫子問他&#xff1a;…

[終端安全]-1 總體介紹

有朋友一直在和筆者研討智駕安全這個熱門話題&#xff0c;筆者十多年工作從不離終端安全這個核心話題&#xff08;芯片安全、操作系統安全、應用安全&#xff09;&#xff0c;近來也一直在梳理終端安全體系&#xff1b;手機、汽車皆是我們生活中應用最普遍的智能終端&#xff0…

VSCode設置好看清晰的字體!中文用鴻蒙,英文用Jetbrains Mono

一、中文字體——HarmonyOS Sans SC 1、下載字體 官網地址&#xff1a;https://developer.huawei.com/consumer/cn/design/resource/ 直接下載&#xff1a;https://communityfile-drcn.op.dbankcloud.cn/FileServer/getFile/cmtyPub/011/111/111/0000000000011111111.20230517…

Redis分布式鎖的應用場景有哪些

? 、應?場景 在多線程并發的場景下 &#xff0c;Java Synchronized/Reentrantlock 鎖能夠實現同?個JVM進程內多線程 并發的安全性 &#xff0c;但?法保證多個JVM進程實例構成的集群環境在多線程下的安全性。在?些業務場景 下需要引?分布式鎖。 1、緩存擊穿 當某個熱點緩…

加密(3)非對稱加密

一、介紹 1、概念 非對稱加密&#xff0c;又稱現代加密算法&#xff0c;非對稱加密是計算機通信安全的基石&#xff0c;保證了加密數據不會被破解。加密和解密使用的是兩個不同的密鑰&#xff0c;這種算法叫作非對稱加密算法。 2、示例 首先生成密鑰對, 公鑰為(5,14)&#…

【分布式系統】ELK 企業級日志分析系統

目錄 一.ELK概述 1.簡介 1.1.可以添加的其他組件 1.2.filebeat 結合 logstash 帶來好處 2.為什么使用ELK 3.完整日志系統基本特征 4.工作原理 二.部署ELK日志分析系統 1.初始化環境 2.完成JAVA部署 三. ELK Elasticsearch 集群部署 1.安裝 2.修改配置文件 3.es 性…

latex英文轉中文word,及一些latex相關工具分享

前言&#xff1a;想要轉換latex生成的英文pdf文件為中文word文件 一、主要步驟 1、文字翻譯&#xff1a;直接使用谷歌翻譯等輔助將英文翻譯成中文即可&#xff1b; **2、圖片&#xff1a;**使用latex時一般保存的.png&#xff0c;.bmp格式圖片可以直接插入word, 但是.eps或者…

Vue3:全局播放背景音樂

說明&#xff1a;一個全局播放的背景音樂&#xff0c;首頁無音樂無音樂圖標&#xff0c;在首頁互動跳轉頁面并開始播放音樂&#xff0c;切換頁面不需暫停音樂也不會重置音樂&#xff0c;可以通過音樂圖標控制暫停或播放。 MusicPlay.vue&#xff08;音樂組件&#xff09; <…

實習手記(2):前端菜鳥的摸魚與成長

在不斷的學習&#xff08;摸魚&#xff09;和實踐中&#xff0c;第二周也快要結束了&#xff0c;這周總體來說還是蠻不錯的比上周好很多&#xff08;沒有那么拘謹&#xff1f;&#xff09;。前面兩天一直在學習和摸魚之后就修改了幾個需求。這里記錄一下學習到的新知識、小技巧…

Vue3.js“非原始值”響應式實現基本原理筆記(一)

如果您覺得這篇文章有幫助的話&#xff01;給個點贊和評論支持下吧&#xff0c;感謝~ 作者&#xff1a;前端小王hs 阿里云社區博客專家/清華大學出版社簽約作者/csdn百萬訪問前端博主/B站千粉前端up主 此篇文章是博主于2022年學習《Vue.js設計與實現》時的筆記整理而來 書籍&a…

Sentinel限流算法總結

文章目錄 一、線程隔離二、滑動窗口算法三、令牌桶算法四、漏桶算法 一、線程隔離 線程隔離有兩種方式實現&#xff1a; 線程池隔離&#xff1a;給每個服務調用業務分配一個線程池&#xff0c;利用線程池本身實現隔離效果信號量隔離&#xff1a;不創建線程池&#xff0c;而是…

Xilinx FPGA:vivado關于同步fifo的兩個小實驗

一、實驗一&#xff1a;在同步fifo里寫一個讀一個&#xff08;寫入是8個位寬&#xff0c;讀出是16個位寬&#xff09; 程序&#xff1a; timescale 1ns / 1ps //要求寫一個讀一個 //讀寫時鐘一致&#xff0c;寫是8個位寬&#xff0c;讀是16個位寬 module sync_fifo_test(inpu…

QQ音樂Android一面涼經

最近面試了不少公司, 近期告一段落, 整理一下各家的面試問題, 打算陸續發布出來, 供有緣人參考。今天給大家帶來的是QQ音樂Android一面涼經。 面試崗位: QQ音樂Android開發工程師面試時長: 50min(提問40min 反問10min)代碼考核: 無 面試問題(40min) 自我介紹 工作經歷, 重點…

銀行信用卡風險大數據分析與挖掘2024

銀行信用卡風險大數據分析與挖掘 使用excel數據挖掘功能完成 一、信用卡客戶信用等級影響因素分析與挖掘 基于客戶信用記錄表 1. 數據預處理 瀏覽數據 客戶等級占比&#xff0c;其中優質客戶占比較少&#xff0c;風險客戶很多&#xff0c;分析影響客戶信用等級的原因 年…

vue3+ts項目中.env配置環境變量與情景配置

一、環境變量配置 官網https://cn.vitejs.dev/guide/env-and-mode.html#intellisense 1. 新建.env開頭的文件在根目錄 為了防止意外地將一些環境變量泄漏到客戶端&#xff0c;只有以 VITE_ 為前綴的變量才會暴露給經過 vite 處理的代碼 .env 所有環境默認加載 .env.developm…

數字化精益生產系統--MRP 需求管理系統

MRP&#xff08;Material Requirements Planning&#xff0c;物料需求計劃&#xff09;需求管理系統是一種在制造業中廣泛應用的計劃工具&#xff0c;旨在通過分析和計劃企業生產和庫存需求&#xff0c;優化資源利用&#xff0c;提高生產效率。以下是對MRP需求管理系統的功能設…

Raylib 坐標系

draftx 符號調整為正數 發現采樣坐標系原點0&#xff0c;0 在左上角&#xff0c;正方向 右&#xff0c;下 繪制坐標系 原點0&#xff0c;0 在左下角&#xff0c;正方向 右&#xff0c;上 拖拽可得 #include <raylib.h> // 重整原因&#xff1a;解決新函數放大縮小之下…

當需要對多個表進行聯合更新操作時,怎樣確保數據的一致性?

文章目錄 一、問題分析二、解決方案三、示例代碼&#xff08;以 MySQL 為例&#xff09;四、加鎖機制示例五、測試和驗證六、總結 在數據庫管理中&#xff0c;經常會遇到需要對多個表進行聯合更新的情況。這種操作帶來了一定的復雜性&#xff0c;因為要確保在整個更新過程中數據…

為什么需要服務器?服務器可以做些什么

目錄 一、服務器和電腦的區別二、什么是SSH三、什么是免密碼登錄四、服務器如何實現SSH免密碼登錄 一、服務器和電腦的區別 服務器和電腦是兩種不同類型的計算機系統&#xff0c;它們在設計、功能和用途上存在明顯的區別。首先&#xff0c;從硬件配置上看&#xff0c;服務器通…