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
: 设置信号量集状态
...
: 可选参数,根据命令不同而不同
返回值
- 根据命令不同返回不同值:
SETVAL
,SETALL
,IPC_RMID
: 成功返回0,失败返回-1GETVAL
: 成功返回信号量值,失败返回-1GETALL
: 成功返回0,失败返回-1IPC_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;
}