linux--- 信号量

信号量

信号量主要用于同步和互斥

进程互斥

  • 由于有些资源是进程共享的,而且这些资源不可以同时被多个进程使用,所以这些进程竞争使用这些资源,这种进程间的关系叫做进程互斥。
  • 系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源
  • 涉及到临界资源的程序段为临界区

进程同步:指的是多个进程需要相互配合共同完成一项任务

P、V原语

P :申请信号量
V :释放信号量

信号量就类似于一个计数器,并不是让进程间能够直接发送字符串数据,而是通过自身计数器的性质,来完成进程之间的同步与互斥。


查看系统中的信号量集

ipcs -s

删除一个已经定义了的信号量集

ipcrm -s +semid

信号量集的结构

struct semid_ds
{
    struct ipc_perm sem_perm;    //包含最主要的元素是 key 和 mode 权限
    time_t sem_otime;  //
    time_t sem_ctime;
    unsigned short sem_nsems;
};

信号量集函数

  1. semget函数
    功能 :用来创建和访问一个信号量集
    原型 :int semget(key_t key, int nsems, int semflg);
    参数 :
    key :信号集的名字
    nsems :信号集中信号集的个数
    semflg :标志位
    返回值 :
    成功返回一个非负整数,即该信号集的标识码,失败返回-1
  2. semctl函数
    功能 :用来控制信号量集
    原型 :int semctl(int semid, int semnum, int cmd, ···);
    参数 :
    semid :由semget返回的信号集标码
    semnum :信号集中信号集的序号
    cmd :将要采取的动作(SETVAL、GETVAL、IPC_STAT、IPC_SET、IPC_RMID)
    最后一个参数根据cmd的选项而不同
    返回值 :
    成功返回0, 失败返回 -1。
    cmd :
    SETVAL :设置信号量集中的信号量的计数值
    GETVAL :获取信号量集中的信号量的计数值
    IPC_STAT :把semid_ds结构中的数据设置为信号集的当前关联值
    IPC_SET :在进程有足够权限下,把信号集的当前关联值设置为semid_ds数据结构中给出的值
    IPC_RMID :删除信号集
    若cmd为IPC_RMID,最后一个参数不需要使用
  3. semop函数
    原型 :int semop(int semid, struct sembuf *sops, unsigned nsops);
    功能 :用于实现PV操作,是原子操作
    参数 :
    semid :信号量集的标识码
    sops :指向一个结构数值的指针
    nsops :信号量的个数
    返回值 :
    成功返回0,失败返回 -1
    struct sembuf :
    struct sembuf
    {
    short sem_num;
    short sem_op;
    short sem_flg;
    };
    sem_num : 信号量的编号
    sem_op :信号量一次PV操作时加减的数值,一般只会用到两个值:
    一个是 -1 ,就是P操作,等待信号量变的可用
    另一个是 +1 ,就是V操作,发出信号量已经可用
    ser_flag :两个取值IPC_NOWAIT(P操作不成功,不等待)、SEM_UNDO(进程结束后,申请的信号量还原)


    实现简单的PV操作

common.h

#pragma once
#include <stdio.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>


#define PATH_NAME "." 
#define PROJ_ID 1

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) */
};

int CreateSem(int n);

int GetSem();

int DestroySem(int semid);

int SetSemValue(int semid, int index, int value);

int GetSemValue(int semid, int index, int *value);

int P(int semid, int index);

int V(int semid, int index);

common.c

#include "common.h"

//n为信号集中信号量的个数
int CommonSem(int n, int flags)    
{
   key_t key = ftok(PATH_NAME, PROJ_ID);
   if(key < 0)
   {
       perror("ftok");
       return -1;
   }
   int semid = semget(key, n,flags);
   if(semid < 0)
   {
       perror("semget");
       return -1;
   }
   return semid;
}

int CreateSem(int n)
{
    return CommonSem(n, IPC_CREAT | IPC_EXCL | 0666);
}

int GetSem()
{
    return CommonSem(0, IPC_CREAT);
}

int DestroySem(int semid)
{
    int ret = semctl(semid, 0, IPC_RMID);
    if(ret < 0)
    {
        perror("semctl");
        return -1;
    }
    return 0;
}

int SetSemValue(int semid, int index, int value)
{ 
    union semun _semun;
    _semun.val = value;
    int ret = semctl(semid, index, SETVAL, _semun);
    if(ret < 0)
    {
        perror("semctl");
        return -1;
    }
    return 0;
}

int GetSemValue(int semid, int index, int *value)
{
    //semctl返回值就是当前value
    int ret = semctl(semid, index, GETVAL);
    if(ret < 0)
    {
        perror("semctl");
        return -1;
    }
    *value = ret;
    return 0;
}

int Commonop(int semid, int index, int n)
{
    //不能直接将信号量减一再设置回去,这样的操作不是原子操作
    //P、V操作应该都是原子操作。
    struct sembuf _sembuf;
    _sembuf.sem_num = index;
    _sembuf.sem_op = n;
    _sembuf.sem_flg = 0;
    int ret = semop(semid, &_sembuf, 1);
    if(ret < 0)
    {
        perror("semop");
        return -1;
    }
    return 0;
}

int P(int semid, int index)
{
    return Commonop(semid, index, -1);
}

int V(int semid, int index)
{
    return Commonop(semid, index, 1);
}

test.c

#include "common.h"

int main()
{
    //int semid = GetSem();
    int semid = CreateSem(10);
    if(semid < 0)
    {
        printf("CreateSem fail");
        return 1;
    }
    printf("semid = %d\n",semid);

    int ret = SetSemValue(semid, 0, 1);
    if(ret < 0)
    {
        printf("SetSemValue failed");
        return 1;
    }

    int value = 0;
    GetSemValue(semid, 0, &value);
    printf("after value expect 1, actual %d\n", value);

    P(semid, 0);
    GetSemValue(semid, 0, &value);
    printf("after value expect 1, actual %d\n", value);

    V(semid, 0);
    printf("---------释放了一个资源后--------\n");

    P(semid, 0);
    GetSemValue(semid, 0, &value);
    printf("-------haha--------\n");

    DestroySem(semid);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_40331034/article/details/81239015
今日推荐