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
239
240
241
242
243
244
245
246
247
|
/* SPDX-License-Identifier: GPL-2.0 */
/** \file ipc.c Inter-process communication and shared memory helpers. */
#include "para.h"
#include "error.h"
#include "ipc.h"
#include <sys/types.h>
#include <sys/param.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
/**
* Define a new mutex.
*
* \return The identifier for the new mutex on success, a negative error code
* on errors.
*
* \sa semget(2).
*/
int mutex_new(void)
{
int ret = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);
return ret < 0? -ERRNO_TO_PARA_ERROR(errno) : ret;
}
/**
* Destroy a mutex.
*
* \param id The identifier of the mutex to be destroyed.
*
* \sa semctl(2)
*/
void mutex_destroy(int id)
{
if (semctl(id, 0, IPC_RMID) < 0)
PARA_WARNING_LOG("%s\n", para_strerror(errno));
}
static void para_semop(int id, struct sembuf *sops, int num)
{
do {
if (semop(id, sops, num) >= 0)
return;
} while (errno == EINTR);
if (errno == EIDRM) {
PARA_CRIT_LOG("semaphore set %d was removed\n", id);
return;
}
PARA_EMERG_LOG("fatal semop error %s: pid %d\n", strerror(errno),
(int)getpid());
exit(EXIT_FAILURE);
}
/**
* Lock the given mutex.
*
* \param id The identifier of the shared memory area to lock.
*
* This function either succeeds or aborts.
*
* \sa semop(2), struct \ref misc_meta_data.
*/
void mutex_lock(int id)
{
struct sembuf sops[2] = {
{
.sem_num = 0,
.sem_op = 0,
.sem_flg = SEM_UNDO
},
{
.sem_num = 0,
.sem_op = 1,
.sem_flg = SEM_UNDO
}
};
para_semop(id, sops, 2);
}
/**
* Unlock a mutex.
*
* \param id The identifier of the mutex.
*
* This function either succeeds or aborts.
*
* \sa semop(2), struct \ref misc_meta_data.
*/
void mutex_unlock(int id)
{
struct sembuf sops[1] = {
{
.sem_num = 0,
.sem_op = -1,
.sem_flg = SEM_UNDO
},
};
para_semop(id, sops, 1);
}
/**
* Create a new shared memory area of given size.
*
* \param size The size of the shared memory area to create.
*
* \return The id of the shared memory array on success, a negative error
* code on errors.
*
* \sa shmget(2).
*/
int shm_new(size_t size)
{
int ret = shmget(IPC_PRIVATE, size, IPC_CREAT | IPC_EXCL | 0600);
return ret < 0 ? -ERRNO_TO_PARA_ERROR(errno) : ret;
}
/**
* Destroy the given shared memory area.
*
* \param id The shared memory identifier.
*
* \return The return value of the underlying shmctl() call on success,
* a negative error code on errors.
*
* \sa shmctl(2).
*/
int shm_destroy(int id)
{
struct shmid_ds shm_desc;
int ret = shmctl(id, IPC_RMID, &shm_desc);
return ret < 0? -ERRNO_TO_PARA_ERROR(errno) : ret;
}
/**
* Attach a shared memory segment.
*
* \param id The identifier of the shared memory segment to attach.
* \param mode Either ATTACH_RO (read only) or ATTACH_RW (read/write).
* \param result Points to the attached area just attached on success.
*
* \return Standard.
*
* \sa shmat(2).
*/
int shm_attach(int id, enum shm_attach_mode mode, void **result)
{
if (mode == ATTACH_RW)
*result = shmat(id, NULL, 0);
else
*result = shmat(id, NULL, SHM_RDONLY);
return *result == (void *) -1? -ERRNO_TO_PARA_ERROR(errno) : 1;
}
/**
* Get the size of a shared memory segment.
*
* \param id The shared memory segment identifier.
* \param result Size in bytes is returned here, zero on errors.
*
* \return Standard.
*
* \sa shmctl(2).
*/
int shm_size(int id, size_t *result)
{
struct shmid_ds ds; /* data structure */
*result = 0;
if (shmctl(id, IPC_STAT, &ds) < 0)
return -ERRNO_TO_PARA_ERROR(errno);
*result = ds.shm_segsz;
return 1;
}
/**
* Detach a shared memory segment.
*
* \param addr The address of the attached segment.
*
* \return Standard.
*
* \sa shmdt(2).
*/
int shm_detach(void *addr)
{
int ret = shmdt(addr);
return ret < 0? -ERRNO_TO_PARA_ERROR(errno) : 1;
}
# if defined __FreeBSD__ || defined __NetBSD__
#include <sys/sysctl.h>
# define SYSCTL_SHMMAX_VARIABLE "kern.ipc.shmmax"
# else
# undef SYSCTL_SHMMAX_VARIABLE
# endif
/**
* Get the maximal size of a shared memory area.
*
* The value is only computed once when the function is called for the first
* time. Subsequent calls return the number which was computed during the
* first call.
*
* \return A number suitable as an argument to \ref shm_new().
*/
size_t shm_get_shmmax(void)
{
static size_t shmmax;
if (shmmax > 0) /* only dance once */
return shmmax;
#ifdef __linux__ /* get it from proc fs */
{
int fd = open("/proc/sys/kernel/shmmax", O_RDONLY);
if (fd >= 0) {
char buf[100];
int ret = read(fd, buf, sizeof(buf) - 1);
if (ret > 0) {
buf[ret] = '\0';
shmmax = strtoul(buf, NULL, 10);
}
close(fd);
}
}
#elif defined SYSCTL_SHMMAX_VARIABLE
{
size_t len = sizeof(shmmax);
sysctlbyname(SYSCTL_SHMMAX_VARIABLE, &shmmax, &len, NULL, 0);
}
#elif defined SHMMAX
shmmax = SHMMAX;
#endif
/*
* The value may be too large for allocating memory. In particular, on
* 32 bit Linux it is almost 4G.
*/
shmmax = PARA_MIN((size_t)10 * 1024 * 1024, shmmax);
if (shmmax == 0) {
PARA_WARNING_LOG("unable to determine shmmax\n");
shmmax = 65535; /* last resort */
}
PARA_INFO_LOG("shmmax: %zu\n", shmmax);
return shmmax;
}
|