什么是信号量?如何在Linux内核中使用信号量?

在这里插入图片描述

🥇今日学习目标:什么是信号量?如何在Linux内核中使用信号量?
🤵‍♂️ 创作者:JamesBin
⏰预计时间:10分钟
🎉个人主页:嵌入式悦翔园个人主页
🍁专栏介绍:Linux驱动开发100问

一、前言

当多个进程或线程同时访问共享资源时,容易出现竞争条件(race condition)问题,从而导致程序运行出错。为了避免这种情况,需要使用同步机制来协调进程或线程的访问,而信号量(Semaphore)就是一种常用的同步机制。本文将介绍信号量的基本概念、原理以及在Linux内核中的使用方法。

二、什么是信号量?

信号量是一种计数器,用于控制对共享资源的访问。其本质是一个整型变量,可通过特定的操作进行访问和修改。信号量用于解决并发程序中的同步和互斥问题。在Linux中,信号量可以用于控制进程和线程对共享内存区域的访问。

信号量具有两种操作:P操作和V操作。P操作(也称为wait操作)会使信号量的值减一,如果此时信号量的值小于0,则进程或线程将被阻塞。V操作(也称为signal操作)会使信号量的值加一,并唤醒可能因为等待信号量而被阻塞的进程或线程。

三、在Linux内核中使用信号量

Linux内核提供了一些系统调用函数来创建和操作信号量,这些函数都定义在<sys/sem.h>头文件中。

3.1 创建信号量

要创建一个信号量,可以使用semget系统调用函数,它的原型如下:

#include <sys/sem.h>

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

其中:

  • key是信号量的关键字,用于唯一标识信号量;
  • nsems是信号量的数量;
  • semflg是创建信号量时的标志,可用于指定信号量的权限和访问方式等。

下面是一个创建信号量的例子:

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

int main() {
    key_t key = ftok(".", 's'); // 生成信号量的关键字
    int semid = semget(key, 1, IPC_CREAT | 0666); // 创建一个信号量,初始值为0
    if (semid == -1) {
        perror("semget");
        exit(1);
    }
    printf("semaphore created with id = %d\n", semid);
    return 0;
}

在这个例子中,我们使用ftok函数生成信号量的关键字,然后调用semget函数创建一个信号量,初始值为0

IPC_CREAT标志表示如果信号量不存在,则创建它,0666表示指定信号量的权限为读写,即任何进程都可以访问该信号量。

3.2 初始化信号量

创建信号量后,需要通过semctl函数来初始化信号量的值和其他属性。semctl的原型如下:

#include <sys/sem.h>

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

其中,semid是信号量的IDsemnum是信号量集中的信号量编号,通常为0cmd是要执行的命令,它可以是IPC_RMID(删除信号量)、SETVAL(设置信号量的值)、GETVAL(获取信号量的值)等。第四个参数通常是一个union semun类型的共用体,用于传递具体的参数。

下面是一个初始化信号量的例子:

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

union semun {
    int val;              // SETVAL命令使用的值
    struct semid_ds *buf; // IPC_STAT和IPC_SET命令使用的缓冲区
    unsigned short *array;// GETALL和SETALL命令使用的数组
};

int main() {
    key_t key = ftok(".", 's'); // 生成信号量的关键字
    int semid = semget(key, 1, IPC_CREAT | 0666); // 创建一个信号量,初始值为0
    if (semid == -1) {
        perror("semget");
        exit(1);
    }
    printf("semaphore created with id = %d\n", semid);
    union semun arg;
    arg.val = 1; // 设置信号量的值为1
    if (semctl(semid, 0, SETVAL, arg) == -1) {
        perror("semctl");
        exit(1);
    }
    printf("semaphore value set to %d\n", arg.val);
    return 0;
}

在这个例子中,我们使用union semun定义了一个共用体,用于传递参数。然后,我们将信号量的值设置为1,使用semctl函数执行SETVAL命令。如果执行成功,将输出 semaphore value set to 1

3.3 使用信号量

要使用信号量来控制进程或线程的访问,通常需要在访问共享资源之前先执行P操作,访问结束后执行V操作。下面是一个使用信号量的例子:

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

int main() {
    key_t key = ftok(".", 's'); // 生成信号量的关键字
    int semid = semget(key, 1, IPC_CREAT | 0666); // 创建一个信号量,初始值为1
    if (semid == -1) {
        perror("semget");
        exit(1);
    }
    printf("semaphore created with id = %d\n", semid);
    struct sembuf buf;
    buf.sem_num = 0; // 操作信号量集中的第1个信号量
    buf.sem_op = -1; // 执行P操作
    buf.sem_flg = SEM_UNDO; // 允许系统在进程异常终止时恢复信号量的值
    if (semop(semid, &buf, 1) == -1) {
        perror("semop");
        exit(1);
    }
    printf("resource acquired\n");

四、结语

信号量是一种在多进程或多线程环境下同步和互斥的机制。通过使用信号量,可以控制进程或线程对共享资源的访问,避免竞争条件的发生,提高程序的并发性能。

在Linux内核中,信号量是通过semaphore.h头文件中的函数实现的。使用信号量需要创建信号量集,初始化信号量集中的信号量,以及执行P操作和V操作。P操作将信号量减1V操作将信号量加1。如果信号量的值小于等于0,则P操作将被阻塞,直到信号量的值变为正数。

在本文中,我们介绍了信号量的基本知识点,并通过一个简单的示例演示了如何在Linux内核中使用信号量。希望本文能够帮助初学者更好地理解信号量的概念和使用方法。

👇点击下方公众号卡片获取资料👇
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值