【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);
​
  • key:用于标识信号量集的键值。
  • nsems:信号量集中的信号量数量。
  • semflg:权限标志和控制标志。

2.3 示例

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

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

三、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, ...);
​
  • 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");
}
​

删除信号量集:

if (semctl(semid, 0, IPC_RMID) == -1) {
    perror("semctl");
}
​

四、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);
​
  • semid:信号量集标识符。
  • sops:指向 sembuf 结构数组的指针。
  • nsops:数组中的元素个数。

4.3 sembuf 结构

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

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");
}
​

五、完整示例

以下是一个完整的示例,演示如何创建信号量、初始化信号量值,以及在两个进程中使用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;
}
​

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;
}
​

进程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;
}
​

5.3 编译和运行

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

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

六、总结

本文详细介绍了

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

目录
相关文章
|
10月前
|
消息中间件 Linux
Linux中的System V通信标准--共享内存、消息队列以及信号量
希望本文能帮助您更好地理解和应用System V IPC机制,构建高效的Linux应用程序。
414 48
|
10月前
|
存储 Linux
linux中的目录操作函数
本文详细介绍了Linux系统编程中常用的目录操作函数,包括创建目录、删除目录、读取目录内容、遍历目录树以及获取和修改目录属性。这些函数是进行文件系统操作的基础,通过示例代码展示了其具体用法。希望本文能帮助您更好地理解和应用这些目录操作函数,提高系统编程的效率和能力。
368 26
|
11月前
|
消息中间件 Linux
Linux:进程间通信(共享内存详细讲解以及小项目使用和相关指令、消息队列、信号量)
通过上述讲解和代码示例,您可以理解和实现Linux系统中的进程间通信机制,包括共享内存、消息队列和信号量。这些机制在实际开发中非常重要,能够提高系统的并发处理能力和数据通信效率。希望本文能为您的学习和开发提供实用的指导和帮助。
833 20
|
12月前
|
Linux Android开发 开发者
linux m、mm、mmm函数和make的区别
通过理解和合理使用这些命令,可以更高效地进行项目构建和管理,特别是在复杂的 Android 开发环境中。
636 18
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
549 13
|
Linux Shell
Linux系统编程:掌握popen函数的使用
记得在使用完 `popen`打开的流后,总是使用 `pclose`来正确关闭它,并回收资源。这种做法符合良好的编程习惯,有助于保持程序的健壮性和稳定性。
734 6
|
Docker 容器
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
380 1
|
3月前
|
Linux 应用服务中间件 Shell
二、Linux文本处理与文件操作核心命令
熟悉了Linux的基本“行走”后,就该拿起真正的“工具”干活了。用grep这个“放大镜”在文件里搜索内容,用find这个“探测器”在系统中寻找文件,再用tar把东西打包带走。最关键的是要学会使用管道符|,它像一条流水线,能把这些命令串联起来,让简单工具组合出强大的功能,比如 ps -ef | grep 'nginx' 就能快速找出nginx进程。
475 2
二、Linux文本处理与文件操作核心命令
|
3月前
|
Linux
linux命令—stat
`stat` 是 Linux 系统中用于查看文件或文件系统详细状态信息的命令。相比 `ls -l`,它提供更全面的信息,包括文件大小、权限、所有者、时间戳(最后访问、修改、状态变更时间)、inode 号、设备信息等。其常用选项包括 `-f` 查看文件系统状态、`-t` 以简洁格式输出、`-L` 跟踪符号链接,以及 `-c` 或 `--format` 自定义输出格式。通过这些选项,用户可以灵活获取所需信息,适用于系统调试、权限检查、磁盘管理等场景。
327 137
|
3月前
|
安全 Ubuntu Unix
一、初识 Linux 与基本命令
玩转Linux命令行,就像探索一座新城市。首先要熟悉它的“地图”,也就是/根目录下/etc(放配置)、/home(住家)这些核心区域。然后掌握几个“生存口令”:用ls看周围,cd去别处,mkdir建新房,cp/mv搬东西,再用cat或tail看文件内容。最后,别忘了随时按Tab键,它能帮你自动补全命令和路径,是提高效率的第一神器。
761 57