semctl系统调用及示例

semctl - 信号量控制

函数介绍

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

data-ad-format="fluid" data-ad-layout-key="-7k+ex-4a-9w+4a">

semctl - 信号量控制(https://www.calcguide.tech/2025/08/18/semctl系统调用及示例/)

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

函数原型

1
2
3
4
5
6
#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,失败返回-1

  • GETVAL: 成功返回信号量值,失败返回-1

  • GETALL: 成功返回0,失败返回-1

  • IPC_STAT: 成功返回0,失败返回-1

失败时设置errno

相似函数

  • semget(): 获取信号量集标识符

  • semop(): 操作信号量集

  • ftok(): 生成System V IPC键值

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#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&#91;3] = {3, 7, 2};
arg.array = values;

if (semctl(semid, 0, SETALL, arg) == -1) {
perror("批量设置信号量值失败");
} else {
printf(" 成功批量设置信号量值\n");

// 验证设置结果
unsigned short get_values&#91;3];
arg.array = get_values;
if (semctl(semid, 0, GETALL, arg) == -1) {
perror("批量获取信号量值失败");
} else {
printf(" 当前信号量值: &#91;%d, %d, %d]\n",
get_values&#91;0], get_values&#91;1], get_values&#91;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;
}
data-ad-format="auto" data-full-width-responsive="true">