进程通信_信号量

钥匙:信号量
房间:临界资源
信号量集:linuxx API
P操作:拿锁 (获取资源)
V操作:放回锁 (释放资源)

semget函数

semget 是 System V 信号量体系中的一个核心函数,用于创建新的信号量集获取已存在的信号量集

函数原型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semget(key_t key, int nsems, int semflg);
参数说明

1. key_t key

  • 作用:信号量集的键值,用于唯一标识信号量集
  • 生成方式
    • 使用 IPC_PRIVATE(值为0):每次调用都创建新的信号量集
    • 使用 ftok() 函数生成:基于文件路径和项目ID生成唯一键值
      2. int nsems
  • 作用:指定要创建或获取的信号量集中信号量的数量
  • 注意事项
    • 如果创建新信号量集,必须指定 nsems > 0
    • 如果获取已存在的信号量集,可将 nsems 设为0
      3. int semflg
  • 作用:标志位,控制信号量集的创建和访问权限
  • 组成:权限标志(9位)与创建标志的按位或
  • 常用标志
    • IPC_CREAT:如果信号量集不存在,则创建
    • IPC_EXCL:与 IPC_CREAT 一起使用,如果信号量集已存在则失败
    • 权限标志:如 06660644 等(与文件权限类似)
      返回值
  • 成功:返回信号量集的标识符(非负整数)
  • 失败:返回 -1,并设置 errno

semop函数

semop 是 System V 信号量系统中用于执行信号量操作的核心函数,它允许进程对信号量进行原子性的 P(等待)和 V(信号)操作。

函数原型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semop(int semid, struct sembuf *sops, size_t nsops);
参数说明

1. int semid

  • 作用:信号量集标识符,由 semget() 调用返回
  • 含义:指定要操作的信号量集
  1. struct sembuf *sops
  • 作用:指向操作数组的指针,每个元素描述一个信号量操作
  • 结构定义
struct sembuf {
        unsigned short sem_num;  // 信号量在集合中的索引(0-based)
        short          sem_op;   // 操作类型(正数、负数或零)
        short          sem_flg;  // 操作标志(如IPC_NOWAIT、SEM_UNDO)
    };

3. size_t nsops

  • 作用:指定操作数组 sops 中的操作数量
  • 含义:一次调用中可以同时对多个信号量进行操作
    ##### 操作类型 (sem_op)(结构体配置的内容)
  1. 正数操作 (sem_op > 0)
  • 作用:执行 V 操作(信号/释放)
  • 行为:将信号量的值增加 sem_op
  • 效果:通常用于释放资源,唤醒等待的进程
  1. 负数操作 (sem_op < 0)
  • 作用:执行 P 操作(等待/获取)
  • 行为
    • 如果信号量值 ≥ |sem_op|:立即减少信号量值
    • 如果信号量值 < |sem_op|:阻塞等待,直到条件满足
  • 效果:用于获取资源,如果资源不足则等待
  1. 零操作 (sem_op == 0)
  • 作用:测试信号量值是否为0
  • 行为
    • 如果信号量值 = 0:立即返回
    • 如果信号量值 ≠ 0:阻塞等待,直到信号量值变为0
  • 效果:用于等待特定条件发生 操作标志 (sem_flg) 1. IPC_NOWAIT
  • 作用:非阻塞模式
  • 行为:如果操作不能立即完成,不阻塞进程而是立即返回错误
  • 错误码:设置 errno 为 EAGAIN
    2. SEM_UNDO
  • 作用:启用”撤销”功能
  • 行为:当进程异常终止时,系统会自动撤销该进程对信号量所做的所有操作
  • 用途:防止进程意外终止导致信号量处于不一致状态
    返回值
  • 成功:返回 0
  • 失败:返回 -1,并设置 errno

semctl函数

semctl 是 System V 信号量系统中用于控制信号量操作的函数,它提供了多种对信号量集的管理功能,包括初始化、获取状态、设置值和删除等操作。

函数原型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semctl(int semid, int semnum, int cmd, ...);
参数说明

1. int semid

  • 作用:信号量集标识符,由 semget() 返回
  • 含义:指定要操作的信号量集
    2. int semnum
  • 作用:信号量在集合中的编号(索引)
  • 取值范围:从 0 到 nsems-1
  • 特殊值:对于某些命令(如 IPC_RMID),此参数被忽略
    3. int cmd
  • 作用:控制命令,指定要执行的操作类型
  • 这是最重要的参数,决定了函数的行为
    4. ...(可变参数)
  • 作用:根据不同的 cmd,可能需要传入额外的参数
  • **通常是一个 union semun 类型的联合体
    union semun 联合体
    这个联合体需要用户自己定义,因为它没有在标准头文件中定义:
union semun {
    int              val;    // 用于SETVAL
    struct semid_ds *buf;    // 用于IPC_STAT和IPC_SET
    unsigned short  *array;  // 用于GETALL和SETALL
    struct seminfo  *__buf;  // 用于IPC_INFO(Linux特定)
};
参数说明2

这个调用非常重要,它的作用是设置信号量的初始值

semctl(semid, 0, SETVAL, arg)

将信号量集 semid 中的第 0 个信号量的值设置为 arg.val

  1. 新创建的信号量值是不确定的
    当你使用 semget() 创建新的信号量集时,系统不会自动设置信号量的值,它们的初始值是未定义的(可能是任意值)。
  2. 确保正确的同步行为
    信号量的值决定了同步行为:
  • 值为 1:二进制信号量(互斥锁),表示资源可用
  • 值为 0:表示资源已被占用,需要等待
  • 值为 N:计数信号量,表示有 N 个资源可用
    如果不初始化,信号量可能从一个随机值开始,导致程序行为不可预测。
参数详解

semid

  • 信号量集标识符,由 semget() 返回
  • 指定要操作哪个信号量集
    0
  • 信号量在集合中的索引(从0开始)
  • 这里表示操作第一个信号量
    SETVAL
  • 命令字,表示”设置值”
  • 告诉 semctl 我们要设置信号量的值
    arg
  • union semun 类型的参数
  • 必须包含要设置的初始值:arg.val = 初始值
常用命令 (cmd)
  1. 控制整个信号量集的命令
    IPC_RMID – 立即删除信号量集
  • 作用:删除信号量集并唤醒所有等待的进程
  • 参数semnum 被忽略,第四个参数不需要
  • 示例
semctl(semid, 0, IPC_RMID);

IPC_STAT – 获取信号量集状态信息

  • 作用:将信号量集的数据结构复制到提供的缓冲
  • 参数:第四个参数是 struct semid_ds *
  • 示例
struct semid_ds ds;
    union semun arg;
    arg.buf = &ds;
    semctl(semid, 0, IPC_STAT, arg);

IPC_SET – 设置信号量集权限

  • 作用:修改信号量集的权限和所有者
  • 参数:第四个参数是 struct semid_ds *
  • 示例
struct semid_ds ds;
    union semun arg;

    // 先获取当前状态
    arg.buf = &ds;
    semctl(semid, 0, IPC_STAT, arg);

    // 修改权限
    ds.sem_perm.mode = 0644;

    // 设置新权限
    semctl(semid, 0, IPC_SET, arg);
  1. 控制单个信号量的命令
    GETVAL – 获取信号量的当前值
  • 作用:返回指定信号量的当前值
  • 参数:不需要第四个参数
  • 返回值:信号量的当前值(非负整数)
  • 示例
int value = semctl(semid, 2, GETVAL);
    printf("信号量2的值: %d\n", value);

SETVAL – 设置信号量的值

  • 作用:设置指定信号量的值
  • 参数:第四个参数是 union semun,使用 val 字段
  • 示例
union semun arg;
    arg.val = 5;  // 设置值为5
    semctl(semid, 2, SETVAL, arg);

GETPID – 获取最后操作进程的PID

  • 作用:返回最后对该信号量执行 semop() 的进程ID
  • 参数:不需要第四个参数
  • 示例
pid_t last_pid = semctl(semid, 1, GETPID);

GETNCNT – 获取等待进程数

  • 作用:返回等待信号量值增加的进程数量
  • 参数:不需要第四个参数
  • 示例
int waiting = semctl(semid, 0, GETNCNT);
    printf("%d个进程正在等待信号量增加\n", waiting);

GETZCNT – 获取等待零的进程数

  • 作用:返回等待信号量值变为0的进程数量
  • 参数:不需要第四个参数
  • 示例
int waiting_zero = semctl(semid, 0, GETZCNT);
  1. 控制整个信号量集值的命令
    GETALL – 获取所有信号量的值
  • 作用:将信号量集中所有信号量的值复制到数组中
  • 参数:第四个参数是 unsigned short * 数组
  • 示例
unsigned short values[3];  // 假设信号量集有3个信号量
    union semun arg;
    arg.array = values;
    semctl(semid, 0, GETALL, arg);

    for (int i = 0; i < 3; i++) {
        printf("信号量%d的值: %d\n", i, values[i]);
    }

SETALL – 设置所有信号量的值

  • 作用:使用数组中的值设置信号量集中所有信号量的值
  • 参数:第四个参数是 unsigned short * 数组
  • 示例
unsigned short new_values[3] = {1, 0, 5};
    union semun arg;
    arg.array = new_values;
    semctl(semid, 0, SETALL, arg);
返回值
  • 成功:取决于执行的命令:
    • GETVALGETPIDGETNCNTGETZCNT:返回相应的整数值
    • 其他命令:返回 0
  • 失败:返回 -1,并设置 errno

信号量编程实现一

可以看一下二的,从一基础下写的,注释更好点

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

       //int semget(key_t key, int nsems, int semflg);
       //int semctl(int semid, int semnum, int op, ...);

 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 main()
{
    key_t key;//
    int semid;//信号量的id存储变量

    key = ftok(".", '2');//创建key值
    semid = semget(key, 1, IPC_CREAT|0666);//key值,1个信号量,IPC_CREAT|0666如果信号量不存在就创建,信号量权限可读可写

    union semun initsem;//联合体
    initsem.val = 1;

    semctl(semid, 0, SETVAL, initsem);//信号量的id,操作第一个信号量(都是从0数起),SETVAL设置值,值设为1(initsem.val = 1;)

    int pid = fork();//创建进程
    if(pid > 0)//父进程
    {
        printf("this is father\n");
    }
    else if(pid == 0)//子进程
    {
        printf("this is son\n");
    }
    else//错误
    {
        printf("fork error\n");
    }


    return 0;
}
image.png

信号量编程实现二

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

       //int semget(key_t key, int nsems, int semflg);
       //int semctl(int semid, int semnum, int op, ...);


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

void pGetKey(int id)
{
    struct sembuf set;//semop的操作结构体

    set.sem_num = 0;//第一的信号量
    set.sem_op = -1;//p操作
    set.sem_flg = SEM_UNDO;//进程异常终止时自动撤销操作,防止锁死

    semop(id, &set, 1);//信号量id,操作结构体,只有一个信号量
    printf("getkey\n");
}

void vPutBackKey(int id)
{
    struct sembuf set;

    set.sem_num = 0;
    set.sem_op = 1;  //V操作
    set.sem_flg = SEM_UNDO;

    semop(id, &set, 1);
    printf("put back the key\n");
}



int main()
{
    key_t key;//
    int semid;//信号量的id存储变量

    key = ftok(".", '2');//创建key值,基于当前目录和字符'2'
    semid = semget(key, 1, IPC_CREAT|0666);//key值,1个信号量,IPC_CREAT|0666如果信号量不存在就创建,信号量权限可读可写

    union semun initsem;//联合体,给
    initsem.val = 0;//无可用资源

    semctl(semid, 0, SETVAL, initsem);//信号量的id,操作第一个信号量(都是从0数起),SETVAL设置值,值设为1(initsem.val = 1;)

    int pid = fork();//创建进程
    if(pid > 0)//父进程
    {
        pGetKey(semid);//获取资源,没有就等待   
        printf("this is father\n");
        vPutBackKey(semid);//释放资源
    }
    else if(pid == 0)//子进程
    {
        printf("this is son\n");
        vPutBackKey(semid);//释放资源 
    }
    else//错误
    {
        printf("fork error\n");
    }


    return 0;
}
image.png

重点

  • semget函数 创建信号量
  • semop函数 执行P/V操作
    1. 正数操作 (sem_op > 0)
      • 作用:执行 V 操作(信号/释放)
      • 行为:将信号量的值增加 sem_op
      • 效果:通常用于释放资源,唤醒等待的进程
    2. 负数操作 (sem_op < 0)
      • 作用:执行 P 操作(等待/获取)
      • 行为
        • 如果信号量值 ≥ |sem_op|:立即减少信号量值
        • 如果信号量值 < |sem_op|:阻塞等待,直到条件满足
      • 效果:用于获取资源,如果资源不足则等待
  • semctl函数 控制信号量操作
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇