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

目录

信号量和P、V原语

信号量集结构体

信号量操作接口

semget

semctl

semop

封装Sem

关于建造者模式


信号量和P、V原语

信号量和 P、V 原语由 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 ignored
        int 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_op
        sem_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;
}
相关推荐
快乐的划水a3 天前
建造者模式及优化
设计模式·建造者模式
源代码•宸3 天前
深入浅出设计模式——创建型模式之建造者模式 Builder
c++·经验分享·设计模式·建造者模式
找不到、了3 天前
Java设计模式之<建造者模式>
java·设计模式·建造者模式
Amagi.10 天前
Java设计模式-建造者模式
java·设计模式·建造者模式
zhysunny11 天前
04.建造者模式的终极手册:从快餐定制到航天飞船的组装哲学
java·开发语言·建造者模式
郝学胜-神的一滴11 天前
建造者模式:构建复杂对象的优雅方式
开发语言·c++·程序人生·建造者模式
hqxstudying19 天前
Java行为型模式---策略模式
java·开发语言·建造者模式·适配器模式·策略模式
仍然探索未知中20 天前
基于建造者模式实现信号量
运维·服务器·建造者模式
vvilkim20 天前
深入理解设计模式:建造者模式详解
设计模式·建造者模式