07进程间的通信-信号量

1、信号量:

基本概念:

  • 1、多个进程或者线程可能同时访问的资源(变量、链表、文件等)称为共享资源也叫临界资源
  • 2、访问这些资源的代码称为临界代码,这些代码的区域称为临界区
  • 3、程序进入临界区之前必须要对资源进行申请,这个动作称为P操作(申请资源),就像汽车进入停车场,先要申请一张停车卡,P操作就是申请资源。如果申请成功,资源的数量就会减少。如果申请失败,要不就等待,要不就取消
  • 4、程序离开临界区之后,要释放响应的资源,这个动作被称为V操作,就像汽车离开停车场之后,会把停车卡还回去,V操作就是释放资源,释放资源就是让资源数增加
  • 信号量的P、V操作最核心的特征:他们是原子性的,他们确保在同一时刻只用一个进程或者线程可以进入临界区,从而避免竞争条件或则数据不一致的问题。保证程序正常、高效的运行

2、 获取信号量的ID

在这里插入图片描述
创建信号量时,还会受到以下系统信息的影响:

  • 1、SEMMNI:系统中信号量的总数最大值
  • 2、SEMMSL:每个信号量中信号量元素的最大个数
  • 3、SEMMNS:系统中所有信号量中的信号元素的总数最大值

3、对信号量进行PV操作

在这里插入图片描述
注意:

  • 1、信号量操作的结构体如下:

      struct sembuf
      {
      	unsigned short sem_num;//信号量元素的序号
      	short sem_op;//操作参数,PV操作
      	short sem_flag;//操作选项
      };
      注意:信号量元素的序号是从0开始的,实际上就是数组下标
    
  • 2、根据sem_op的值,信号量的操作分为三种情况:

    • ①、当sem_op大于0时:进行V操作,信号量元素的值(semval)会被加上sem_op的值(这意味着V操作会增加信号量的值,从而允许其他正在等待信号量资源的进程或则线程继续执行)。如果SEM_UNDO被设置了,那么该V操作会被系统记录。V操作永远不会被进程阻塞
    • ②、当sem_op等于0时:进行等零操作,如果此时semval正好为0,则semop( )立即成功返回,否则如果IPC_NOWAIT被设置,则立刻出错返回,并将error设置为EAGAIN,否则将使进程进入睡眠,直到以下情况发生:
      • semval变为0
      • 信号量被删除(将导致semop( )出错退出,返回EIDRM)
      • 收到信号(将导致semop( )出错,返回EINTR)
    • ③、当sem_op小于0时:进行P操作,信号量元素的值semval将会被减去sem_op的绝对值,如果 semval 大于或等于 sem_op 的绝对值,则 semop( )立即成功返回,semval 的值将减去 sem_op 的绝对值,并且如果 SEM_UNDO 被设置了,那么该 P 操作将会被系统记录。如果 semval 小于sem_op 的绝对值并且设置了IPC_NOWAIT,那么 semop( )将会出错返回且将错误码置为 EAGAIN否则将使得进程进入睡眠,直到以下情况发生:
      • semval 的值变得大于或者等于 sem_op 的绝对值。
      • 信号量被删除。(将导致 semop( )出错退出,错误码为 EIDRM)
      • 收到信号。(将导致 semop( )出错退出,错误码为 EINTR)

4、获取或者设置信号量的属性

在这里插入图片描述
练习:jack.c和rose.c进行通信

//jack.c 用于写入信息(申请空间资源)

#include "stdio.h"
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <sys/sem.h>

union semun
{
    int val;//当cmd为SETVAL时使用
    struct semid_ds *buf;//当cmd为IPC_STAT或IPC_SET时使用
    unsigned short *array;//当cmd为GETALL或SETALL时使用
    struct seminfo *_buf;//当cmd为IPC_INFO时使用
};

char* shm_Init(void)
{
    //获取key值
    int key = ftok("./", 'X');

    //获取ID
    int ID = shmget(key, 4096, IPC_CREAT | 0666);

    //映射共享内存到该进程
    char* shm_msg = shmat(ID, NULL, 0);
    if(shm_msg == (void *)-1)
    {
        printf("映射失败\n");
        exit(1);//直接退出程序
    }
    return shm_msg;
}

int sem_Init(void)
{
    //获取键值
    int key = ftok("./", 'V');

    //获取信号量的ID(信号元素为2个)
    int ID = semget(key, 2, IPC_CREAT | 0666);

    //初始化信号量的内容,主要是初始化他们的资源数
    //初始化没有数据,只有一个空间
    union semun set;
    set.val = 0;
    semctl(ID, 0, SETVAL, set);//初始化数据资源为0

    set.val = 1;
    semctl(ID, 1, SETVAL, set);//初始化空间资源为1

    //返回信号量的ID
    return ID;
}

int main()
{
    //申请共享内存并初始化
    char* shm_map = shm_Init();

    //初始化信号量
    int ID = sem_Init();

	// struct sembuf 
	// {
	//     unsigned short sem_num;//信号量元素序号,数组下标
	//     short sem_op;//操作参数
	//     short sem_flg;//操作选项
	// };
    //在写入共享内存之前需要先申请一个空间资源
    //对于jack来说,需要申请的资源是空间资源
    struct sembuf space = {
        .sem_num = 1,//需要设置的空间资源的元素下标为1
        .sem_flg = 0,//设置标记为0,啥也不选
        .sem_op = -1,//(信号量操作的标志)表示资源量将减1,这是P操作,用于申请资源
    };

    struct sembuf data = {
        .sem_num = 0,//需要设置的数据资源的元素下标为0
        .sem_flg = 0,//设置标记为0,不选
        .sem_op = 1,//(信号量操作的标志)表示资源量将加1,这是V操作,用于释放资源
    };
    /*在使用信号量来控制共享资源时,
    会先执行P操作申请资源,然后在操作完成后执行V操作释放资源,
    以确保共享资源在同一时刻只有一个进程或线程在访问。
    */

   while(1)
   {
        //等待空间资源 如果资源暂时不能得到则会阻塞等待(睡眠)
        printf("正在等待空间进行写入数据\n");
        semop(ID, &space, 1);
        printf("已经得到空间,正在写入数据\n");

        printf("请输入需要发送的数据\n");
        fgets(shm_map, 4096, stdin);

        //设置数据资源为1
        printf("设置数据为1\n");
        semop(ID, &data, 1);
   }
    return 0;
}

在jack.c中,space结构体用于申请空间资源(P操作),释放数据资源(V操作)。这是因为它需要确保在写入共享内存之前有足够的空间资源可用,同时写入完成后释放数据资源,允许rose.c进程读取。

//rose.c 用于读取信息(申请数据资源)

#include "stdio.h"
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <sys/sem.h>

union semun
{
    int val;//当cmd为SETVAL时使用
    struct semid_ds *buf;//当cmd为IPC_STAT或IPC_SET时使用
    unsigned short *array;//当cmd为GETALL或SETALL时使用
    struct seminfo *_buf;//当cmd为IPC_INFO时使用
};

// struct sembuf 
// {
//     unsigned short sem_num;//信号量元素序号,数组下标
//     short sem_op;//操作参数
//     short sem_flg;//操作选项
// };

char* shm_Init(void)
{
    //获取key值
    int key = ftok("./", 'X');

    //获取ID
    int ID = shmget(key, 4096, IPC_CREAT | 0666);

    //映射共享内存到该进程
    char* shm_msg = shmat(ID, NULL, 0);
    if(shm_msg == (void *)-1)
    {
        printf("映射失败\n");
        exit(1);//直接退出程序
    }
    return shm_msg;
}


int sem_Init(void)
{
    //获取键值
    int key = ftok("./", 'V');

    //获取信号量的ID(信号元素为2个)
    int ID = semget(key, 2, IPC_CREAT | 0666);

    //初始化信号量的内容,主要是初始化他们的资源数
    //初始化没有数据,只有一个空间
    union semun set;
    set.val = 0;
    semctl(ID, 0, SETVAL, set);//初始化数据资源为0

    set.val = 1;
    semctl(ID, 1, SETVAL, set);//初始化空间资源为1

    //返回信号量的ID
    return ID;
}

int main()
{
    //申请共享内存并初始化
    char* shm_map = shm_Init();

    //初始化信号量
    int ID = sem_Init();

    //在写入共享内存之前需要先申请一个空间资源
    //对于rose来说,与要==释放==的资源是空间资源
    struct sembuf space = {
        .sem_num = 1,//需要设置的空间资源的元素下标为1
        .sem_flg = 0,//设置标记为0,啥也不选
        .sem_op = 1,//(表示空间资源量加1)V操作,释放空间资源
    };

    //对于rose来说,需要==申请==的资源为数据资源
    //在写入共享内存之前需要先申请一个数据资源
    struct sembuf data = {
        .sem_num = 0,//需要设置的数据资源的元素下标为0
        .sem_flg = 0,//设置标记为0,不选
        .sem_op = -1,//(表示数据资源量减1)P操作,申请数据资源
    };
    

   while(1)
   {
        //等待数据资源 如果资源暂时不能得到则会阻塞等待(睡眠)
        printf("正在等待数据到达\n");
        semop(ID, &data, 1);
        printf("数据已经到达\n");

        printf("数据为:%s\n", shm_map);

        //设置空间资源设置为1
        printf("数据读取完毕,设置空间资源为1\n");
        semop(ID, &space, 1);
   }
    return 0;
}

在rose.c中,data结构体用于申请数据资源(P操作),释放空间资源(V操作)。这是因为它需要确保在读取共享内存之前有可用的数据资源,同时读取完成后释放空间资源,允许jack.c进程继续写入。
注意:

  • 注意:需要分清当前进程 需要申请的是什么资源。通过sem_op来设置;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值