semtimedop系统调用及示例

semtimedop 函数详解

  1. 函数介绍

semtimedop 是Linux系统调用,用于对System V信号量集执行原子操作,并支持超时控制。它是 semop 函数的增强版本,允许指定操作的超时时间,避免无限期阻塞。信号量是进程间同步的重要机制,常用于控制对共享资源的访问。

  1. 函数原型
1
2
3
4
5
6
#include <sys/sem.h>
#include <sys/types.h>

int semtimedop(int semid, struct sembuf *sops, size_t nsops,
const struct timespec *timeout);

  1. 功能

semtimedop 对指定的信号量集执行一系列原子操作,如果操作不能立即完成且设置了阻塞标志,则在指定的超时时间内等待。该函数提供了精确的时间控制,使得程序可以在等待信号量时避免永久阻塞。

  1. 参数
  • int semid: 信号量集标识符(由semget返回)

  • *struct sembuf sops: 信号量操作数组

  • size_t nsops: 操作数组中元素的个数

  • *const struct timespec timeout: 超时时间(NULL表示无限等待)

  1. 返回值
  • 成功: 返回0

  • 超时: 返回-1,并设置errno为EAGAIN

  • 失败: 返回-1,并设置相应的errno

  1. 相似函数,或关联函数
  • semop: 不带超时的信号量操作函数

  • semget: 获取信号量集

  • semctl: 信号量控制操作

  • semctl: 信号量控制函数

  1. 示例代码

示例1:基础semtimedop使用

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
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>

/**
* 信号量操作结构
*/
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};

/**
* 初始化信号量
*/
int init_semaphore(key_t key, int nsems, int init_val) {
int semid;
union semun arg;

// 创建或获取信号量集
semid = semget(key, nsems, IPC_CREAT | 0666);
if (semid == -1) {
perror("semget 失败");
return -1;
}

// 初始化信号量值
arg.val = init_val;
if (semctl(semid, 0, SETVAL, arg) == -1) {
perror("semctl 初始化失败");
return -1;
}

return semid;
}

/**
* 演示基础semtimedop使用方法
*/
int demo_semtimedop_basic() {
int semid;
struct sembuf sop;
struct timespec timeout;
key_t key = ftok(".", 's');

printf("=== 基础semtimedop使用示例 ===\n");

// 初始化信号量(初始值为1,表示资源可用)
semid = init_semaphore(key, 1, 1);
if (semid == -1) {
return -1;
}

printf("创建信号量集,ID: %d\n", semid);

// 设置超时时间(5秒)
timeout.tv_sec = 5;
timeout.tv_nsec = 0;

// 定义P操作(获取信号量)
sop.sem_num = 0; // 操作第0个信号量
sop.sem_op = -1; // P操作:减1
sop.sem_flg = 0; // 阻塞操作

printf("执行P操作(获取信号量)...\n");
if (semtimedop(semid, &sop, 1, &timeout) == -1) {
if (errno == EAGAIN) {
printf("操作超时\n");
} else {
perror("semtimedop P操作失败");
}
goto cleanup;
}

printf("成功获取信号量\n");

// 模拟使用资源
printf("使用资源中...(3秒)\n");
sleep(3);

// 定义V操作(释放信号量)
sop.sem_op = 1; // V操作:加1

printf("执行V操作(释放信号量)...\n");
if (semtimedop(semid, &sop, 1, &timeout) == -1) {
perror("semtimedop V操作失败");
goto cleanup;
}

printf("成功释放信号量\n");

cleanup:
// 清理信号量集
if (semctl(semid, 0, IPC_RMID) == -1) {
perror("清理信号量集失败");
}

return 0;
}

int main() {
return demo_semtimedop_basic();
}

示例2:多进程同步演示

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
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>

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

/**
* 创建二进制信号量(互斥锁)
*/
int create_binary_semaphore(key_t key) {
int semid;
union semun arg;

semid = semget(key, 1, IPC_CREAT | 0666);
if (semid == -1) {
perror("创建信号量失败");
return -1;
}

// 初始化为1(可用状态)
arg.val = 1;
if (semctl(semid, 0, SETVAL, arg) == -1) {
perror("初始化信号量失败");
semctl(semid, 0, IPC_RMID);
return -1;
}

return semid;
}

/**
* 获取互斥锁
*/
int acquire_mutex(int semid, int timeout_sec) {
struct sembuf sop;
struct timespec timeout;

sop.sem_num = 0;
sop.sem_op = -1;
sop.sem_flg = 0;

timeout.tv_sec = timeout_sec;
timeout.tv_nsec = 0;

return semtimedop(semid, &sop, 1, &timeout);
}

/**
* 释放互斥锁
*/
int release_mutex(int semid) {
struct sembuf sop;

sop.sem_num = 0;
sop.sem_op = 1;
sop.sem_flg = 0;

return semop(semid, &sop, 1);
}

/**
* 演示多进程同步
*/
int demo_multiprocess_synchronization() {
int semid;
key_t key = ftok(".", 'm');
int shared_resource = 0;

printf("=== 多进程同步演示 ===\n");

// 创建互斥信号量
semid = create_binary_semaphore(key);
if (semid == -1) {
return -1;
}

printf("创建互斥信号量,ID: %d\n", semid);

// 创建多个子进程
for (int i = 0; i < 3; i++) {
if (fork() == 0) {
// 子进程代码
pid_t pid = getpid();
printf("子进程 %d 启动\n", pid);

// 尝试获取互斥锁(超时3秒)
if (acquire_mutex(semid, 3) == -1) {
if (errno == EAGAIN) {
printf("子进程 %d: 获取锁超时\n", pid);
} else {
printf("子进程 %d: 获取锁失败: %s\n", pid, strerror(errno));
}
exit(1);
}

printf("子进程 %d: 成功获取锁\n", pid);

// 模拟临界区操作
printf("子进程 %d: 访问共享资源\n", pid);
sleep(2); // 模拟处理时间

// 释放互斥锁
if (release_mutex(semid) == -1) {
printf("子进程 %d: 释放锁失败: %s\n", pid, strerror(errno));
} else {
printf("子进程 %d: 成功释放锁\n", pid);
}

exit(0);
}
}

// 父进程等待所有子进程完成
for (int i = 0; i < 3; i++) {
int status;
wait(&status);
if (WIFEXITED(status)) {
printf("子进程 %d 退出,状态码: %d\n",
i + 1, WEXITSTATUS(status));
}
}

// 清理信号量
semctl(semid, 0, IPC_RMID);

return 0;
}

int main() {
return demo_multiprocess_synchronization();
}

示例3:生产者-消费者模型

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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>

#define BUFFER_SIZE 5
#define MAX_ITEMS 10

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

// 信号量索引定义
#define EMPTY_SEM 0 // 空槽位计数
#define FULL_SEM 1 // 满槽位计数
#define MUTEX_SEM 2 // 缓冲区互斥锁

/**
* 初始化生产者-消费者信号量
*/
int init_pc_semaphores(key_t key) {
int semid;
union semun arg;
unsigned short values&#91;3] = {BUFFER_SIZE, 0, 1}; // empty, full, mutex

// 创建包含3个信号量的信号量集
semid = semget(key, 3, IPC_CREAT | 0666);
if (semid == -1) {
perror("创建信号量集失败");
return -1;
}

// 初始化信号量值
arg.array = values;
if (semctl(semid, 0, SETALL, arg) == -1) {
perror("初始化信号量失败");
semctl(semid, 0, IPC_RMID);
return -1;
}

return semid;
}

/**
* 生产者操作
*/
int producer_operation(int semid, int item, int timeout_sec) {
struct sembuf sops&#91;2];
struct timespec timeout;

// 设置超时时间
timeout.tv_sec = timeout_sec;
timeout.tv_nsec = 0;

// 1. 等待空槽位 (P(empty))
sops&#91;0].sem_num = EMPTY_SEM;
sops&#91;0].sem_op = -1;
sops&#91;0].sem_flg = 0;

// 2. 获取缓冲区互斥锁 (P(mutex))
sops&#91;1].sem_num = MUTEX_SEM;
sops&#91;1].sem_op = -1;
sops&#91;1].sem_flg = 0;

printf("生产者: 尝试生产项目 %d\n", item);

// 执行P操作
if (semtimedop(semid, sops, 2, &timeout) == -1) {
if (errno == EAGAIN) {
printf("生产者: 等待超时,无法生产项目 %d\n", item);
} else {
printf("生产者: 操作失败: %s\n", strerror(errno));
}
return -1;
}

// 模拟生产过程
printf("生产者: 正在生产项目 %d\n", item);
sleep(1);
printf("生产者: 生产完成项目 %d\n", item);

// 释放互斥锁和增加满槽位计数
sops&#91;0].sem_num = MUTEX_SEM;
sops&#91;0].sem_op = 1; // V(mutex)
sops&#91;1].sem_num = FULL_SEM;
sops&#91;1].sem_op = 1; // V(full)

if (semop(semid, sops, 2) == -1) {
perror("生产者: 释放信号量失败");
return -1;
}

return 0;
}

/**
* 消费者操作
*/
int consumer_operation(int semid, int *item, int timeout_sec) {
struct sembuf sops&#91;2];
struct timespec timeout;

// 设置超时时间
timeout.tv_sec = timeout_sec;
timeout.tv_nsec = 0;

// 1. 等待满槽位 (P(full))
sops&#91;0].sem_num = FULL_SEM;
sops&#91;0].sem_op = -1;
sops&#91;0].sem_flg = 0;

// 2. 获取缓冲区互斥锁 (P(mutex))
sops&#91;1].sem_num = MUTEX_SEM;
sops&#91;1].sem_op = -1;
sops&#91;1].sem_flg = 0;

printf("消费者: 尝试消费项目\n");

// 执行P操作
if (semtimedop(semid, sops, 2, &timeout) == -1) {
if (errno == EAGAIN) {
printf("消费者: 等待超时,无项目可消费\n");
} else {
printf("消费者: 操作失败: %s\n", strerror(errno));
}
return -1;
}

// 模拟消费过程
*item = rand() % 100; // 模拟消费的项目
printf("消费者: 正在消费项目 %d\n", *item);
sleep(1);
printf("消费者: 消费完成项目 %d\n", *item);

// 释放互斥锁和增加空槽位计数
sops&#91;0].sem_num = MUTEX_SEM;
sops&#91;0].sem_op = 1; // V(mutex)
sops&#91;1].sem_num = EMPTY_SEM;
sops&#91;1].sem_op = 1; // V(empty)

if (semop(semid, sops, 2) == -1) {
perror("消费者: 释放信号量失败");
return -1;
}

return 0;
}

/**
* 演示生产者-消费者模型
*/
int demo_producer_consumer() {
int semid;
key_t key = ftok(".", 'p');

printf("=== 生产者-消费者模型演示 ===\n");
printf("缓冲区大小: %d\n", BUFFER_SIZE);
printf("生产/消费项目数: %d\n", MAX_ITEMS);

// 初始化信号量
semid = init_pc_semaphores(key);
if (semid == -1) {
return -1;
}

printf("创建生产者-消费者信号量集,ID: %d\n", semid);

// 创建生产者进程
if (fork() == 0) {
// 生产者进程
srand(getpid());
printf("生产者进程启动 (PID: %d)\n", getpid());

for (int i = 1; i <= MAX_ITEMS; i++) {
if (producer_operation(semid, i, 5) == -1) {
printf("生产者: 生产项目 %d 失败\n", i);
break;
}
sleep(1); // 生产间隔
}

printf("生产者进程结束\n");
exit(0);
}

// 创建消费者进程
if (fork() == 0) {
// 消费者进程
srand(getpid() + 1000);
printf("消费者进程启动 (PID: %d)\n", getpid());

for (int i = 1; i <= MAX_ITEMS; i++) {
int item;
if (consumer_operation(semid, &item, 5) == -1) {
printf("消费者: 消费项目失败\n");
break;
}
sleep(2); // 消费间隔
}

printf("消费者进程结束\n");
exit(0);
}

// 父进程等待子进程完成
int status;
wait(&status);
wait(&status);

// 显示最终信号量状态
union semun arg;
unsigned short values&#91;3];
arg.array = values;

if (semctl(semid, 0, GETALL, arg) != -1) {
printf("\n最终信号量状态:\n");
printf(" 空槽位: %d\n", values&#91;EMPTY_SEM]);
printf(" 满槽位: %d\n", values&#91;FULL_SEM]);
printf(" 互斥锁: %d\n", values&#91;MUTEX_SEM]);
}

// 清理信号量集
semctl(semid, 0, IPC_RMID);

return 0;
}

int main() {
return demo_producer_consumer();
}

示例4:超时控制演示

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
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>

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

/**
* 创建计数信号量
*/
int create_counting_semaphore(key_t key, int initial_value) {
int semid;
union semun arg;

semid = semget(key, 1, IPC_CREAT | 0666);
if (semid == -1) {
perror("创建计数信号量失败");
return -1;
}

arg.val = initial_value;
if (semctl(semid, 0, SETVAL, arg) == -1) {
perror("初始化计数信号量失败");
semctl(semid, 0, IPC_RMID);
return -1;
}

return semid;
}

/**
* 演示不同超时设置
*/
int demo_timeout_settings() {
int semid;
struct sembuf sop;
struct timespec timeout;
key_t key = ftok(".", 't');

printf("=== 超时控制演示 ===\n");

// 创建计数信号量,初始值为0(无资源可用)
semid = create_counting_semaphore(key, 0);
if (semid == -1) {
return -1;
}

printf("创建计数信号量(初始值: 0),ID: %d\n", semid);

// 定义P操作
sop.sem_num = 0;
sop.sem_op = -1; // 请求资源
sop.sem_flg = 0; // 阻塞操作

printf("\n1. 测试短超时(1秒):\n");
timeout.tv_sec = 1;
timeout.tv_nsec = 0;

clock_t start = clock();
if (semtimedop(semid, &sop, 1, &timeout) == -1) {
if (errno == EAGAIN) {
clock_t end = clock();
double elapsed = ((double)(end - start)) / CLOCKS_PER_SEC;
printf(" 操作超时,等待时间: %.2f 秒\n", elapsed);
} else {
printf(" 操作失败: %s\n", strerror(errno));
}
}

printf("\n2. 测试中等超时(3秒):\n");
timeout.tv_sec = 3;
timeout.tv_nsec = 0;

start = clock();
if (semtimedop(semid, &sop, 1, &timeout) == -1) {
if (errno == EAGAIN) {
clock_t end = clock();
double elapsed = ((double)(end - start)) / CLOCKS_PER_SEC;
printf(" 操作超时,等待时间: %.2f 秒\n", elapsed);
} else {
printf(" 操作失败: %s\n", strerror(errno));
}
}

printf("\n3. 测试精确超时(1.5秒):\n");
timeout.tv_sec = 1;
timeout.tv_nsec = 500000000; // 500毫秒

start = clock();
if (semtimedop(semid, &sop, 1, &timeout) == -1) {
if (errno == EAGAIN) {
clock_t end = clock();
double elapsed = ((double)(end - start)) / CLOCKS_PER_SEC;
printf(" 操作超时,等待时间: %.2f 秒\n", elapsed);
} else {
printf(" 操作失败: %s\n", strerror(errno));
}
}

printf("\n4. 测试零超时(立即返回):\n");
timeout.tv_sec = 0;
timeout.tv_nsec = 0;

if (semtimedop(semid, &sop, 1, &timeout) == -1) {
if (errno == EAGAIN) {
printf(" 立即返回,操作无法完成\n");
} else {
printf(" 操作失败: %s\n", strerror(errno));
}
}

printf("\n5. 测试NULL超时(无限等待):\n");
printf(" 注意:这会导致无限期等待,需要外部干预\n");
printf(" 演示跳过此测试以避免阻塞\n");

// 启动一个进程来释放信号量
if (fork() == 0) {
sleep(2); // 等待主进程开始等待

// 释放信号量
sop.sem_op = 1; // V操作
if (semop(semid, &sop, 1) == -1) {
perror("释放信号量失败");
} else {
printf(" 后台进程:信号量已释放\n");
}
exit(0);
}

// 等待一小段时间后测试
sleep(1);
timeout.tv_sec = 5;
timeout.tv_nsec = 0;

printf(" 等待信号量释放(5秒超时)...\n");
if (semtimedop(semid, &sop, 1, &timeout) == 0) {
printf(" 成功获取信号量\n");
} else {
printf(" 获取信号量失败: %s\n", strerror(errno));
}

// 等待后台进程完成
int status;
wait(&status);

// 清理
semctl(semid, 0, IPC_RMID);

return 0;
}

int main() {
return demo_timeout_settings();
}

示例5:复杂信号量操作

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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>

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

/**
* 创建多个信号量的信号量集
*/
int create_multi_semaphore(key_t key, int nsems) {
int semid;

semid = semget(key, nsems, IPC_CREAT | 0666);
if (semid == -1) {
perror("创建多信号量集失败");
return -1;
}

// 初始化所有信号量为1
unsigned short *values = malloc(nsems * sizeof(unsigned short));
if (!values) {
perror("分配内存失败");
semctl(semid, 0, IPC_RMID);
return -1;
}

for (int i = 0; i < nsems; i++) {
values&#91;i] = 1;
}

union semun arg;
arg.array = values;
if (semctl(semid, 0, SETALL, arg) == -1) {
perror("初始化多信号量失败");
free(values);
semctl(semid, 0, IPC_RMID);
return -1;
}

free(values);
return semid;
}

/**
* 演示复杂的多信号量操作
*/
int demo_complex_operations() {
int semid;
struct sembuf sops&#91;5];
struct timespec timeout;
key_t key = ftok(".", 'c');
int num_semaphores = 5;

printf("=== 复杂信号量操作演示 ===\n");
printf("创建包含 %d 个信号量的信号量集\n", num_semaphores);

// 创建信号量集
semid = create_multi_semaphore(key, num_semaphores);
if (semid == -1) {
return -1;
}

printf("信号量集创建成功,ID: %d\n", semid);

// 设置超时时间
timeout.tv_sec = 10;
timeout.tv_nsec = 0;

// 演示原子性操作:同时操作多个信号量
printf("\n1. 原子性多信号量操作:\n");
printf(" 同时获取信号量 0, 1, 2\n");

// 同时获取3个信号量(P操作)
for (int i = 0; i < 3; i++) {
sops&#91;i].sem_num = i;
sops&#91;i].sem_op = -1;
sops&#91;i].sem_flg = 0;
}

if (semtimedop(semid, sops, 3, &timeout) == 0) {
printf(" 成功原子性获取3个信号量\n");

// 模拟使用资源
printf(" 使用获取的资源...\n");
sleep(2);

// 同时释放3个信号量(V操作)
for (int i = 0; i < 3; i++) {
sops&#91;i].sem_op = 1;
}

if (semop(semid, sops, 3) == 0) {
printf(" 成功原子性释放3个信号量\n");
} else {
perror(" 释放信号量失败");
}
} else {
printf(" 获取信号量失败: %s\n", strerror(errno));
}

// 演示混合操作:获取和释放不同信号量
printf("\n2. 混合信号量操作:\n");
printf(" 获取信号量0,释放信号量1\n");

sops&#91;0].sem_num = 0;
sops&#91;0].sem_op = -1; // 获取
sops&#91;0].sem_flg = 0;

sops&#91;1].sem_num = 1;
sops&#91;1].sem_op = 1; // 释放
sops&#91;1].sem_flg = 0;

if (semtimedop(semid, sops, 2, &timeout) == 0) {
printf(" 混合操作成功\n");
} else {
printf(" 混合操作失败: %s\n", strerror(errno));
}

// 演示条件操作:使用SEM_UNDO标志
printf("\n3. 带自动回滚的操作:\n");
printf(" 使用SEM_UNDO标志,进程退出时自动释放\n");

sops&#91;0].sem_num = 2;
sops&#91;0].sem_op = -1;
sops&#91;0].sem_flg = SEM_UNDO; // 自动回滚

if (semtimedop(semid, sops, 1, &timeout) == 0) {
printf(" 获取信号量2(带自动回滚)\n");
printf(" 程序退出时会自动释放该信号量\n");

// 模拟程序继续运行
printf(" 程序继续运行中...\n");
sleep(1);

// 不显式释放,依赖SEM_UNDO
} else {
printf(" 获取信号量失败: %s\n", strerror(errno));
}

// 演示NOWAIT操作
printf("\n4. 非阻塞操作:\n");
printf(" 使用IPC_NOWAIT标志\n");

sops&#91;0].sem_num = 3;
sops&#91;0].sem_op = -1;
sops&#91;0].sem_flg = IPC_NOWAIT; // 非阻塞

if (semtimedop(semid, sops, 1, NULL) == 0) {
printf(" 非阻塞获取信号量3成功\n");
// 立即释放
sops&#91;0].sem_op = 1;
sops&#91;0].sem_flg = 0;
semop(semid, sops, 1);
} else {
if (errno == EAGAIN) {
printf(" 信号量3不可用,立即返回\n");
} else {
printf(" 操作失败: %s\n", strerror(errno));
}
}

// 显示当前信号量状态
printf("\n5. 当前信号量状态:\n");
unsigned short *values = malloc(num_semaphores * sizeof(unsigned short));
if (values) {
union semun arg;
arg.array = values;

if (semctl(semid, 0, GETALL, arg) != -1) {
for (int i = 0; i < num_semaphores; i++) {
printf(" 信号量 %d: %d\n", i, values&#91;i]);
}
}
free(values);
}

// 清理
semctl(semid, 0, IPC_RMID);

return 0;
}

int main() {
return demo_complex_operations();
}

semtimedop 使用注意事项

系统要求:

内核版本: 需要Linux 2.6.12或更高版本

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

架构支持: 支持所有主流架构

编译环境: 需要正确的头文件包含

参数验证:

信号量ID有效性: 确保semid有效且未被删除

操作数组合法性: 确保sops指针有效且nsops合理

超时参数: timeout可以为NULL(无限等待)或有效时间结构

错误处理:

EAGAIN: 操作超时

EINTR: 被信号中断

EINVAL: 参数无效

EACCES: 权限不足

EIDRM: 信号量集已被删除

ENOMEM: 内存不足

性能考虑:

  1. 超时设置: 合理设置超时时间避免过度等待2. 原子性: 多信号量操作保证原子性3. 资源清理: 及时清理不再使用的信号量

最佳实践:

  1. 超时控制: 始终考虑设置合理的超时时间2. 错误恢复: 妥善处理超时和错误情况3. 资源管理: 及时清理信号量资源4. 并发安全: 确保多进程/线程环境下的正确性

信号量操作结构详解

struct sembuf 结构:

1
2
3
4
5
6
struct sembuf {
unsigned short sem_num; // 信号量编号
short sem_op; // 操作类型
short sem_flg; // 操作标志
};

操作类型 (sem_op):

  • 正值: V操作(释放资源),将值加到信号量上

  • 负值: P操作(获取资源),从信号量中减去该值

  • 零值: 等待信号量变为0

操作标志 (sem_flg):

  • SEM_UNDO: 操作完成后自动撤销(进程退出时)

  • IPC_NOWAIT: 非阻塞操作,如果不能立即完成则返回错误

  • 0: 默认阻塞操作

常见使用场景

1. 互斥锁:

1
2
3
4
5
6
7
8
// 获取锁
sop.sem_op = -1;
semtimedop(semid, &sop, 1, &timeout);

// 释放锁
sop.sem_op = 1;
semop(semid, &sop, 1);

2. 资源计数:

1
2
3
4
5
6
7
8
// 请求资源
sop.sem_op = -1;
semtimedop(semid, &sop, 1, &timeout);

// 释放资源
sop.sem_op = 1;
semop(semid, &sop, 1);

3. 条件同步:

1
2
3
4
// 等待条件满足(信号量为0)
sop.sem_op = 0;
semtimedop(semid, &sop, 1, &timeout);

总结

semtimedop 是一个功能强大的信号量操作函数,提供了:

  1. 超时控制: 避免无限期等待,提高程序健壮性2. 原子操作: 保证多信号量操作的原子性3. 灵活配置: 支持多种操作标志和模式4. 广泛应用: 适用于各种进程间同步场景

通过合理使用 semtimedop,可以构建更加可靠和高效的并发程序,特别是在需要精确时间控制的场景中表现出色。在实际应用中,需要注意超时设置、错误处理和资源管理等关键问题。

data-ad-format="auto" data-full-width-responsive="true">