Linux信号量


一、信号量常用函数

1. semget函数

  • 功能:创建或者获取信号量
  • 函数声明:int semget(key_t key, int nsems, int semflg);
  • 头文件<sys/sem.h>
  • key: 信号量的键值,是一个整数typedef unsigned int key_t,一般采用十六进制,例如0x5005,不同信号量的key不能相同。
  • nsems: 信号量的个数,如1,2
  • semflg: 信号量的访问权限,与文件的权限一样,例如0666|IPC_CREAT0666表示全部用户对它可读写,IPC_CREAT表示如果信号量不存在,就创建它,在头文件<sys/ipc.h>中声明。
  • 返回值: 成功返回信号量的id;失败返回-1,并设置errno
  • 使用ipcs -s命令可以查看系统的信号量,包括:键值key,信号量idsemid),拥有者(owner),权限(perms),大小(bytes
  • 使用ipcrm -s 信号量id命令可以手工删除信号量

2. semop函数

  • 功能:给信号量做pv操作,p则信号量减1v是信号量加1
  • 头文件<sys/sem.h>
  • 函数声明:int semop(int semid, struct sembuf *sops, size_t nsops);
  • semid: semget()返回的信号量唯一标识
  • sops: 头文件<sys/sem.h>中定义的结构体
struct sembuf {            
	unsigned short sem_num;  /* 信号的第几个。信号量的编号 */
    short          sem_op;   /* 信号量的pv操作,如果是-1则是p操作,如果是v则1*/
    short          sem_flg;  /*  如果信号量用于互斥锁,设置为SEM_UNDO;如果信号量用于生产消费者模型,设置为0。*/
};
  • nsops: 需要操作的信号量的个数
  • 返回值:成功返回信号量的标识符;失败返回-1
  • 注意:p操作把当前进程的运行状态设置为堵塞状态,直到另外1个进程唤醒。申请1个空的资源(信号量减1)如果申请成功直接退出,如果失败,进程堵塞,一直等待,直到有进程v操作为止。v操作负责把1个堵塞的进程唤醒(释放资源)(信号量加1)。信号量如果只有01两种状态,可用作互斥锁,1表示解锁,0表示加锁。因此,p操作就是加锁的过程,v操作就是解锁的过程。

3. semctl函数

  • 功能:删除、修改、获取、初始化信号量,可以理解为对信号量的一些操作,通过不同的入口参数来设置
  • 头文件<sys/sem.h>
  • 函数声明:
    常用两个重载版本:
    int semctl(int semid, int semnum, int cmd);用于信号量的删除以及信号量值的获取
    int semctl(int semid, int semnum, int cmd, union semun sem_union);用于信号量的初始化以及信号量的删除
  • semid: semget()返回的信号量唯一标识
  • semnum: 信号量的编号,使用单个信号量的时候默认是0
  • cmd: 操作信号的命令
    常用宏:
    IPC_RMID 删除信号量
    SETVAL 设置信号量的值
    GETVAL 获取信号量的值
  • sem_union: 自行定义的共同体
union semun {   //当前联合需要自行定义
     int              val;    /* 设置信号量的值 */
     struct semid_ds *buf;    /*IPC_STAT, IPC_SET的时候需要的buf结构体指针 */
     unsigned short  *array;  /* 设置多个信号量的时候 数组指针 */
     struct seminfo  *__buf;  /* IPC_INFO  */
}
  • 返回值:如果是GETVAL,成功返回信号量的值,失败返回-1;如果是SETVAL或者IPC_RMID,成功返回0 ,失败返回-1

二、信号量当作互斥锁使用

1.csemp类

  • _public.h
#ifndef _PUBLIC_HH
#define _PUBLIC_HH
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>   
class csemp                                                                                                                                                                  
{
private:
    union semun
    {   
        int val;
        struct semid_ds *buf;
        unsigned short *array;
        struct seminfo *__buf;
    };  
    int m_semid;
    short m_sem_flg;
    csemp(const csemp&) = delete;
    csemp& operator=(const csemp&) = delete;
public:
    csemp(): m_semid(-1) {}
    // 初始化信号量
    bool init(key_t key, unsigned short value=1, short sem_flg=SEM_UNDO);
    // P操作,加锁
    bool wait(short value=-1);
    // V操作,释放锁
    bool post(short value=1);
    int getValue();
    bool destroy();
    ~csemp() {}
};
#endif

2.成员函数实现

  • _public.cpp
#include <_public.h>

// 初始化
bool csemp::init(key_t key, unsigned short value, short sem_flg)
{
    // 如果已经初始化,无需再次初始化
    // 这里的初始化指的是将内存中的信号量绑定到进程中实例化的对象
    if (m_semid != -1) return false;
    m_sem_flg = sem_flg;
    // 获取信号量,如果获取不成功就创建
    if ((m_semid = semget(key, 1, 0666)) == -1)
    {
        // 如果失败的原因是内存中的信号量不存在,那么就创建信号量
        if (errno == ENOENT)
        {
            // IPC_EXCL保证只有一个进程可以创建信号量,其他进程只能获取
            if ((m_semid = semget(key, 1, 0666|IPC_CREAT|IPC_EXCL)) == -1)
            {
                // 如果失败的原因是信号量已经存在,那么就获取信号量,返回true
                if (errno == EEXIST)
                {
                    // 如果获取失败,就返回false
                    if ((m_semid = semget(key, 1, 0666) == -1))
                    {
                        perror("init 1 semget()");
                        return false;
                    }
                    return true;
                }
                                // 如果是其他原因导致的失败,那么返回false
                else                                                                                                                                                         
                {
                    perror("init 2 segment()");
                    return false;
                }
            }
            // 创建信号量成功之后,设置信号量的值
            // 实例化共同体成员对象
            union semun sem_union;
            sem_union.val = value;
            if (semctl(m_semid, 0, SETVAL, sem_union) < 0)
            {
                perror("init semctl()");
                return false;
            }
        }
        // 如果是其他原因导致的失败,则说明已存在的信号量无法被获取
        else
        {
            perror("init 3 semget()");
            return false;
        }
    }
    // 获取成功,直接返回true
    return true;
}

3. 信号量实现互斥锁

demo.cpp

#include <_public.h>

struct Turtle
{
    int age;                                                                                                                                                                 
    char name[51];
};

int main(int argc, char* argv[])
{
    if (argc != 3)
    {   
        cout << "入口参数过少" << endl;
        return -1; 
    }   
    int shmid = shmget(0x5001, sizeof(Turtle), 0640|IPC_CREAT);
    if (shmid == -1) 
    {   
        cout << "获取共享内存失败" << endl;
        return -1; 
    }   
    // 链接共享内存至当前进程
    Turtle* tur = (Turtle*)shmat(shmid, 0, 0); 

    cout << "age: " << tur->age << ' ' << "name: " << tur->name << endl;

    // 初始化信号量
    csemp mutex;
    if (mutex.init(0x6003) == -1)
    {
        cout << "初始化信号量失败" << endl;
        return -1;
    }

    // 加锁
    if (mutex.wait())
    {
        cout << "申请加锁成功!" << endl;
    }
    // 修改共享内存数据
    tur->age = atoi(argv[1]);
    strcpy(tur->name, argv[2]);
    sleep(10);

    cout << "age: " << tur->age << ' ' << "name: " << tur->name << endl;
    // 解锁
    if (mutex.post())
    {
        cout << "解锁成功!" << endl;
    }
    // 断开进程与共享内存的链接
    shmdt(tur);

   //删除共享内存(当真正需要删除共享内存时将注释解开即可)
   /*
   if (shmctl(shmid, IPC_RMID, 0) == -1)
   {
       cout << "共享内存删除失败" << endl;
       return -1;
   }*/

    return 0;
}

三、信号量用于生产者消费者模型

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值