【Linux】System V信号量详解以及semget()、semctl()和semop()函数讲解

简介: System V信号量的概念及其在Linux中的使用,包括 `semget()`、`semctl()`和 `semop()`函数的具体使用方法。通过实际代码示例,演示了如何创建、初始化和使用信号量进行进程间同步。掌握这些知识,可以有效解决多进程编程中的同步问题,提高程序的可靠性和稳定性。

Linux System V 信号量详解

System V 信号量(semaphore)是用于进程间同步的机制,在多进程编程中用于控制对共享资源的访问。System V 信号量允许多个进程通过信号量集进行同步操作。本文将详细介绍 System V 信号量的概念及其相关函数 semget()semctl()semop() 的使用。

一、System V 信号量概述

1.1 信号量的基本概念

信号量是一个计数器,用于控制多个进程对共享资源的访问。信号量可以用来解决以下问题:

  • 互斥:确保一次只有一个进程访问共享资源。
  • 同步:协调进程之间的执行顺序。

1.2 信号量的类型

  • 二元信号量:取值只能是0和1,用于互斥锁(mutex)。
  • 计数信号量:可以取0和正整数,用于控制多个进程的访问。

1.3 System V 信号量特点

  • 信号量集:一个信号量集包含一个或多个信号量。
  • 标识符:信号量集通过一个标识符进行标识。
  • 内核持久性:信号量集在内核中持久存在,直到显式删除或系统重启。

二、semget() 函数

2.1 功能

semget() 用于创建一个新的信号量集或获取一个已有的信号量集。

2.2 语法

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

int semget(key_t key, int nsems, int semflg);
​
AI 代码解读
  • key:用于标识信号量集的键值。
  • nsems:信号量集中的信号量数量。
  • semflg:权限标志和控制标志。

2.3 示例

创建一个包含一个信号量的信号量集:

key_t key = ftok("semfile", 65);
int semid = semget(key, 1, 0666 | IPC_CREAT);
if (semid == -1) {
    perror("semget");
}
​
AI 代码解读

三、semctl() 函数

3.1 功能

semctl() 用于控制信号量集,支持初始化、获取和设置信号量的值,以及删除信号量集。

3.2 语法

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

int semctl(int semid, int semnum, int cmd, ...);
​
AI 代码解读
  • semid:信号量集标识符。
  • semnum:信号量编号。
  • cmd:控制命令,如 IPC_RMIDSETVALGETVAL 等。
  • 可变参数:根据 cmd 的不同,可以是 intunion semun

3.3 示例

初始化信号量的值:

union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
} arg;

arg.val = 1;  // 初始化为 1
if (semctl(semid, 0, SETVAL, arg) == -1) {
    perror("semctl");
}
​
AI 代码解读

删除信号量集:

if (semctl(semid, 0, IPC_RMID) == -1) {
    perror("semctl");
}
​
AI 代码解读

四、semop() 函数

4.1 功能

semop() 用于对信号量进行P(等待)和V(释放)操作,以实现对资源的控制和同步。

4.2 语法

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

int semop(int semid, struct sembuf *sops, size_t nsops);
​
AI 代码解读
  • semid:信号量集标识符。
  • sops:指向 sembuf 结构数组的指针。
  • nsops:数组中的元素个数。

4.3 sembuf 结构

struct sembuf {
    unsigned short sem_num;  // 信号量编号
    short sem_op;            // 操作:P(-1) 或 V(+1)
    short sem_flg;           // 操作标志
};
​
AI 代码解读

4.4 示例

执行P操作(等待)和V操作(释放):

struct sembuf sb = {0, -1, 0};  // P操作
if (semop(semid, &sb, 1) == -1) {
    perror("semop");
}

// 临界区代码

sb.sem_op = 1;  // V操作
if (semop(semid, &sb, 1) == -1) {
    perror("semop");
}
​
AI 代码解读

五、完整示例

以下是一个完整的示例,演示如何创建信号量、初始化信号量值,以及在两个进程中使用P和V操作进行同步。

5.1 创建和初始化信号量

创建一个信号量,并初始化其值为1:

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

union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
};

int main() {
    key_t key = ftok("semfile", 65);
    int semid = semget(key, 1, 0666 | IPC_CREAT);
    if (semid == -1) {
        perror("semget");
        exit(EXIT_FAILURE);
    }

    union semun arg;
    arg.val = 1;
    if (semctl(semid, 0, SETVAL, arg) == -1) {
        perror("semctl");
        exit(EXIT_FAILURE);
    }

    return 0;
}
​
AI 代码解读

5.2 使用信号量进行同步

在两个进程中使用信号量进行同步,确保两个进程不会同时进入临界区。

进程1:

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

int main() {
    key_t key = ftok("semfile", 65);
    int semid = semget(key, 1, 0666);
    if (semid == -1) {
        perror("semget");
        exit(EXIT_FAILURE);
    }

    struct sembuf sb = {0, -1, 0};  // P操作
    if (semop(semid, &sb, 1) == -1) {
        perror("semop P");
        exit(EXIT_FAILURE);
    }

    printf("Process 1 in critical section\n");
    sleep(2);  // 模拟临界区操作
    printf("Process 1 leaving critical section\n");

    sb.sem_op = 1;  // V操作
    if (semop(semid, &sb, 1) == -1) {
        perror("semop V");
        exit(EXIT_FAILURE);
    }

    return 0;
}
​
AI 代码解读

进程2:

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

int main() {
    key_t key = ftok("semfile", 65);
    int semid = semget(key, 1, 0666);
    if (semid == -1) {
        perror("semget");
        exit(EXIT_FAILURE);
    }

    struct sembuf sb = {0, -1, 0};  // P操作
    if (semop(semid, &sb, 1) == -1) {
        perror("semop P");
        exit(EXIT_FAILURE);
    }

    printf("Process 2 in critical section\n");
    sleep(2);  // 模拟临界区操作
    printf("Process 2 leaving critical section\n");

    sb.sem_op = 1;  // V操作
    if (semop(semid, &sb, 1) == -1) {
        perror("semop V");
        exit(EXIT_FAILURE);
    }

    return 0;
}
​
AI 代码解读

5.3 编译和运行

  1. 编译程序:
gcc -o sem_create sem_create.c
gcc -o process1 process1.c
gcc -o process2 process2.c
AI 代码解读
  1. 运行创建和初始化信号量的程序:
./sem_create
​
AI 代码解读
  1. 分别运行进程1和进程2:
./process1
./process2
​
AI 代码解读

可以看到,两个进程会依次进入和离开临界区,而不会同时进入。

六、总结

本文详细介绍了

System V信号量的概念及其在Linux中的使用,包括 semget()semctl()semop()函数的具体使用方法。通过实际代码示例,演示了如何创建、初始化和使用信号量进行进程间同步。掌握这些知识,可以有效解决多进程编程中的同步问题,提高程序的可靠性和稳定性。

目录
打赏
0
19
20
2
443
分享
相关文章
Linux中的System V通信标准--共享内存、消息队列以及信号量
希望本文能帮助您更好地理解和应用System V IPC机制,构建高效的Linux应用程序。
106 48
|
9天前
|
linux中的目录操作函数
本文详细介绍了Linux系统编程中常用的目录操作函数,包括创建目录、删除目录、读取目录内容、遍历目录树以及获取和修改目录属性。这些函数是进行文件系统操作的基础,通过示例代码展示了其具体用法。希望本文能帮助您更好地理解和应用这些目录操作函数,提高系统编程的效率和能力。
55 26
Linux:进程间通信(共享内存详细讲解以及小项目使用和相关指令、消息队列、信号量)
通过上述讲解和代码示例,您可以理解和实现Linux系统中的进程间通信机制,包括共享内存、消息队列和信号量。这些机制在实际开发中非常重要,能够提高系统的并发处理能力和数据通信效率。希望本文能为您的学习和开发提供实用的指导和帮助。
138 20
linux m、mm、mmm函数和make的区别
通过理解和合理使用这些命令,可以更高效地进行项目构建和管理,特别是在复杂的 Android 开发环境中。
86 18
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
123 13
14 response from daemon: open \\.\pipe\docker_engine_linux: The system cannot find the file speci
14 response from daemon: open \\.\pipe\docker_engine_linux: The system cannot find the file speci
96 1
|
5月前
|
Linux系统编程:掌握popen函数的使用
记得在使用完 `popen`打开的流后,总是使用 `pclose`来正确关闭它,并回收资源。这种做法符合良好的编程习惯,有助于保持程序的健壮性和稳定性。
241 6
|
12天前
|
Linux系统之whereis命令的基本使用
Linux系统之whereis命令的基本使用
52 23
Linux系统之whereis命令的基本使用
Linux 超级强大的十六进制 dump 工具:XXD 命令,我教你应该如何使用!
在 Linux 系统中,xxd 命令是一个强大的十六进制 dump 工具,可以将文件或数据以十六进制和 ASCII 字符形式显示,帮助用户深入了解和分析数据。本文详细介绍了 xxd 命令的基本用法、高级功能及实际应用案例,包括查看文件内容、指定输出格式、写入文件、数据比较、数据提取、数据转换和数据加密解密等。通过掌握这些技巧,用户可以更高效地处理各种数据问题。
365 8
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等