semop系统调用及示例

semop - 信号量操作

函数介绍

semop系统调用用于对System V信号量集执行操作。它是信号量机制的核心操作函数,通过原子性地执行一系列信号量操作来实现进程同步。](https://www.calcguide.tech/2025/08/18/semop系统调用及示例/)

函数原型

1
2
3
4
5
6
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semop(int semid, struct sembuf *sops, unsigned nsops);

功能

对信号量集执行原子操作,用于进程间同步。

参数

  • int semid: 信号量集的标识符(由semget返回)

struct sembuf *sops: 指向sembuf结构体数组的指针struct sembuf { unsigned short sem_num; // 信号量在集合中的索引 short sem_op; // 操作类型 short sem_flg; // 操作标志 };

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

  • sem_op < 0: 请求资源(P操作),从信号量值中减去sem_op的绝对值

  • sem_op = 0: 等待信号量值变为0

unsigned nsops: sops数组中操作的数量

返回值

  • 成功时返回0

失败时返回-1,并设置errno:

  • EAGAIN: 操作会阻塞但设置了IPC_NOWAIT

  • EIDRM: 信号量集已被删除

  • EINTR: 系统调用被信号中断

  • EINVAL: 参数无效

  • EFBIG: sem_op绝对值过大

相似函数

  • semtimedop(): 带超时的信号量操作

  • semget(): 获取信号量集

  • semctl(): 控制信号量集

示例代码

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
#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>
#include <sys/wait.h>

// 信号量操作联合体
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};

// P操作(等待/减1)
int P(int semid, int sem_num) {
struct sembuf sb;
sb.sem_num = sem_num;
sb.sem_op = -1; // P操作
sb.sem_flg = 0;
return semop(semid, &sb, 1);
}

// V操作(发送/加1)
int V(int semid, int sem_num) {
struct sembuf sb;
sb.sem_num = sem_num;
sb.sem_op = 1; // V操作
sb.sem_flg = 0;
return semop(semid, &sb, 1);
}

// 等待信号量为0
int wait_zero(int semid, int sem_num) {
struct sembuf sb;
sb.sem_num = sem_num;
sb.sem_op = 0;
sb.sem_flg = 0;
return semop(semid, &sb, 1);
}

int main() {
key_t key;
int semid;
union semun arg;

printf("=== Semop函数示例 ===\n");

// 创建信号量集
key = ftok(".", 'b');
if (key == -1) {
perror("ftok失败");
exit(EXIT_FAILURE);
}

semid = semget(key, 2, 0666 | IPC_CREAT | IPC_EXCL);
if (semid == -1) {
if (errno == EEXIST) {
// 如果已存在,先删除再创建
semid = semget(key, 2, 0666);
semctl(semid, 0, IPC_RMID);
semid = semget(key, 2, 0666 | IPC_CREAT | IPC_EXCL);
} else {
perror("semget失败");
exit(EXIT_FAILURE);
}
}

if (semid == -1) {
perror("创建信号量集失败");
exit(EXIT_FAILURE);
}
printf("成功创建信号量集,标识符: %d\n", semid);

// 初始化信号量
// 信号量0:用于互斥,初始值为1(二进制信号量)
// 信号量1:用于资源计数,初始值为3(表示有3个资源)
unsigned short init_values&#91;2] = {1, 3};
arg.array = init_values;
if (semctl(semid, 0, SETALL, arg) == -1) {
perror("初始化信号量失败");
semctl(semid, 0, IPC_RMID);
exit(EXIT_FAILURE);
}
printf("信号量初始化完成\n");
printf(" 信号量0(互斥): %d\n", semctl(semid, 0, GETVAL));
printf(" 信号量1(资源): %d\n", semctl(semid, 1, GETVAL));

// 示例1: 基本的P/V操作
printf("\n示例1: 基本的P/V操作\n");
printf("操作前 - 信号量0: %d, 信号量1: %d\n",
semctl(semid, 0, GETVAL), semctl(semid, 1, GETVAL));

// P操作获取互斥锁
printf("执行P操作获取互斥锁...\n");
if (P(semid, 0) == -1) {
perror("P操作失败");
} else {
printf("成功获取互斥锁\n");
printf("操作后 - 信号量0: %d, 信号量1: %d\n",
semctl(semid, 0, GETVAL), semctl(semid, 1, GETVAL));

// V操作释放互斥锁
printf("执行V操作释放互斥锁...\n");
if (V(semid, 0) == -1) {
perror("V操作失败");
} else {
printf("成功释放互斥锁\n");
printf("操作后 - 信号量0: %d, 信号量1: %d\n",
semctl(semid, 0, GETVAL), semctl(semid, 1, GETVAL));
}
}

// 示例2: 多进程同步演示
printf("\n示例2: 多进程同步演示\n");
pid_t pid = fork();

if (pid == -1) {
perror("fork失败");
semctl(semid, 0, IPC_RMID);
exit(EXIT_FAILURE);
} else if (pid == 0) {
// 子进程
printf("子进程 %d 开始执行\n", getpid());

// 模拟需要互斥访问的临界区
printf("子进程尝试获取互斥锁...\n");
if (P(semid, 0) == 0) {
printf("子进程 %d 进入临界区\n", getpid());
printf("子进程在临界区工作2秒...\n");
sleep(2);
printf("子进程 %d 离开临界区\n", getpid());
V(semid, 0); // 释放锁
}

exit(EXIT_SUCCESS);
} else {
// 父进程
sleep(1); // 让子进程先运行

printf("父进程 %d 开始执行\n", getpid());
printf("父进程尝试获取互斥锁...\n");

if (P(semid, 0) == 0) {
printf("父进程 %d 进入临界区\n", getpid());
printf("父进程在临界区工作2秒...\n");
sleep(2);
printf("父进程 %d 离开临界区\n", getpid());
V(semid, 0); // 释放锁
}

// 等待子进程结束
wait(NULL);
}

// 示例3: 资源计数信号量演示
printf("\n示例3: 资源计数信号量演示\n");
printf("当前可用资源数: %d\n", semctl(semid, 1, GETVAL));

// 模拟多个进程竞争资源
for (int i = 0; i < 4; i++) {
pid = fork();
if (pid == 0) {
// 子进程
printf("进程 %d 尝试获取资源...\n", getpid());

// P操作获取资源
if (P(semid, 1) == 0) {
printf("进程 %d 成功获取资源,剩余资源: %d\n",
getpid(), semctl(semid, 1, GETVAL));

// 使用资源
printf("进程 %d 使用资源2秒...\n", getpid());
sleep(2);

// V操作释放资源
V(semid, 1);
printf("进程 %d 释放资源,剩余资源: %d\n",
getpid(), semctl(semid, 1, GETVAL));
} else {
printf("进程 %d 获取资源失败\n", getpid());
}

exit(EXIT_SUCCESS);
}
}

// 等待所有子进程结束
for (int i = 0; i < 4; i++) {
wait(NULL);
}

// 清理资源
printf("\n清理资源...\n");
if (semctl(semid, 0, IPC_RMID) == -1) {
perror("删除信号量集失败");
} else {
printf("成功删除信号量集\n");
}

return 0;
}
data-ad-format="auto" data-full-width-responsive="true">