当前位置: 首页 > news >正文

进程间通信——信号量

进程间通信——信号量

目录

一、基本概念 

1.1 概念

1.2 基本操作

1.3 相关函数

1.3.1 semget创建/获取

1.3.2 semop操作信号量

1.3.3 semctl初始化/删除

二、代码操作

2.1 不用PV的

2.2 用PV 的

2.2.1 a.c

2.2.2 b.c

2.2.3 sem.h

 2.2.4 sem.c


一、基本概念 

1.1 概念

信号量本质上是一个计数器,用于记录可用资源的数量。它有两种类型的操作:P 操作(也称为 wait 操作)和 V 操作(也称为 signal 操作)。P 操作会将信号量的值减 1,表示请求一个资源V 操作会将信号量的值加 1,表示释放一个资源

当信号量的值为 0 时,表示没有可用资源,此时进程在请求资源时会被阻塞,直到其他进程释放资源。

临界资源:同一时刻只允许一个进程访问的资源

临界区:访问临界资源的代码段。这段代码执行了就会访问例如打印机之类的临界资源

1.2 基本操作

初始化:在使用信号量之前,需要对其进行初始化,设置初始值。这个值通常表示系统中某种资源的初始数量。
P 操作:当一个进程需要访问共享资源时,它首先执行 P 操作。如果信号量的值大于 0,那么 P 操作会成功执行,进程可以继续执行并使用资源,同时信号量的值减 1。如果信号量的值为 0,那么进程会被阻塞,放入与该信号量相关的等待队列中,直到其他进程执行 V 操作释放资源。
V 操作:当进程使用完共享资源后,它执行 V 操作。V 操作会将信号量的值加 1,如果有其他进程在等待该资源(即信号量的等待队列不为空),那么系统会从等待队列中唤醒一个进程,让它继续执行 P 操作以获取资源。

1.3 相关函数

1.3.1 semget创建/获取

semget 函数用于创建一个新的信号量集或获取一个已存在的信号量集的标识符

#include <sys/sem.h>

int semget(key_t key, int nsems, int semflg);
  • key:一个键值,用于标识信号量集。可以使用 ftok 函数生成一个唯一的键值,也可以使用特殊值 IPC_PRIVATE 来创建一个私有的信号量集,该信号量集只能由创建它的进程及其子进程访问。
  • nsems:指定信号量集中信号量的数量。如果是创建新的信号量集,必须指定该值;如果是获取已存在的信号量集,该值通常为 0。
  • semflg:标志位,用于指定创建信号量集的模式和权限等。常见的标志有:
    • IPC_CREAT:如果信号量集不存在,则创建它。
    • IPC_EXCL:与 IPC_CREAT 一起使用,若信号量集已存在,则返回错误。
    • 权限标志,如 0666 表示所有用户都有读写权限。
  • 成功时,返回一个非负整数,即信号量集的标识符(semid)。
  • 失败时,返回 -1,并设置 errno 来指示错误类型。

1.3.2 semop操作信号量

semop 函数用于对信号量集中的一个或多个信号量进行操作,如 P 操作(申请资源)和 V 操作(释放资源)

#include <sys/sem.h>

int semop(int semid, struct sembuf *sops, unsigned nsops);
  • semid:信号量集的标识符,由 semget 函数返回。
  • sops:指向一个 struct sembuf 结构体数组的指针,每个 struct sembuf 结构体描述了对一个信号量的操作。struct sembuf 结构体的定义如下:
  • struct sembuf {
        unsigned short sem_num;  // 信号量在信号量集中的索引(从 0 开始)
        short          sem_op;   // 操作类型,正数表示释放资源(V 操作),负数表示申请资源(P 操作),0 表示等待信号量值为 0
        short          sem_flg;  // 标志位,常见的有 IPC_NOWAIT 表示非阻塞操作
    };
  • nsopssops 数组中元素的数量,即要执行的操作数量。

1.3.3 semctl初始化/删除

semctl 函数用于对信号量集进行控制操作,如初始化信号量的值、获取信号量的状态信息、删除信号量集等。

#include <sys/sem.h>

int semctl(int semid, int semnum, int cmd, ...);
  • semid:信号量集的标识符,由 semget 函数返回。
  • semnum:信号量在信号量集中的索引(从 0 开始)。如果 cmd 不需要指定特定的信号量,则该值通常为 0。
  • cmd:要执行的控制命令,常见的命令有:
    • SETVAL:设置指定信号量的值,需要传递一个 int 类型的参数作为新值。
    • GETVAL:获取指定信号量的值,返回值即为信号量的当前值。
    • IPC_RMID:删除信号量集,不需要指定 semnum,也不需要额外的参数。
  • ...:可变参数,根据 cmd 的不同而有所不同。例如,当 cmd 为 SETVAL 时,需要传递一个 int 类型的参数作为新的信号量值。
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "sem.h"

int main()
{
    sem_init(); // 创建并初始化信号量
    for(int i=0; i<5; i++)
    {
        sem_p();
        printf("A");
        fflush(stdout);
        int n = rand() % 3; // 随机睡眠一段时间
        sleep(n);
        printf("A");
        fflush(stdout);
        sem_v();
        n = rand() % 3;
        sleep(n);
    }

    sleep(10);
    sem_destroy(); // 删除信号量
}

二、代码操作

2.1 不用PV的

a.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main()
{
   for(int i=0;i<5;i++)
  {
    printf("A");
    fflush(stdout);
    int n=rand()%3;
    sleep(n);
    printf("A");
    fflush(stdout);
  }
}

b.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main()
{
   for(int i=0;i<5;i++)
  {
    printf("B");
    fflush(stdout);
    int n=rand()%3;
    sleep(n);
    printf("B");
    fflush(stdout);
  }
}

 

  1. /a &./b& :在后台同时运行可执行文件 a 和 b ,[1] 10916 和 [2] 10917 分别是这两个后台进程的作业编号和进程号。

2.2 用PV 的

2.2.1 a.c

a.c使用信号量进行进程同步的C语言程序示例。

它演示了如何使用信号量来控制对共享资源的访问,以防止数据竞争和条件竞争

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "sem.h"

int main()
{
    sem_init();//创建并初始化信号量
    //p
    for(int i = 0; i < 5; i++ )
    {
        sem_p();
        printf("A");
        fflush(stdout);
        int n = rand() % 3;
        sleep(n);
        printf("A");
        fflush(stdout);
        sem_v();
        
        n = rand()%3;
        sleep(n);
    }
    //v
    sleep(10);
    sem_destroy();//删除信号量
}

2.2.2 b.c

 b.c它演示了如何使用信号量来控制对共享资源的访问。

在这个例子中,程序通过信号量来确保在打印字符'B'时不会有多个线程或进程同时执行,从而避免输出混乱。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "sem.h"
int main()
{
    sem_init();
    for(int i = 0; i < 5; i++ )
    {
        sem_p();
        printf("B");
        fflush(stdout);
        int n = rand() % 3;
        sleep(n);
        printf("B");
        fflush(stdout);
        sem_v();

        n = rand()%3;
        sleep(n);
    }


}

2.2.3 sem.h

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/sem.h>

union semun
{
    int val;
};

void sem_init();
void sem_p();
void sem_v();
void sem_destroy();

定义了一组与信号量操作相关的函数原型和一个用于信号量操作的联合体semun

这个联合体用于在信号量操作中传递信号量的值

这些函数原型定义了信号量操作的接口,下面是这些函数可能的实现方式:

  1. sem_init:初始化信号量。

  2. sem_p:执行信号量的P操作(wait操作),减少信号量的值,如果信号量的值小于0,则进程将被阻塞。

  3. sem_v:执行信号量的V操作(signal操作),增加信号量的值,如果有进程因等待此信号量而被阻塞,则它们可能会被唤醒。

  4. sem_destroy:销毁信号量,释放相关资源。

 2.2.4 sem.c

信号量操作实现

#include "sem.h"
static int semid = -1;

void sem_init()
{
    semid = semget((key_t)1234,1,IPC_CREAT|IPC_EXCL|0600);//尝试创建一个新的信号了
    if( semid == -1 )//信号量可能存在
    {
        semid = semget((key_t)1234,1,0600);
        if( semid == -1)
        {
            printf("sem err\n");
            return;
        }
    }
    else//初始化 1
    {
       union semun a; 
       a.val = 1;
       if( semctl(semid,0,SETVAL,a) == -1)
       {
            printf("semctl err\n");
       }
    }
}
void sem_p()
{
    struct sembuf buf;
    buf.sem_num = 0;
    buf.sem_op = -1;//p
    buf.sem_flg = SEM_UNDO;//
    if( semop(semid,&buf,1) == -1)//可能阻塞
    {
        printf("p err\n");
    }

}
void sem_v()
{
    struct sembuf buf;
    buf.sem_num = 0;
    buf.sem_op = 1;//v
    buf.sem_flg = SEM_UNDO;//
    if( semop(semid,&buf,1) == -1)
    {
        printf("v err\n");
    }
}
void sem_destroy()
{
    if( semctl(semid,0,IPC_RMID) == -1)
    {
        printf("rm semid err\n");
    }
}

sem_init 函数 初始化一个信号量

相关文章:

  • git 如何统计还尚未合并完成的文件
  • UE4学习笔记 FPS游戏制作31 显示计分板
  • flex和bison笔记
  • 2025最新“科研创新与智能化转型“暨AI智能体开发与大语言模型的本地化部署、优化技术实践
  • 【MySQL基础-14】MySQL的INSERT语句详解:高效数据插入的艺术
  • 数据特征的判断
  • 机器学习算法
  • mysql不能远程访问可能有哪些原因,及如何解决
  • ubuntu 创建新用户
  • 权值线段树算法讲解及例题
  • 性能测试理论基础-测试流程及方案设计要点
  • 内联函数/函数重载/函数参数缺省
  • 211 本硕研三,已拿 C++ 桌面应用研发 offer,计划转音视频或嵌入式如何规划学习路线?
  • 前端框架入门:Angular
  • Flutter中实现拍照识题的功能
  • Starrocks架构及如何选择
  • 60V单通道高精度线性恒流LED驱动器防60V反接SOD123封装
  • 智能物流调度:AI如何让快递更快更省?
  • 04-SpringBoot3入门-配置文件(多环境配置)
  • Android 设备实现 adb connect 连接的步骤
  • 马上评|“衣服越来越难买”,对市场是一个提醒
  • 2025财政观察|长三角“三公”经费普降,钱要用在刀刃上
  • 新能源汽车,告别混乱创新
  • 多地警务新媒体整合:关停交警等系统账号,统一信息发布渠道
  • 脑血管支架:救命神器还是定时炸弹?听听医生的大实话
  • 线下哪些商家支持无理由退货?查询方法公布