【Linux】System V - 基于建造者模式的信號量

目錄

信號量和P、V原語

信號量集結構體

信號量操作接口

semget

semctl

semop

封裝Sem

關于建造者模式


信號量和P、V原語

信號量和 PV 原語由 Dijkstra (迪杰斯特拉)提出

信號量值含義

  • S>0: S 表?可?資源的個數
  • S=0: 表??可?資源,?等待進程
  • S<0: |S| 表?等待隊列中進程個數

信號量結構體偽代碼

//信號量本質上是?個計數器
struct semaphore
{
? ? int value;
? ? pointer_PCB queue;
}

P原語

P(s)
{
? ? s.value = s.value--;
? ? if (s.value < 0)
? ? {
? ? ? ? // 該進程狀態置為等待狀狀態
? ? ? ? // 將該進程的PCB插??相應的等待隊列s.queue末尾
? ? }
}

V原語

V(s)
{
? ? s.value = s.value++;
? ? if (s.value > 0)
? ? {
? ? ? ? // 喚醒相應等待隊列s.queue中等待的??個進程
? ? ? ? // 改變其狀態為就緒態
? ? ? ? // 并將其插?OS就緒隊列
? ? }
}

信號量集結構體


The semid_ds data structure is defined in <sys / sem.h> as follows :
struct semid_ds {
? ? struct ipc_perm sem_perm; /* Ownership and permissions */
? ? time_t sem_otime; /* Last semop time */
? ? time_t sem_ctime; /* Last change time */
? ? unsigned long sem_nsems; /* No. of semaphores in set */
};
The ipc_perm structure is defined as follows(the highlighted fields
? ? are settable using IPC_SET) :
? ? struct ipc_perm {
? ? key_t __key; /* Key supplied to semget(2) */
? ? uid_t uid; /* Effective UID of owner */
? ? gid_t gid; /* Effective GID of owner */
? ? uid_t cuid; /* Effective UID of creator */
? ? gid_t cgid; /* Effective GID of creator */
? ? unsigned short mode; /* Permissions */
? ? unsigned short __seq; /* Sequence number */
};

信號量操作接口

semget

NAME
semget - get a System V semaphore set identifier

SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
RETURN VALUE
If successful, the return value will be the semaphore set identifier
(a nonnegative integer), otherwise, -1 is returned, with errno indicating the
error.

參數介紹

  • key: 信號量集的鍵值,同消息隊列和共享內存
  • nsems: 信號量集中信號量的個數
  • semflg: 同消息隊列和共享內存

semctl

NAME
semctl - System V semaphore control operations
SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
This function has three or four arguments, depending on cmd.When there are four, the fourth has the type union semun.The calling program must define?this union as follows :
? ? 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) */
};
RETURN VALUE
On failure, semctl() returns - 1 with errno indicating the error.
Otherwise, the system call returns a nonnegative value depending on
cmd as follows :
GETNCNT the value of semncnt.
GETPID the value of sempid.
GETVAL the value of semval.
GETZCNT the value of semzcnt.
IPC_INFO the index of the highest used entry in the kernel's internal
array recording information about all semaphore sets. (This information can
? ? be used with repeated SEM_STAT or
? ? SEM_STAT_ANY operations to obtain information about all semaphore sets
? ? on the system.)
? ? SEM_INFO as for IPC_INFO.
? ? SEM_STAT the identifier of the semaphore set whose index was given in
? ? semid.
? ? SEM_STAT_ANY as for SEM_STAT.
? ? All other cmd values return 0 on success.

參數介紹
  • semid: 由 semget 返回的信號集標識碼
  • semnum: 信號集中信號量的序號

semnum: semctl() performs the control operation specified by cmd on the
System V semaphore set identified by semid, or on the semnum - th semaphore of
that set. (The semaphores in a set are numbered starting at 0.)

cmd: 將要采取的動作, 具體動作看 man ?冊

semop

NAME
semop, semtimedop - System V semaphore operations
SYNOPSIS
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf* sops, size_t nsops);
semop() performs operations on selected semaphores in the set indicated by
semid.Each of the nsops elements in the array pointed to by sops is a
structure that specifies an operation to be performed on a single semaphore.
The elements of this structure are of type struct sembuf, containing the
following members :
unsigned short sem_num; /* semaphore number */
short sem_op; /* semaphore operation :-1,P操作。1,V操作
*/
short sem_flg; /* operation flags */
Flags recognized in sem_flg are IPC_NOWAIT and SEM_UNDO.If an operation specifies SEM_UNDO, it will be automatically undone when the process terminates.
RETURN VALUE
If successful, semop() and semtimedop() return 0; otherwise they
return -1 with errno indicating the error.

參數介紹

  • semid: 是該信號量的標識碼,也就是 semget 函數的返回值
  • sops: 指向?個結構 sembuf 的指針
  • nsops: sops 對應的信號量的個數,也就是可以同時對多個信號量進?PV操作

封裝Sem

我們使?信號量,簡化信號量使?,測試使??元信號量進?顯?器交替打印

Sem.hpp

#pragma once#include <iostream>
#include <string>
#include <memory>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>const std::string pathname = "/tmp";
int proj_id = 0x77;#define GET_SEM IPC_CREAT // 不給權限
#define BUILD_SEM (IPC_CREAT | IPC_EXCL | 0666)// 只關注使用和刪除
class Semaphore
{
public:Semaphore(int semid, int flag) : _semid(semid), _flag(flag){}void P(){struct sembuf sb; // 該結構系統提供sb.sem_num = 0;sb.sem_op = -1;sb.sem_flg = SEM_UNDO;int n = ::semop(_semid, &sb, 1);(void)n;}void V(){struct sembuf sb;sb.sem_num = 0;sb.sem_op = 1;sb.sem_flg = SEM_UNDO;int n = ::semop(_semid, &sb, 1);(void)n;}~Semaphore(){if(_flag == GET_SEM) return;// 讓信號量自動銷毀// 如果銷毀信號量集合:The argument semnum is ignoredint n = ::semctl(_semid, 0, IPC_RMID);(void)n;std::cout << "sem set destroy!" << std::endl;}private:int _semid;int _flag;
};using sem_sptr = std::shared_ptr<Semaphore>;// 使用一下簡單的建造者模式,用它來構建單sem
class SemaphoreBuilder
{
public:SemaphoreBuilder() : _val(-1){}SemaphoreBuilder &SetVal(int val){_val = val;return *this; // 支持連續訪問}sem_sptr Build(int flag){// 0. 先做一下簡單的合法性判斷if (_val < 0){std::cerr << "you must init first!" << std::endl;return nullptr;}// 1. 申請key值key_t k = ::ftok(pathname.c_str(), proj_id);if (k < 0)exit(1);// 2. 根據初始值,創建信號量集合int semid = ::semget(k, 1, flag); // 這里讓信號量集合中,只創建一個信號量就夠用了if (semid < 0)exit(2);if (BUILD_SEM == flag){// 3. 初始化信號量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) */} un;un.val = _val; // 設置為初始值int n = ::semctl(semid, 0, SETVAL, un);if (n < 0)exit(3);}// 4. 創建并返回信號量集合return std::make_shared<Semaphore>(semid, flag);}~SemaphoreBuilder(){}private:int _val; // 所有信號量的初始值
};

Writer.cc:測試信號量接口

#include "Sem.hpp"
#include <cstdio>
#include <time.h>
#include <unistd.h>int main()
{SemaphoreBuilder sb;auto fsem = sb.SetVal(1).Build(BUILD_SEM); // 創建信號量集合,只有一個信號量,初始化成為1,就是當做鎖來進行使用if (fork() == 0){auto csem = sb.Build(GET_SEM);int cnt = 10;while (cnt--){csem->P();printf("C");fflush(stdout);usleep(rand() % 95270);printf("C ");usleep(rand() % 43990);fflush(stdout);csem->V();}exit(0);}int cnt = 50;while (cnt--){fsem->P();printf("F");fflush(stdout);usleep(rand() % 95270);printf("F ");usleep(rand() % 43990);fflush(stdout);fsem->V();}return 0;
}

結論

  • System V 信號量?命周期也是隨內核的
  • ipcs -s && ipcrm -s semid

關于建造者模式

Sem_V.hpp
#ifndef SEM_HPP
#define SEM_HPP#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>const std::string SEM_PATH = "/tmp";
const int SEM_PROJ_ID = 0x77;
const int defaultnum = 1;
#define GET_SEM (IPC_CREAT)
#define BUILD_SEM (IPC_CREAT | IPC_EXCL)///////////////////////////先設計建造者模式的代碼結構////////////// 一個把整數轉十六進制的函數
std::string intToHex(int num)
{char hex[64];snprintf(hex, sizeof(hex), "0x%x", num);return std::string(hex);
}// 產品類, 只需要關心自己的使用即可(刪除)
// 這里的Semaphore不是一個信號量!!而是一個信號量集合!!,要指明你要PV操作哪一個信號量!!
class Semaphore
{
private:void PV(int who, int data){struct sembuf sem_buf;sem_buf.sem_num = who;        // 信號量編號,從0開始sem_buf.sem_op = data;      // S + sem_buf.sem_opsem_buf.sem_flg = SEM_UNDO; // 不關心int n = semop(_semid, &sem_buf, 1);if (n < 0){std::cerr << "semop PV failed" << std::endl;}}
public:Semaphore(int semid) : _semid(semid){}int Id() const{return _semid;}void P(int who){PV(who, -1);}void V(int who){PV(who, 1);}~Semaphore(){if (_semid >= 0){int n = semctl(_semid, 0, IPC_RMID);if (n < 0){std::cerr << "semctl IPC_RMID failed" << std::endl;}std::cout << "Semaphore " << _semid << " removed" << std::endl;}}private:int _semid;// key_t _key; // 信號量集合的鍵值// int _perm;  // 權限// int _num;   // 信號量集合的個數
};// 建造者接口
class SemaphoreBuilder
{
public:virtual ~SemaphoreBuilder() = default;virtual void BuildKey() = 0;virtual void SetPermission(int perm) = 0;virtual void SetSemNum(int num) = 0;virtual void SetInitVal(std::vector<int> initVal) = 0;virtual void Build(int flag) = 0;virtual void InitSem() = 0;virtual std::shared_ptr<Semaphore> GetSem() = 0;
};// 具體建造者類
class ConcreteSemaphoreBuilder : public SemaphoreBuilder
{
public:ConcreteSemaphoreBuilder() {}virtual void BuildKey() override{// 1. 構建鍵值std::cout << "Building a semaphore" << std::endl;_key = ftok(SEM_PATH.c_str(), SEM_PROJ_ID);if (_key < 0){std::cerr << "ftok failed" << std::endl;exit(1);}std::cout << "Got key: " << intToHex(_key) << std::endl;}virtual void SetPermission(int perm) override{_perm = perm;}virtual void SetSemNum(int num) override{_num = num;}virtual void SetInitVal(std::vector<int> initVal) override{_initVal = initVal;}virtual void Build(int flag) override{// 2. 創建信號量集合int semid = semget(_key, _num, flag | _perm);if (semid < 0){std::cerr << "semget failed" << std::endl;exit(2);}std::cout << "Got semaphore id: " << semid << std::endl;_sem = std::make_shared<Semaphore>(semid);}virtual void InitSem() override{if (_num > 0 && _initVal.size() == _num){// 3. 初始化信號量集合for (int i = 0; i < _num; i++){if (!Init(_sem->Id(), i, _initVal[i])){std::cerr << "Init failed" << std::endl;exit(3);}}}}virtual std::shared_ptr<Semaphore> GetSem() override{ return _sem; }
private:bool Init(int semid, int num, int val){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) */} un;un.val = val;int n = semctl(semid, num, SETVAL, un);if (n < 0){std::cerr << "semctl SETVAL failed" << std::endl;return false;}return true;}private:key_t _key;                      // 信號量集合的鍵值int _perm;                       // 權限int _num;                        // 信號量集合的個數std::vector<int> _initVal;       // 初始值std::shared_ptr<Semaphore> _sem; // 我們要創建的具體產品
};// 指揮者類
class Director
{
public:void Construct(std::shared_ptr<SemaphoreBuilder> builder, int flag, int perm = 0666, int num = defaultnum, std::vector<int> initVal = {1}){builder->BuildKey();builder->SetPermission(perm);builder->SetSemNum(num);builder->SetInitVal(initVal);builder->Build(flag);if (flag == BUILD_SEM){builder->InitSem();}}
};#endif // SEM_HPP

Writer.cc

#include "Sem_V.hpp"
#include <unistd.h>
#include <ctime>
#include <cstdio>int main()
{// 基于抽象接口類的具體建造者std::shared_ptr<SemaphoreBuilder> builder = std::make_shared<ConcreteSemaphoreBuilder>();// 指揮者對象std::shared_ptr<Director> director = std::make_shared<Director>();// 在指揮者的指導下,完成建造過程director->Construct(builder, BUILD_SEM, 0600, 3, {1, 2, 3});// 完成了對象的創建的過程,獲取對象auto fsem = builder->GetSem();// sleep(10);// SemaphoreBuilder sb;// auto fsem = sb.SetVar(1).build(BUILD_SEM, 1);srand(time(0) ^ getpid());pid_t pid = fork();// 我們期望的是,父子進行打印的時候,C或者F必須成對出現!保證打印是原子的.if (pid == 0){director->Construct(builder, GET_SEM);auto csem = builder->GetSem();while (true){// csem->P(0);printf("C");usleep(rand() % 95270);fflush(stdout);printf("C");usleep(rand() % 43990);fflush(stdout);// csem->V(0);}}while (true){// fsem->P(0);printf("F");usleep(rand() % 95270);fflush(stdout);printf("F");usleep(rand() % 43990);fflush(stdout);// fsem->V(0);}return 0;
}

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

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

相關文章

機器學習(11):嶺回歸Ridge

嶺回歸是失損函數通過添加所有權重的平方和的乘積(L2)來懲罰模型的復雜度。均方差除以2是因為方便求導&#xff0c;w_j指所有的權重系數, λ指懲罰型系數&#xff0c;又叫正則項力度特點:嶺回歸不會將權重壓縮到零&#xff0c;這意味著所有特征都會保留在模型中&#xff0c;但它…

調整Idea緩存目錄,釋放C盤空間

本文使用 Idea2024 Idea 會將一些配置默認緩存在C盤&#xff0c;使用久了會占用大量空間&#xff08;本人的Idea占用了將近5個G&#xff0c;以至于不得不進行遷移&#xff09; 緩存目錄主要涉及以下四個目錄&#xff0c;四個目錄可以分為兩組&#xff0c;每組目錄必須一起調整 …

手搓柵格工具-山體陰影

一、概述 山體陰影工具通過為柵格中的每個像元確定照明度&#xff0c;來獲取表面的假定照明度。 通過設置假定光源的位置并計算每個像元相對于相鄰像元的照明度值來實現此目的。 它可以顯著增強用于分析或圖形顯示的表面的可視化效果&#xff0c;尤其是在使用透明度時。 默認情…

Censtos docker安裝方法

#設置防火墻 systemctl stop firewalld.service setenforce 0 #安裝依賴包 yum install -y yum-utils device-mapper-persistent-data lvm2 #yum-utils&#xff1a;提供了 yum-config-manager 工具。 #device mapper&#xff1a; 是Linux內核中支持邏輯卷管理的通用設備映射機制…

單片機51 day46

單片機 一&#xff1a;基礎概念 一&#xff1a;單片機最小系統 單片機&#xff1a;電源時鐘&#xff08;晶振&#xff09;復位 //實現的最小組件 電源&#xff1a;5V直流 時鐘(晶振)&#xff1a;決定系統運行的速率 一般12M&#xff08;不超過50M&#xff09;&#xff0c…

【無標題】解鎖未來無線網絡的無限可能——Mesh自組網設備

在科技迅猛發展的今天&#xff0c;無線網絡已經成為了現代生活不可或缺的一部分。無論是在家庭中娛樂觀看視頻、在線游戲&#xff0c;還是在企業中進行辦公、遠程協作&#xff0c;網絡的穩定性和覆蓋范圍都直接影響著我們的使用體驗。傳統的Wi-Fi網絡在面臨多設備同時連接或大面…

Libevent(5)之使用教程(4)工具

Libevent(5)之使用教程(4)工具函數 Author: Once Day Date: 2025年8月3日 一位熱衷于Linux學習和開發的菜鳥&#xff0c;試圖譜寫一場冒險之旅&#xff0c;也許終點只是一場白日夢… 漫漫長路&#xff0c;有人對你微笑過嘛… 本文檔翻譯于&#xff1a;Fast portable non-blo…

Linux指令(3):

1. cal指令&#xff1a;我們的cal指令有日歷的意思看上面&#xff0c;我們輸入一個cal指令&#xff0c;可以查看當前月的日歷&#xff0c;我們給cal指令后面加上 - 3&#xff0c;他就會顯示這個月為中間的三個月的日歷&#xff0c;但是-4 不行&#xff0c;-5 也不行。只能 - 3。…

MLS平滑濾波

1.前言 最近在學習&#xff0c;因此查閱相關資料&#xff0c;該怎么表述感覺有些困難 2.代碼 2.1代碼1 使用全局坐標系 參考&#xff1a;python點云移動最小二乘法(Moving Least Squares)平滑_移動最小二乘法python-CSDN博客 def Moving_Least_Squares_Smoothing_v1_expla…

華為2288H V5服務器閃紅燈 無法開機案例

廣東某客戶1臺華為2288H V5服務器&#xff0c;由于單位外圍電力維修導致服務器有過一次異常斷電。結果來電之后發現服務器無法開機&#xff0c;開機面板上有個紅色心跳指示燈&#xff0c; 工程師到客戶現場后通過192.168.2.100登陸到2288H V5服務器的BMC管理口&#xff0c;打算…

SRIO入門之官方例程仿真驗證

仿真SRIO事務時序仿真之前先完成下面兩步操作&#xff1a;1.Vivado軟件版本2020.1&#xff0c;創建好工程及SRIO的IP核2.右鍵綜合化的IP核&#xff0c;然后選擇打開IP示例工程直接運行仿真分別將request和response兩個模塊添加到仿真窗口進行查看運行1000us左右就可以看到信號動…

CMake進階: 使用FetchContent方法基于gTest的C++單元測試

目錄 1.前言 2.FetchContent詳解 2.1.FetchContent簡介 2.2.FetchContent_Declare 2.2.1.簡介 2.2.2.關鍵特性 2.2.3.常見示例 2.3.FetchContent_MakeAvailable 2.3.1.簡介 2.3.2.核心功能與工作流程 2.3.3.示例用法 2.3.4.關鍵特性 2.3.5.常見問題與解決方案 3.…

亞馬遜廣告投放:如何減少無效曝光提高ROI

“為什么廣告花費高但轉化率低&#xff1f;”“如何判斷關鍵詞是否值得繼續投放&#xff1f;”“曝光量暴漲但訂單沒增加怎么辦&#xff1f;”“ACOS居高不下該如何優化&#xff1f;”“手動廣告和自動廣告的預算怎么分配&#xff1f;”如果你也在為這些問題頭疼&#xff0c;說…

Ethereum:擁抱開源,OpenZeppelin 未來的兩大基石 Relayers 與 Monitor

不知道大家是否注意到&#xff0c;OpenZeppelin 正在經歷一次重大的戰略轉型。他們決定在 2026 年 7 月 1 日正式關閉其廣受好評的 SaaS 平臺——Defender&#xff0c;并將重心全面轉向開源工具的建設。 這一舉動在社區引發了廣泛的討論&#xff0c;也標志著 OpenZeppelin 希望…

HFSS許可監控與分析

在電磁仿真領域&#xff0c;HFSS&#xff08;High Frequency Structure Simulator&#xff09;因其卓越的性能和廣泛的應用而受到用戶的青睞。然而&#xff0c;隨著企業和研究機構對HFSS使用需求的不斷增長&#xff0c;如何有效監控和分析HFSS許可證的使用情況&#xff0c;以確…

【前端:Html】--1.3.基礎語法

目錄 1.Html--文件路徑 2.Html--頭部元素 2.1.head元素 2.2.title元素 2.3.style元素 2.4.link元素 2.5.meta元素 2.6.script元素 2.7.base 3.Html--布局技巧 3.1.CSS Float 浮動布局 3.2.CSS Flexbox 布局 3.3.CSS Grid 網格布局 3.Html--響應式web設計 3.1.設…

Java 中 Nd4j 中的 INDArray 經過 reshape 之后數據丟失(rank = 0)

問題&#xff1a; 數據經過&#xff1a; INDArray inputArray Nd4j.create(input); // 將整個輸入數組轉換為 INDArray INDArray accs inputArray.get(NDArrayIndex.interval(0, imuNum * 3)).reshape(imuNum, 3, 1); // 加速度部分 INDArray oris inputArray.get(NDArrayIn…

正點原子阿波羅STM32F429IGT6移植zephyr rtos(四)---在獨立的應用工程里使用MPU6050

硬件平臺&#xff1a;正點原子阿波羅STM32F429IGT6 zephyr版本&#xff1a;Zephyr version 4.2.0 開發環境&#xff1a;wsl ubuntu 24.4 前景提要&#xff1a; 正點原子阿波羅STM32F429IGT6移植zephyr rtos&#xff08;三&#xff09;---創建一個獨立的應用工程-CSDN博客 一.修…

SAP_MMFI模塊-質保金標準解決方案詳解

一、業務背景 在許多企業的采購業務中,尤其是設備采購、工程項目或關鍵物料供應,通常會與供應商約定一筆質保金(或稱保留金)。這筆款項在貨物交付驗收后并不會立即支付,而是會被扣留一段時間(如一年),作為供應商產品質量的保證。 核心業務痛點: 在沒有系統化管理的…

Stanford CS336 assignment1 | Byte-Pair Encoding (BPE) Tokenizer

BPE一、 BPETrain1、 unicode standard and unicode encoding2、 子詞分詞(subword tokenization)3、 BPE的訓練a、 Vocabulary initializationb、 Pre-tokenizationc、 Compute BPE merges4、 train_BPE更多實現上的細節二、 BPETokenizerinit函數from_filesencodedecodeencod…