semctl系统调用及示例

semctl – 信号量控制

函数介绍

semctl系统调用用于控制System V信号量集,可以获取和设置信号量的各种属性和状态。它是信号量管理的重要工具,用于初始化、查询、修改和删除信号量。

semctl – 信号量控制
(https://www.calcguide.tech/2025/08/18/semctl%e7%b3%bb%e7%bb%9f%e8%b0%83%e7%94%a8%e5%8f%8a%e7%a4%ba%e4%be%8b/)

函数介绍
semctl系统调用用于控制System V信号量集,可以获取和设置信号量的各种属性和状态。它是信号量管理的重要工具,用于初始化、查询、修改和删除信号量。

函数原型

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

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

功能

对信号量集执行各种控制操作,包括设置值、获取状态、删除信号量集等。

参数

  • int semid: 信号量集的标识符
  • int semnum: 信号量在集合中的索引号(某些命令需要)
  • int cmd: 要执行的控制命令
    • SETVAL: 设置单个信号量的值
    • GETVAL: 获取单个信号量的值
    • SETALL: 设置所有信号量的值
    • GETALL: 获取所有信号量的值
    • IPC_RMID: 删除信号量集
    • IPC_STAT: 获取信号量集状态
    • IPC_SET: 设置信号量集状态
  • ...: 可选参数,根据命令不同而不同

返回值

  • 根据命令不同返回不同值:
    • SETVALSETALLIPC_RMID: 成功返回0,失败返回-1
    • GETVAL: 成功返回信号量值,失败返回-1
    • GETALL: 成功返回0,失败返回-1
    • IPC_STAT: 成功返回0,失败返回-1
  • 失败时设置errno

相似函数

  • semget(): 获取信号量集标识符
  • semop(): 操作信号量集
  • ftok(): 生成System V IPC键值

示例代码

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <errno.h>
#include <string.h>

// 信号量操作联合体
union semun {
    int val;               // 用于SETVAL
    struct semid_ds *buf;  // 用于IPC_STAT和IPC_SET
    unsigned short *array; // 用于GETALL和SETALL
};

int main() {
    key_t key;
    int semid;
    union semun arg;
    struct semid_ds sem_info;
    
    printf("=== Semctl函数示例 ===\n");
    
    // 创建信号量集
    key = ftok(".", 'c');
    if (key == -1) {
        perror("ftok失败");
        exit(EXIT_FAILURE);
    }
    
    // 先尝试删除可能存在的同名信号量集
    semid = semget(key, 1, 0666);
    if (semid != -1) {
        semctl(semid, 0, IPC_RMID);
    }
    
    // 创建包含3个信号量的信号量集
    semid = semget(key, 3, 0666 | IPC_CREAT | IPC_EXCL);
    if (semid == -1) {
        perror("创建信号量集失败");
        exit(EXIT_FAILURE);
    }
    printf("成功创建信号量集,标识符: %d\n", semid);
    
    // 示例1: 设置单个信号量的值
    printf("\n示例1: 设置单个信号量的值\n");
    
    // 设置信号量0的值为5
    arg.val = 5;
    if (semctl(semid, 0, SETVAL, arg) == -1) {
        perror("设置信号量0失败");
    } else {
        printf("  成功设置信号量0的值为: %d\n", semctl(semid, 0, GETVAL));
    }
    
    // 设置信号量1的值为10
    arg.val = 10;
    if (semctl(semid, 1, SETVAL, arg) == -1) {
        perror("设置信号量1失败");
    } else {
        printf("  成功设置信号量1的值为: %d\n", semctl(semid, 1, GETVAL));
    }
    
    // 设置信号量2的值为0
    arg.val = 0;
    if (semctl(semid, 2, SETVAL, arg) == -1) {
        perror("设置信号量2失败");
    } else {
        printf("  成功设置信号量2的值为: %d\n", semctl(semid, 2, GETVAL));
    }
    
    // 示例2: 获取单个信号量的值
    printf("\n示例2: 获取单个信号量的值\n");
    int val0 = semctl(semid, 0, GETVAL);
    int val1 = semctl(semid, 1, GETVAL);
    int val2 = semctl(semid, 2, GETVAL);
    
    if (val0 != -1 && val1 != -1 && val2 != -1) {
        printf("  信号量0的值: %d\n", val0);
        printf("  信号量1的值: %d\n", val1);
        printf("  信号量2的值: %d\n", val2);
    } else {
        perror("获取信号量值失败");
    }
    
    // 示例3: 批量设置所有信号量的值
    printf("\n示例3: 批量设置所有信号量的值\n");
    unsigned short values[3] = {3, 7, 2};
    arg.array = values;
    
    if (semctl(semid, 0, SETALL, arg) == -1) {
        perror("批量设置信号量值失败");
    } else {
        printf("  成功批量设置信号量值\n");
        
        // 验证设置结果
        unsigned short get_values[3];
        arg.array = get_values;
        if (semctl(semid, 0, GETALL, arg) == -1) {
            perror("批量获取信号量值失败");
        } else {
            printf("  当前信号量值: [%d, %d, %d]\n", 
                   get_values[0], get_values[1], get_values[2]);
        }
    }
    
    // 示例4: 获取信号量集状态信息
    printf("\n示例4: 获取信号量集状态信息\n");
    arg.buf = &sem_info;
    if (semctl(semid, 0, IPC_STAT, arg) == -1) {
        perror("获取信号量集状态失败");
    } else {
        printf("  信号量集状态信息:\n");
        printf("    键值: %d\n", sem_info.sem_perm.__key);
        printf("    信号量数量: %ld\n", sem_info.sem_nsems);
        printf("    最后操作时间: %ld\n", sem_info.sem_otime);
        printf("    最后修改时间: %ld\n", sem_info.sem_ctime);
        printf("    创建者UID: %d\n", sem_info.sem_perm.cuid);
        printf("    创建者GID: %d\n", sem_info.sem_perm.cgid);
        printf("    所有者UID: %d\n", sem_info.sem_perm.uid);
        printf("    所有者GID: %d\n", sem_info.sem_perm.gid);
        printf("    权限: %o\n", sem_info.sem_perm.mode);
    }
    
    // 示例5: 修改信号量集权限
    printf("\n示例5: 修改信号量集权限\n");
    struct semid_ds new_info;
    memcpy(&new_info, &sem_info, sizeof(struct semid_ds));
    new_info.sem_perm.mode = 0644;  // 修改为读写权限
    
    arg.buf = &new_info;
    if (semctl(semid, 0, IPC_SET, arg) == -1) {
        perror("修改信号量集权限失败");
    } else {
        printf("  成功修改信号量集权限为: %o\n", new_info.sem_perm.mode);
    }
    
    // 示例6: 错误处理演示
    printf("\n示例6: 错误处理演示\n");
    
    // 尝试操作不存在的信号量集
    if (semctl(999999, 0, GETVAL) == -1) {
        printf("  操作不存在的信号量集: %s\n", strerror(errno));
    }
    
    // 尝试操作不存在的信号量索引
    if (semctl(semid, 999, GETVAL) == -1) {
        printf("  操作不存在的信号量索引: %s\n", strerror(errno));
    }
    
    // 示例7: 删除信号量集
    printf("\n示例7: 删除信号量集\n");
    if (semctl(semid, 0, IPC_RMID) == -1) {
        perror("删除信号量集失败");
    } else {
        printf("  成功删除信号量集 %d\n", semid);
        
        // 验证删除结果
        if (semctl(semid, 0, GETVAL) == -1) {
            printf("  验证: 信号量集已不存在\n");
        }
    }
    
    return 0;
}
此条目发表在未分类分类目录。将固定链接加入收藏夹。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注