当前位置: 首页 > news >正文

【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.dtcms.com/a/314158.html

相关文章:

  • Go语言流式输出技术实现-服务器推送事件(Server-Sent Events, SSE)
  • Git 与 GitHub 协作
  • BackgroundTasks 如何巧妙驾驭多任务并发?
  • 9. Linux 交换空间管理
  • [GESP202306 四级] 2023年6月GESP C++四级上机题超详细题解,附带讲解视频!
  • 事件(二)实战案例
  • Do-Calculus:因果推断的演算基础与跨领域应用
  • 17.6 超拟人大模型CharacterGLM技术解析:92.7%角色一致性+虚拟偶像互动提升300%,如何吊打GPT-4?
  • Maya 2024安装指南及安装包下载
  • UILabel设置字重
  • Coze Loop:开源智能体自动化流程编排平台原理与实践
  • Ethereum: 深度解析Web3世界的合规之门, ERC-1400证券型代币标准
  • Oracle ASH的手册
  • Linux定制篇-Tomcat的安装和配置
  • Druid学习笔记 03、Druid的AstNode类详解与其他产品测试体验
  • 【精品项目】进阶版贪吃蛇:现代Web技术打造的经典游戏重生
  • 从零认识OpenFlow
  • TCP为什么采用三次握手而不是二次握手
  • 使用 Marian 进行机器翻译详解及对应案例
  • 在安卓中使用 FFmpegKit 剪切视频并添加文字水印
  • Android进程基础:Zygote
  • (JAVA)自建应用调用企业微信API接口,设置企业可信IP
  • 开疆智能ModbusTCP转Profient网关连接ER机器人配置案例
  • DPDK中的TCP头部处理
  • 第五篇: 深入解析基于 SQLAlchemy 的聊天记录持久化模块:`message_model` 与数据库操作封装
  • 高速信号设计之 PCIe6.0 篇
  • Windows中Idea或者其他开发工具如何使用Google Sans Code - 码农开源等宽字体
  • 数据结构:如何判断一个链表中是否存在环(Check for LOOP in Linked List)
  • JSqlParser学习笔记 快速使用JSqlParser
  • 从exec到Shell:深度解析Linux进程等待,程序替换与自主Shell实现