cha48.System V 共享内存

读书笔记

shmat/mmap时可以指定虚拟内存地址,指定其位置,这个位置是需要提前malloc或sbrk创建出来的吗?

48.1

使用事件标记来替换程序清单48-2(svshm_xfr_writerc)和程序清单48-3(svshm_xfr_reader.c)中的二元信号量。(就是27节的event flag)

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
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
//
// Created by root on 9/14/23.
//
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
#include <stdbool.h>
#include <stddef.h>
#include <syslog.h>
#include <wait.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>

#define DEBUG_LEVEL LOG_DEBUG

#ifndef DEBUG_LEVEL
#define DEBUG_LEVEL LOG_INFO
#endif
#define STRING_MSG "MSG"
FILE *logfile = NULL;
bool syslog_enable = false;
void __attribute__ ((constructor)) init() {
syslog_enable = false;
logfile = stderr;
}

#define alloc_sprintf(__alloc_sprintf_str, __format...) do { \
int __alloc_sprintf_len = snprintf(NULL, 0, __format); \
(__alloc_sprintf_str) = malloc(__alloc_sprintf_len+1); \
if(__alloc_sprintf_str != NULL) \
snprintf(__alloc_sprintf_str, __alloc_sprintf_len+1, __format); \
} while(0)

#define logger(level, msg...) do { \
if(level <= DEBUG_LEVEL) { \
fprintf(logfile, "[%ld] ", (long) getpid()); \
fprintf(logfile, msg); \
fprintf(logfile, "\n"); \
} \
if(syslog_enable) { \
char *data; \
alloc_sprintf(data, msg); \
syslog(level, "%s", data); \
safe_free(data); \
} \
} while(0)

#define COND_RET(x, ret, msg...) \
do { \
if(!(x)) { \
if(errno != 0) logger(LOG_ERR, "Error(%d): %s", errno, strerror(errno)); \
logger(LOG_ERR, "%s:%d", __FILE__, __LINE__); \
logger(LOG_ERR, "unmet condition:\"%s\"", #x); \
logger(LOG_ERR, msg); \
ret; \
} \
errno = 0; \
} while(0)

#define CHECK(x, msg...) COND_RET(x, return -1;, msg)
#define CHECK_EXIT(x, msg...) COND_RET(x, exit(EXIT_FAILURE);, msg)
#define CHECK_LOG(x, msg...) COND_RET(x, ;, msg)

#define safe_free(__safe_free_ptr) do { if(__safe_free_ptr) { free(__safe_free_ptr); (__safe_free_ptr) = NULL;} } while(0)

#define BUFFER_SIZE 4096

union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
#if defined(__linux__)
struct seminfo *__buf;
#endif
};

typedef struct {
int semid;
}EventFlag_t;

int incrsem(int sem_id, int semnum, short incr, short flg) {
struct sembuf sembuf;
sembuf = (struct sembuf){
.sem_num=semnum,
.sem_flg=flg,
.sem_op=incr
};
return semop(sem_id, &sembuf, 1);
}

int P(int sem_id) {
return incrsem(sem_id, 0, -1, 0);
}

int V(int sem_id) {
return incrsem(sem_id, 0, 1, 0);
}

int waitFor(int sem_id) {
return incrsem(sem_id, 1, 0, 0);
}

int getsem(int sem_id, int semnum, short *n) {
union semun arg;
int ret = semctl(sem_id, semnum, GETVAL, arg);
*n = ret;
return ret;
}

int setsem(int sem_id, int semnum, short n) {
union semun arg;
arg.val = n;
return semctl(sem_id, semnum, SETVAL, arg);
}

int notifyAll(int sem_id) {
return setsem(sem_id, 1, 0);
}
EventFlag_t *newEventFlag(const char *file, char x) {
EventFlag_t eventFlag;
key_t key = ftok(file, x);
eventFlag.semid = semget(key, 4, IPC_CREAT | IPC_EXCL | 0666); // sem[0] as mutex, sem[1] as notifier, sem[2]|sem[3] as flag
union semun arg;
if(eventFlag.semid == -1) {
if(errno == EEXIST) {
eventFlag.semid = semget(key, 4, 0666);
COND_RET(eventFlag.semid != -1, return NULL, STRING_MSG);
struct semid_ds ds;
arg.buf = &ds;
COND_RET(semctl(eventFlag.semid, 0, IPC_STAT, arg) != -1, return NULL, STRING_MSG);
while (ds.sem_otime == 0) {
logger(LOG_INFO, "waiting for sem init");
sleep(1);
COND_RET(semctl(eventFlag.semid, 0, IPC_STAT, arg) != -1, return NULL, STRING_MSG);
}
logger(LOG_INFO, "semget get old");
} else {
CHECK_LOG(false, STRING_MSG);
return NULL;
}
} else {
arg.array = (unsigned short *)&(unsigned short[]){0, 0, 0, 0}; // sem[0] = 1, sem[1] = 0
if(semctl(eventFlag.semid, 0, SETALL, arg) == -1) {
CHECK_LOG(false, STRING_MSG);
CHECK_LOG(semctl(eventFlag.semid, 0, IPC_RMID) != -1, STRING_MSG);
return NULL;
}
if(incrsem(eventFlag.semid, 0,1,0) == -1) {
CHECK_LOG(false, STRING_MSG);
CHECK_LOG(semctl(eventFlag.semid, 0, IPC_RMID) != -1, STRING_MSG);
return NULL;
}
logger(LOG_INFO, "semget create new");
}
return memcpy(malloc(sizeof(EventFlag_t)), &eventFlag, sizeof(EventFlag_t));
}

int __setEventFlag(EventFlag_t *eventFlag, int flag) {
short currentFlag;

CHECK(getsem(eventFlag->semid, 2, &currentFlag) != -1, STRING_MSG);
CHECK(setsem(eventFlag->semid, 2, currentFlag | (flag & 0xffff)) != -1, STRING_MSG);

CHECK(getsem(eventFlag->semid, 3, &currentFlag) != -1, STRING_MSG);
CHECK(setsem(eventFlag->semid, 3, currentFlag | ((flag >> 16) & 0xffff)) != -1, STRING_MSG);

CHECK(notifyAll(eventFlag->semid) != -1, STRING_MSG);
}

int setEventFlag(EventFlag_t *eventFlag, int flag) {
CHECK(P(eventFlag->semid) != -1, STRING_MSG);
CHECK(__setEventFlag(eventFlag, flag) != -1, STRING_MSG);
CHECK(V(eventFlag->semid) != -1, STRING_MSG);
return 0;
}

int __clearEventFlag(EventFlag_t *eventFlag, int flag) {
short currentFlag;

CHECK(getsem(eventFlag->semid, 2, &currentFlag) != -1, STRING_MSG);
CHECK(setsem(eventFlag->semid, 2, currentFlag & ~(flag & 0xffff)) != -1, STRING_MSG);

CHECK(getsem(eventFlag->semid, 3, &currentFlag) != -1, STRING_MSG);
CHECK(setsem(eventFlag->semid, 3, currentFlag & ~((flag >> 16) & 0xffff)) != -1, STRING_MSG);
// CHECK(notifyAll(eventFlag->semid) != -1, STRING_MSG);
return 0;
}
int clearEventFlag(EventFlag_t *eventFlag, int flag) {
CHECK(P(eventFlag->semid) != -1, STRING_MSG);
CHECK(__clearEventFlag(eventFlag, flag) != -1, STRING_MSG);
CHECK(V(eventFlag->semid) != -1, STRING_MSG);
return 0;
}

int __getEventFlag(EventFlag_t *eventFlag, int *flag) {
if(flag) {
short currentFlag = 0;
int ret = 0;
CHECK(getsem(eventFlag->semid, 2, &currentFlag) != -1, STRING_MSG);
ret |= currentFlag;
CHECK(getsem(eventFlag->semid, 3, &currentFlag) != -1, STRING_MSG);
ret |= currentFlag << 16;
*flag = ret;
return 0;
}
return -1;
}

int getEventFlag(EventFlag_t *eventFlag, int *flag) {
CHECK(P(eventFlag->semid) != -1, STRING_MSG);
CHECK_LOG(__getEventFlag(eventFlag, flag) != -1, STRING_MSG);
CHECK(V(eventFlag->semid) != -1, STRING_MSG);
return 0;
}
#define waitForMethod(eventFlag, flag, method) do { \
int currentFlag;\
CHECK(P(eventFlag->semid) != -1, STRING_MSG);\
__getEventFlag(eventFlag, &currentFlag); \
while (method) {\
CHECK(V(eventFlag->semid) != -1, STRING_MSG);\
CHECK(waitFor(eventFlag->semid) != -1, STRING_MSG);\
CHECK(P(eventFlag->semid) != -1, STRING_MSG);\
__getEventFlag(eventFlag, &currentFlag); \
}\
CHECK(V(eventFlag->semid) != -1, STRING_MSG);\
} while(0)

int waitForAny(EventFlag_t *eventFlag, int flag) {
waitForMethod(eventFlag, flag, (currentFlag & flag) == 0);
return 0;
}
int waitForAll(EventFlag_t *eventFlag, int flag) {
waitForMethod(eventFlag, flag, (currentFlag & flag) != flag);
return 0;
}

int waitForAllAndClear(EventFlag_t *eventFlag, int flag) {
int currentFlag;
CHECK(P(eventFlag->semid) != -1, STRING_MSG);
__getEventFlag(eventFlag, &currentFlag);
while ((currentFlag & flag) != flag) {
CHECK(V(eventFlag->semid) != -1, STRING_MSG);
CHECK(waitFor(eventFlag->semid) != -1, STRING_MSG);
CHECK(P(eventFlag->semid) != -1, STRING_MSG);
__getEventFlag(eventFlag, &currentFlag);
}
__clearEventFlag(eventFlag, flag);
CHECK(V(eventFlag->semid) != -1, STRING_MSG);
return 0;
}
void destroyEventFlag(EventFlag_t **eventFlag) {
if(eventFlag) {
if(*eventFlag) {
semctl((*eventFlag)->semid, 0, IPC_RMID);
}
safe_free(*eventFlag);
}
}

#define WRITE_FLAG 1
#define READ_FLAG 2
struct ShmStruct {
ssize_t len;
char buf[BUFFER_SIZE - sizeof(int)];
};

void onExitEventFlag(int status, void *arg) {
destroyEventFlag((EventFlag_t **)&arg);
logger(LOG_INFO, "onExitEventFlag, status = %d", status);
}

void onExitSHM(int status, void *id) {
shmctl(*(int *)id, IPC_RMID, NULL);
logger(LOG_INFO, "onExitSHM, status = %d", status);
}

void onExitSHMdt(int status, void *ptr) {
shmdt(ptr);
logger(LOG_INFO, "onExitSHMdt, status = %d", status);
}

void handler(int sig) {
exit(0);
}
int main(int argc, char **argv) {
CHECK_EXIT(argc == 2, "Usage: %s writer|reader", argv[0]);
signal(SIGINT, handler);
signal(SIGTERM, handler);
signal(SIGHUP, handler);
EventFlag_t *eventFlag = newEventFlag(argv[0], 'a');
on_exit(onExitEventFlag, eventFlag);
key_t key = ftok(argv[0], 'c');
CHECK_EXIT(key != -1, STRING_MSG);
int shmid = shmget(key, BUFFER_SIZE, IPC_CREAT | IPC_EXCL | 0666);
int savedErrno = errno;
CHECK_EXIT(shmid != -1 || errno == EEXIST, STRING_MSG);
if(shmid != -1) {
setEventFlag(eventFlag, WRITE_FLAG);
} else {
if(savedErrno == EEXIST) {
shmid = shmget(key, BUFFER_SIZE, 0666);
} else {
CHECK_EXIT(false, "code will never reach");
}
}
on_exit(onExitSHM, &shmid);
struct ShmStruct *shmp = shmat(shmid, NULL, 0);
CHECK_EXIT(shmp != NULL, STRING_MSG);
on_exit(onExitSHMdt, shmp);
if(!strcmp(argv[1], "writer")) {
size_t size = 0;
bool stop = false;
for(int i = 0; !stop ;i++) {
CHECK_EXIT(waitForAllAndClear(eventFlag, WRITE_FLAG) != -1, STRING_MSG);
logger(LOG_INFO, "write start");
CHECK_EXIT((shmp->len = read(STDIN_FILENO, shmp->buf, BUFFER_SIZE - sizeof(int))) >= 0, STRING_MSG);
size += shmp->len;
if(shmp->len == 0) {
stop = true;
}
CHECK_EXIT(setEventFlag(eventFlag, READ_FLAG) != -1, STRING_MSG);
logger(LOG_INFO, "write end: i = %d, size = %lu", i, size);
}
} else {
size_t size = 0;
bool stop = false;
for(int i = 0; !stop ;i++) {
CHECK_EXIT(waitForAllAndClear(eventFlag, READ_FLAG) != -1, STRING_MSG);
logger(LOG_INFO, "read start");
CHECK_EXIT(write(STDIN_FILENO, shmp->buf, shmp->len) == shmp->len, STRING_MSG);
size += shmp->len;
if(shmp->len == 0) {
stop = true;
}
CHECK_EXIT(setEventFlag(eventFlag, WRITE_FLAG) != -1, STRING_MSG);
logger(LOG_INFO, "read end: i = %d, size = %lu", i, size);
}

}
}

48.2

解释为何程序清单48-3在 for 循环被修改成如下形式时会错误地报告了传输字节数。

1
2
3
4
5
6
for (xfrs =0,bytes = 0; shmp->cnt != 0; xfrs++,bytes += shmp->cnt) {
reserveSem(semid,READ SEM); /*Wait for return*/
if(write(STDOUT_FILENO,shmp->buf,shmp->cnt) != shmp->cnt)
fatal("write");
releaseSem(semid,WRITE SEM); /*Give writer a return*/
}

原版代码

1
2
3
4
5
6
7
for (xfrs =0; shmp->cnt != 0; xfrs++) {
reserveSem(semid,READ SEM); /*Wait for return*/
if(write(STDOUT_FILENO,shmp->buf,shmp->cnt) != shmp->cnt)
fatal("write");
bytes += shmp->cnt;
releaseSem(semid,WRITE SEM); /*Give writer a return*/
}

很显然,他将bytes的自增放在了Sem锁定区域之外,由于shmp指向的是共享内存,在releaseSembytes增加之间,shmp指向的区域内存中的值可能已经发生改变,造成统计不准确。

48.5

编写一个目录服务使之使用一个共享内存段来发布名称-值对。程序需要提供一个API来允许调用者创建新名称、修改一个既有名称、删除一个既有名称以及获取与个名称相关联的值。使用信号量来确保一个执行共享内存段更新操作的进程能够互斥地访问段。

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
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
//
// Created by root on 9/14/23.
//
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
#include <stdbool.h>
#include <stddef.h>
#include <syslog.h>
#include <wait.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>

#define DEBUG_LEVEL LOG_DEBUG

#ifndef DEBUG_LEVEL
#define DEBUG_LEVEL LOG_INFO
#endif
#define STRING_MSG "MSG"
FILE *logfile = NULL;
bool syslog_enable = false;
void __attribute__ ((constructor)) init() {
syslog_enable = false;
logfile = stderr;
}

#define alloc_sprintf(__alloc_sprintf_str, __format...) do { \
int __alloc_sprintf_len = snprintf(NULL, 0, __format); \
(__alloc_sprintf_str) = malloc(__alloc_sprintf_len+1); \
if(__alloc_sprintf_str != NULL) \
snprintf(__alloc_sprintf_str, __alloc_sprintf_len+1, __format); \
} while(0)

#define logger(level, msg...) do { \
if(level <= DEBUG_LEVEL) { \
fprintf(logfile, "[%ld] ", (long) getpid()); \
fprintf(logfile, msg); \
fprintf(logfile, "\n"); \
} \
if(syslog_enable) { \
char *__syslog_enable__data; \
alloc_sprintf(__syslog_enable__data, msg); \
syslog(level, "%s", __syslog_enable__data); \
safe_free(__syslog_enable__data); \
} \
} while(0)

#define COND_RET(x, ret, msg...) \
do { \
if(!(x)) { \
if(errno != 0) logger(LOG_ERR, "Error(%d): %s", errno, strerror(errno)); \
logger(LOG_ERR, "%s:%d", __FILE__, __LINE__); \
logger(LOG_ERR, "unmet condition:\"%s\"", #x); \
logger(LOG_ERR, msg); \
ret; \
} \
errno = 0; \
} while(0)

#define CHECK(x, msg...) COND_RET(x, return -1;, msg)
#define CHECK_EXIT(x, msg...) COND_RET(x, exit(EXIT_FAILURE);, msg)
#define CHECK_LOG(x, msg...) COND_RET(x, ;, msg)

#define safe_free(__safe_free_ptr) do { if(__safe_free_ptr) { free(__safe_free_ptr); (__safe_free_ptr) = NULL;} } while(0)

#define BUFFER_SIZE 4096*4096

union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
#if defined(__linux__)
struct seminfo *__buf;
#endif
};


int incrsem(int sem_id, int semnum, short incr, short flg) {
struct sembuf sembuf;
sembuf = (struct sembuf){
.sem_num=semnum,
.sem_flg=flg,
.sem_op=incr
};
return semop(sem_id, &sembuf, 1);
}

int P(int sem_id, int semnum) {
return incrsem(sem_id, semnum, -1, 0);
}

int V(int sem_id, int semnum) {
return incrsem(sem_id, semnum, 1, 0);
}

int getsem(int sem_id, int semnum, short *n) {
union semun arg;
int ret = semctl(sem_id, semnum, GETVAL);
*n = ret;
return ret;
}

int setsem(int sem_id, int semnum, short n) {
union semun arg;
arg.val = n;
return semctl(sem_id, semnum, SETVAL, arg);
}

int newMutex(const char *file, char x, int semcnt) {
key_t key = ftok(file, x);
int semid = semget(key, semcnt, IPC_CREAT | IPC_EXCL | 0666); // sem[0] as mutex, sem[1] as notifier, sem[2]|sem[3] as flag
union semun arg;
if(semid == -1) {
if(errno == EEXIST) {
semid = semget(key, semcnt, 0666);
CHECK(semid != -1, STRING_MSG);
struct semid_ds ds;
arg.buf = &ds;
CHECK(semctl(semid, 0, IPC_STAT, arg) != -1, STRING_MSG);
while (ds.sem_otime == 0) {
logger(LOG_INFO, "waiting for sem init");
sleep(1);
CHECK(semctl(semid, 0, IPC_STAT, arg) != -1, STRING_MSG);
}
logger(LOG_INFO, "semget get old: %d", semid);
} else {
CHECK_LOG(false, STRING_MSG);
return -1;
}
} else {
arg.array = (unsigned short *)malloc(sizeof(unsigned short) * semcnt); // sem[0] = 0, sem[1] = 1, sem[2] = 1, ...
for(int i = 0; i < semcnt; i++) {
arg.array[i] = 1;
}
arg.array[0] = 0;
if(semctl(semid, 0, SETALL, arg) == -1) {
CHECK_LOG(false, STRING_MSG);
CHECK_LOG(semctl(semid, 0, IPC_RMID) != -1, STRING_MSG);
return -1;
}
safe_free(arg.array);
if(incrsem(semid, 0,1,0) == -1) {
CHECK_LOG(false, STRING_MSG);
CHECK_LOG(semctl(semid, 0, IPC_RMID) != -1, STRING_MSG);
return -1;
}
logger(LOG_INFO, "semget create new: %d", semid);
}
return semid;
}
void handler(int sig) {
logger(LOG_INFO, "received signal(%d):%s", sig, strsignal(sig));
exit(0);
}

#define KVMax 1024*8
#define KMax 1024
#define VMax 1024
struct KVData {
char key[KMax];
char val[VMax];
};
void *initshm(int argc, char *argv[], int *shmid) {
signal(SIGINT, handler);
signal(SIGTERM, handler);
signal(SIGHUP, handler);
key_t key = ftok(argv[0], 'c');
CHECK_EXIT(key != -1, STRING_MSG);
*shmid = shmget(key, BUFFER_SIZE, IPC_CREAT | IPC_EXCL | 0666);
int savedErrno = errno;
CHECK_EXIT(*shmid != -1 || errno == EEXIST, STRING_MSG);
bool creator = false;
if(*shmid == -1) {
if(savedErrno == EEXIST) {
*shmid = shmget(key, BUFFER_SIZE, 0666);
} else {
CHECK_EXIT(false, "code will never reach");
}
} else {
creator = true;
}
void *shmp = shmat(*shmid, NULL, 0);
CHECK_EXIT(shmp != NULL, STRING_MSG);
if(creator) memset(shmp, 0, BUFFER_SIZE);
return shmp;
}

int getValue(int semid, char *head, char* key, char *value) {
CHECK(head != NULL, "head should not be null");
CHECK(key != NULL, "key should not be null");
CHECK(value != NULL, "value should not be null");
bool ok = false;
for(int i = 0; i < KVMax && !ok; i++) {
CHECK(P(semid, i) != -1, STRING_MSG);
struct KVData *data = (struct KVData *)head;
if(!strcmp(key, data->key)) {
logger(DEBUG_LEVEL, "data->key = %s", data->key);
logger(DEBUG_LEVEL, "data->val = %s", data->val);
strncpy(value, data->val, VMax);
value[VMax-1] = 0;
ok = true;
} else {
head += sizeof(struct KVData);
}
CHECK(V(semid, i) != -1, STRING_MSG);
}
if(!ok) return -1;
return 0;
}
int setValue(int semid, char *head, char* key, char *value) {
CHECK(head != NULL, "head should not be null");
CHECK(key != NULL, "key should not be null");
CHECK(value != NULL, "value should not be null");
bool ok = false;
for(int i = 0; i < KVMax && !ok; i++) {
CHECK(P(semid, i) != -1, STRING_MSG);
struct KVData *data = (struct KVData *)head;
logger(DEBUG_LEVEL, "data = %p, key = %s, val = %s", data, data->key, data->val);
if(!strcmp(key, data->key)) {
strncpy(data->val, value, VMax);
data->val[VMax-1] = 0;
ok = true;
} else {
head += sizeof(struct KVData);
}
CHECK(V(semid, i) != -1, STRING_MSG);
}
if(!ok) return -1;
return 0;
}

int addKV(int semid, char *head, char* key, char *value) {
CHECK(head != NULL, "head should not be null");
CHECK(key != NULL, "key should not be null");
CHECK(value != NULL, "value should not be null");
bool ok = false;
for(int i = 0; i < KVMax && !ok; i++) {
CHECK(P(semid, i) != -1, STRING_MSG);
struct KVData *data = (struct KVData *)head;
if(!*data->key) {
logger(DEBUG_LEVEL, "i = %d, data = %p", i, data);
strncpy(data->key, key, KMax);
strncpy(data->val, value, VMax);
data->key[KMax-1] = 0;
data->val[VMax-1] = 0;
ok = true;
} else {
head += sizeof(struct KVData);
}
CHECK(V(semid, i) != -1, STRING_MSG);
}
if(!ok) return -1;
return 0;
}

int getAll(int semid, char *head, char *value) {
CHECK(head != NULL, "head should not be null");
CHECK(value != NULL, "value should not be null");
char *ret = value;
for(int i = 0; i < KVMax; i++) {
CHECK(P(semid, i) != -1, STRING_MSG);
struct KVData *data = (struct KVData *)head;
if(*data->key) {
strncpy(ret, data->key, KMax);
ret = strchr(ret, '\0');
strcpy(ret, ":");
ret++;
strncpy(ret, data->val, VMax);
ret = strchr(ret, '\0');
strcpy(ret, "\n");
ret++;
}
head += sizeof(struct KVData);
CHECK(V(semid, i) != -1, STRING_MSG);
}
return 0;
}

int rmKV(int semid, char *head, char* key, char *value) {
CHECK(key != NULL, "key should not be null");
CHECK(head != NULL, "head should not be null");
CHECK(value != NULL, "value should not be null");
bool ok = false;
for(int i = 0; i < KVMax && !ok; i++) {
CHECK(P(semid, i) != -1, STRING_MSG);
struct KVData *data = (struct KVData *)head;
if(!strcmp(key, data->key)) {
strncpy(value, data->val, VMax);
value[VMax-1] = 0;
data->key[0] = 0;
data->val[0] = 0;
ok = true;
} else {
head += sizeof(struct KVData);
}
CHECK(V(semid, i) != -1, STRING_MSG);
}
if(!ok) return -1;
return 0;
}


int main(int argc, char *argv[]) {
CHECK_EXIT(argc > 1, "Usage: %s add|get|set|getAll|rm|destroy", argv[0]);
int shmid;
char *shmp = initshm(argc, argv, &shmid);
int mutexid = newMutex(argv[0], 'a', KVMax+1);
CHECK_EXIT(mutexid != -1, STRING_MSG);
if(!strcmp("get", argv[1])) {
CHECK_EXIT(argc > 2, "Usage: %s set [key]", argv[0]);
char *value = malloc(VMax);
for(int i = 2; i < argc; i++) {
getValue(mutexid, shmp, argv[i], value);
logger(LOG_INFO, "value for \"%s\" is %s", argv[i], value);
}
safe_free(value);
} else if(!strcmp("set", argv[1])) {
CHECK_EXIT(argc > 2, "Usage: %s set [key:value]", argv[0]);
for(int i = 2; i < argc; i++) {
char *value = strchr(argv[i], ':');
if(!value) {
CHECK_EXIT(false, "Usage: %s set [key:value]", argv[0]);
}
*value = 0;
value++;
setValue(mutexid, shmp, argv[i], value);
logger(LOG_INFO, "value for \"%s\" is %s", argv[i], value);
}
} else if(!strcmp("add", argv[1])) {
CHECK_EXIT(argc > 2, "Usage: %s add [key:value]", argv[0]);
for(int i = 2; i < argc; i++) {
char *value = strchr(argv[i], ':');
if(!value) {
CHECK_EXIT(false, "Usage: %s set [key:value]", argv[0]);
}
*value = 0;
value++;
addKV(mutexid, shmp, argv[i], value);
logger(LOG_INFO, "value for \"%s\" is %s", argv[i], value);
}
} else if(!strcmp("rm", argv[1])) {
CHECK_EXIT(argc > 2, "Usage: %s add [key:value]", argv[0]);
char *value = malloc(VMax);
for(int i = 2; i < argc; i++) {
rmKV(mutexid, shmp, argv[i], value);
logger(LOG_INFO, "value for \"%s\" is %s", argv[i], value);
}
safe_free(value);
} else if(!strcmp("getAll", argv[1])) {
char *value = malloc((VMax + KMax) * KVMax);
getAll(mutexid, shmp, value);
logger(LOG_INFO, "getAll");
logger(LOG_INFO, "%s", value);
safe_free(value);
} else if(!strcmp("destroy", argv[1])) {
CHECK_LOG(semctl(mutexid, 0, IPC_RMID) != -1, STRING_MSG);
CHECK_LOG(shmctl(shmid, IPC_RMID, NULL) != -1, STRING_MSG);
} else {
CHECK_EXIT(false, "unknown operator: %s", argv[1]);
}
return 0;
}
作者

Meow Meow Liu

发布于

2023-09-14

更新于

2025-04-15

许可协议

评论