Linux进程通信之信号量

目录

1、信号量的特点

2、信号量的使用介绍

1.函数介绍

1.1semget函数

1.2semctl函数 

1.3 semop函数

3、代码示例

信号量与已经介绍过的 IPC 结构不同,它是一个计数器,是一种用于在多个进程或线程之间同步和通信的机制。它通常用于控制对共享资源的访问,以防止竞态条件的发生。

1、信号量的特点

  1. 信号量的操作是原子的,这意味着对信号量的操作是不可分割的。这确保了在多进程环境中对信号量的操作是线程安全的。
  2. 信号量是一个计数器,它可以表示可用资源的数量。当进程获取资源时,信号量的计数减少;当进程释放资源时,信号量的计数增加。
  3. 多个信号量可以被组合成一个信号量集。这样的信号量集可以一次性操作,简化了对多个信号量的管理。
  4. 信号量常常用于进程间的同步,确保在多进程环境中资源的正确共享和协调。信号量还可以用于控制对一定数量资源的访问。通过适当设置信号量的初值,可以限制对资源的并发访问。
  5. 与其他 进程通信IPC 对象一样,信号量具有持久性,它们在系统中创建并且可以一直存在,直到显式地被删除。

2、信号量的使用介绍

最简单的信号量是只能取 0 和 1 的变量,这也是信号量最常见的一种形式,叫做二值信号量(Binary Semaphore)。而可以取多个正整数的信号量被称为通用信号量。

Linux 下的信号量函数都是在通用的信号量数组上进行操作,而不是在一个单一的二值信号量上进行操作。

1.函数介绍

1.创建或获取一个信号量集

int semget(key_t key, int nsems, int semflg);

2.对信号量进行控制操作,其中 cmdSETVAL 时用于初始化信号量的值。

int semctl(int semid, int semnum, int cmd, ...);

3.对信号量进行操作改变信号量的值

int semop(int semid, struct sembuf *sops, size_t nsops);

1.1semget函数

当semget创建新的信号量集合时,必须指定集合中信号量的个数(即nsems),通常为1; 如果是引用一个现有的集合,则将nsems指定为 0 。

semflg为标志位

  • IPC_CREAT: 表示创建一个新的 IPC 对象(例如,信号量集、共享内存、消息队列)。
  • IPC_EXCL 用于创建一个新的唯一键值,如果已存在会使得创建失败,需与IPC_CREAT连用表示创建新的唯一键值
  • 一般我们使用 IPC_CREAT | 0666 来创建或获取信号量,存在时获取,不存在则创建
1.2semctl函数 

semctl函数用于对信号量集进行控制操作的函数。它提供了多种功能,通过传递不同的 cmd 参数来执行不同的操作。一般我们使用SETVAL用于设置信号量的初始值,IPC_RMID从系统中删除信号量集。

1.3 semop函数

nsops: sops数组中结构体的数量

struct sembuf *sops:是一个结构体

struct sembuf 结构体定义如下:

struct sembuf {
    unsigned short sem_num;  // 信号量在信号量集中的索引
    short sem_op;            // 操作类型(负值表示 P 操作,正值表示 V 操作,
    short sem_flg;           // 操作标志
};

当 sem_op的值为0时,P 操作是一种特殊情况。这时,P 操作只会检查信号量的当前值,而不进行实际的减法操作。如果信号量的值为正数,P 操作将成功执行,信号量的值不变。如果信号量的值为0,P 操作将被阻塞,等待信号量的值变为正数。

3、代码示例

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

// P 操作(等待信号量值变为 0)
void Pgetsem(int semid) {
    struct sembuf sops;
    sops.sem_num = 0;
    sops.sem_op = -1;
    sops.sem_flg = SEM_UNDO;
    if (semop(semid, &sops, 1) == -1) {
        perror("P 操作失败");
        exit(-1);
    }
    printf("----- P 操作\n");
}

// V 操作(增加信号量值)
void Vgetsem(int semid) {
    struct sembuf sops;
    sops.sem_num = 0;
    sops.sem_op = 1;
    sops.sem_flg = SEM_UNDO;
    if (semop(semid, &sops, 1) == -1) {
        perror("V 操作失败");
        exit(-1);
    }
    printf("++++++++ V 操作\n");
}

int main() {
    int key = ftok(".", 5);
    if (key == -1) {
        perror("ftok 失败");
        exit(-1);
    }

    int semid = semget(key, 1, IPC_CREAT | 0666);
    if (semid == -1) {
        perror("semget 失败");
        exit(-1);
    }

    union semun tmp;
    if (semctl(semid, 0, SETVAL, 0) == -1) {
        perror("semctl SETVAL 失败");
        exit(-1);
    }

    int pid = fork();
    if (pid == -1) {
        perror("fork 失败");
        exit(EXIT_FAILURE);
    }

    if (pid == 0) {
        // 子进程
        sleep(5);
        printf("子进程\n");
        Vgetsem(semid);
    } else {
        // 父进程
        Pgetsem(semid);
        printf("父进程\n");
        // 删除信号量,注意要确保在删除信号量集之前没有其他进程正在使用它
        if (semctl(semid, 0, IPC_RMID) == -1) {
            perror("semctl IPC_RMID 失败");
            exit(-1);    
        }
    }
    return 0;
}

  • 18
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值