• 【Linux】System V 信号量


    一、信号量的概念理论渗透

    1.1 基本概念

    • 共享资源:多个执行流,可以看到的一份资源
    • 临界资源:被保护起来的资源 —— 保护的方式:同步和互斥
    • 互斥:任何时候只能有一个进程在访问共享资源
    • 资源,一定要被程序员进行访问的,我们使用代码进行访问, 代码 = 访问共享资源的代码(临界区) + 不访问共享资源的代码(非临界区)
    • 所谓的对共享资源进行保护——临界资源——本质上就是对访问共享资源的代码的保护

    1.2 什么是同步和互斥

    • 由于各进程要求共享资源,而且有些资源需要互斥使用,因此各进程间竞争使用这些资源,进程的这种关系为进程的互斥
    • 系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源。
    • 在进程中涉及到互斥资源的程序段叫临界区

    1.3 特性方面

           IPC资源必须删除,否则不会自动消除,除非重启,所以System V IPC资源的生命周期随内核

    二、如何理解信号量的理论

           信号量(信号灯):用于保护共享资源(临界资源)

           我们来举个例子:电影院买票,我们需要进行买票。我们在之前的共享内存是一个整体使用的,但是在电影院中,会有很多的座位供我们挑选。信号量本质上是一个计数器,当我们购买电影票时,我们需要进行电影票的预定,申请信号量的本质就是对公共资源的一种预定机制。

           信号量分为二元信号量和多元信号量,在二元信号量中,信号量的个数为1(相当于将临界资源看成一整块),二元信号量本质解决了临界资源的互斥问题,以下面的伪代码进行解释:

    1. while (1)
    2. {
    3. if(sem == 1)
    4. sum--;
    5. else
    6. // 挂起
    7. // 写入共享内存
    8. sem++;
    9. }

           根据以上代码,当进程A申请访问共享内存资源时,如果此时sem为1(sem代表当前信号量个数),则进程A申请资源成功,此时需要将sem减减,然后进程A就可以对共享内存进行一系列操作,但是在进程A在访问共享内存时,若是进程B申请访问该共享内存资源,此时sem就为0了,那么这时进程B会被挂起,直到进程A访问共享内存结束后将sem加加,此时才会将进程B唤起,然后进程B再对该共享内存进行访问操作。

           在这种情况下,无论什么时候都只会有一个进程在对同一份共享内存进行访问操作,也就解决了临界资源的互斥问题。

           实际上,代码中计数器sem减减的操作就叫做P操作,而计数器加加的操作就叫做V操作,P操作就是申请信号量,而V操作就是释放信号量。

                              

    三、信号量的操作

    3.1 信号量的函数

    3.1.1 semget函数

    函数的原型:

    函数的功能:

           创建一个新的信号量或获取一个已经存在的信号量的键值。
    函数的参数:

    • key:为整型值,用户可以自己设定。有两种情况:键值是IPC_PRIVATE,该值通常为0,意思就是创建一个仅能被进程进程给我的信号量。键值不是IPC_PRIVATE,我们可以指定键值,例如1234;也可以一个ftok()函数来取得一个唯一的键值。
    • nsems:表示初始化信号量的个数。比如我们要创建一个信号量,则该值为1.,创建2个就是2。
    • semflg:信号量的创建方式或权限。有IPC_CREAT,IPC_EXCL。IPC_CREAT如果信号量不存在,则创建一个信号量,否则获取。IPC_EXCL只有信号量不存在的时候,新的信号量才建立,否则就产生错误。

    函数的返回值:

           成功返回信号量的标识码ID。失败返回-1

    3.1.2 semctl函数

    函数的原型:

    函数的功能:

           控制信号量的函数,在这个函数中我们可以删除信号量或初始化信号量。

    函数的参数:

    • semid:信号量的标志码(ID),也就是semget()函数的返回值
    • semnum:操作信号在信号集中的编号。从0开始
    • cmd:表示要进行的操作

    cmd参数可以使用的命令:

    • IPC_STAT读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。
    • IPC_SET设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数。
    • IPC_RMID将信号量集从内存中删除。
    • GETALL用于读取信号量集中的所有信号量的值。
    • GETNCNT返回正在等待资源的进程数目。
    • GETPID返回最后一个执行semop操作的进程的PID。
    • GETVAL返回信号量集中的一个单个的信号量的值。
    • GETZCNT返回这在等待完全空闲的资源的进程数目。
    • SETALL设置信号量集中的所有的信号量的值。
    • SETVAL设置信号量集中的一个单独的信号量的值。

    函数的返回值:

           成功返回0,失败返回-1        

    3.1.3 semop函数

    函数的原型:

    函数的功能:

           用户改变信号量的值。也就是使用资源还是释放资源使用权
    函数的参数:

    • shmid:信号量的标识码。也就是semget()的返回值。
    • sops是一个指向结构体数组的指针。 
    1. struct   sembuf{
    2.      unsigned short  sem_num;//第几个信号量,第一个信号量为0;
    3.      short  sem_op;//对该信号量的操作。
    4.      short _semflg;
    5. };
    • nsops:操作结构的数量,恒大于或等于1。

    函数的返回值:

           成功返回0,失败返回-1

    四、信号量的指令

    1. // 查看系统中的信号量
    2. ipcs -s
    3. // 删除系统中的信号量
    4. ipcrm -s shmid

    五、操作系统如何把共享内存、消息队列、信号量管理起来

           通过对system V系列进程间通信的学习,可以发现共享内存、消息队列以及信号量,虽然它们内部的属性差别很大,但是维护它们的数据结构的第一个成员确实一样的,都是ipc_perm类型的成员变量。

           这样设计的好处就是,在操作系统内可以定义一个struct ipc_perm类型的数组,此时每当我们申请一个IPC资源,就在该数组当中开辟一个这样的结构。

  • 相关阅读:
    题目 1061: 二级C语言-计负均正
    人工智能、机器学习、深度学习:技术革命的深度解析
    《Qt5 Cadaques》学习笔记(六):QT QUICK Controls 2
    Redis哨兵模式
    【python】—— python的基本介绍并附安装教程
    什么是迭代器失效问题?
    JAVA 学习笔记 2年经验
    2023-2024年云赛道模拟题库
    C++ string 类相关知识
    【STC32G12K128开发板】——搭建开发环境
  • 原文地址:https://blog.csdn.net/2301_77868664/article/details/139384997