1 /*
2 * Copyright (C) Bull S.A. 1996
3 * Copyright (c) International Business Machines Corp., 2001
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
13 * the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19 /*---------------------------------------------------------------------+
20 | shmem_test_03 |
21 | ==================================================================== |
22 | |
23 | Description: Verify shared memory mapping of /dev/zero with |
24 | exclusive writes and shared reads using semaphores |
25 | as the synchronization method. |
26 | |
27 | Algorithm: o Obtain three shared memory segments by mapping |
28 | /dev/zero, one for random data created by the |
29 | parent, another for the childs checksums, and the |
30 | last for the read count (number of child processes |
31 | reading the data). |
32 | o Spawn N child processes |
33 | o Parent: |
34 | - obtain write lock (exclusive) on data |
35 | - fill shared memory segment with data |
36 | - compute data checksum |
37 | - release lock |
38 | o Child: |
39 | - obtain read lock (shared) on data |
40 | - compute data checksum |
41 | - release lock |
42 | - store checksum in other shared memory segment |
43 | o Parent: |
44 | - wait for child proceeses to complete |
45 | - compare child's checksum (from shared memory) |
46 | with that of the parent |
47 | |
48 | System calls: The following system calls are tested: |
49 | |
50 | mmap () - Maps a file-system object into memory |
51 | semget () - Gets a set of semaphores |
52 | semctl () - Controls semaphore operations |
53 | semop () - Preforms semaphore operations |
54 | |
55 | Usage: shmem_test_03 [-c num_children] [-s shmem_size] |
56 | |
57 | To compile: cc -o shmem_test_03 shmem_test_03.c |
58 | |
59 | Last update: Ver. 1.2, 2/8/94 00:08:45 |
60 | |
61 | Change Activity |
62 | |
63 | Version Date Name Reason |
64 | 0.1 012093 DJK Initial version for AIX 4.1 |
65 | 1.2 020794 DJK Moved to "prod" directory |
66 | |
67 +---------------------------------------------------------------------*/
68
69 #include <errno.h>
70 #include <stdio.h>
71 #include <stdlib.h>
72 #include <string.h>
73 #include <unistd.h>
74 #include <limits.h>
75 #include <fcntl.h>
76 #include <sys/file.h>
77 #include <sys/ipc.h>
78 #include <sys/mman.h>
79 #include <sys/sem.h>
80 #include <sys/signal.h>
81 #include <sys/types.h>
82 #include <sys/wait.h>
83 #include "lapi/semun.h"
84
85 /* Defines
86 *
87 * USAGE: usage statement
88 */
89 #define MAX_CHILDREN 400
90 #define DEFAULT_NUM_CHILDREN 2
91 #define DEFAULT_SHMEM_SIZE 100000
92 #define USAGE "\nUsage: %s [-c num_children] [-s shmem_size]\n\n" \
93 "\t-c num_children number of child processes to spawn\n" \
94 "\t-s shmem_size size of shared memory segment (bytes)\n" \
95 "\t (must be less than 256MB!)\n\n"
96
97 /*
98 * Function prototypes
99 *
100 * create_semaphores (): Create semaphores for synchorizing memory accesses
101 * delete_semaphores (): Delete the semaphores
102 * lock_resource (): Obtains the resource (shared memory segment)
103 * unlock_resource (): Releases the resource (shared memory segment)
104 * parse_args (): Parse command line arguments
105 * setup_signal_handlers (): Setup the signal catching function
106 * handler (): Signal catching function
107 * child (): Child process
108 * sys_error (): System error message function
109 * error (): Error message function
110 * cleanup (): Releases semaphores & kills child processes
111 */
112 static void create_semaphores();
113 static void delete_semaphores();
114 static void lock_resource(int);
115 static void unlock_resource(int);
116 static void parse_args(int, char **);
117 static void setup_signal_handlers();
118 static void handler(int, int, struct sigcontext *);
119 static void child(int, unsigned char *);
120 static void sys_error(const char *, int);
121 static void error(const char *, int);
122 static void cleanup();
123
124 /*
125 * Global Variables:
126 *
127 * checksum: Array of checksums computed by child processes
128 * parent_pid: Process id of the parent
129 * pid: Array of child process id's
130 * semid: System wide unique shared memory identifier
131 * num_children: number of child processes to spawn
132 * buffer_size: size of "scratch" shared memory segment
133 * read_count: number of child processes reading data
134 */
135 enum { READ_COUNT, WRITE }; /* semaphore constants */
136 int *read_count;
137
138 unsigned long *checksum; /* shared memory segment address */
139 pid_t parent_pid; /* process id of parent */
140 pid_t pid[MAX_CHILDREN]; /* child processes process id's */
141 int semid; /* semaphore id */
142 int num_children = DEFAULT_NUM_CHILDREN;
143 int buffer_size = DEFAULT_SHMEM_SIZE;
144
145 union semun arg;
146
147 /*---------------------------------------------------------------------+
148 | main |
149 | ==================================================================== |
150 | |
151 | Function: Main program (see prolog for more details) |
152 | |
153 | Returns: (0) Successful completion |
154 | (-1) Error occurred |
155 | |
156 +---------------------------------------------------------------------*/
main(int argc,char ** argv)157 int main(int argc, char **argv)
158 {
159 int fd; /* Misc file descriptor */
160 int i; /* Misc loop index */
161 int shmem_size; /* Size (in bytes) of shared memory segment */
162 int status; /* Child processes exit status */
163 unsigned char *ptr; /* Misc pointer */
164 unsigned char data = 0; /* Value written into shared memory segment */
165 unsigned char *shmptr; /* Shared memory segment address */
166 unsigned long cksum; /* Shared memory segment checksum */
167
168 /*
169 * Parse command line arguments and print out program header
170 */
171 parse_args(argc, argv);
172 printf("%s: IPC Shared Memory TestSuite program\n", *argv);
173
174 /*
175 * Setup the signal handlers (in case user aborts program).
176 *
177 * Create the semaphores to insure exclusive writes to the
178 * shared memory segment.
179 *
180 * Save the parent process id and initialize the array of child
181 * process ids.
182 */
183 setup_signal_handlers();
184 create_semaphores();
185
186 parent_pid = getpid();
187 for (i = 0; i < num_children; i++)
188 pid[i] = (pid_t) 0;
189
190 /*
191 * Create a shared memory segment for storing the read count
192 * (number of child processes reading shared data)
193 * After creating the shared memory segment, initialize it.
194 */
195 if ((fd = open("/dev/zero", O_RDWR)) < 0)
196 sys_error("open failed", __LINE__);
197 if ((read_count = (int *)
198 mmap(0, sizeof(int), PROT_READ | PROT_WRITE,
199 MAP_SHARED, fd, 0)) < 0)
200 sys_error("mmap failed", __LINE__);
201 close(fd);
202 *read_count = 0;
203
204 /*
205 * Create a shared memory segment for storing the child
206 * processes checksums by memory mapping /dev/zero.
207 * After creating the shared memory segment, initialize it.
208 */
209 if ((fd = open("/dev/zero", O_RDWR)) < 0)
210 sys_error("open failed", __LINE__);
211 shmem_size = sizeof(unsigned long) * num_children;
212 if ((checksum = (unsigned long *)
213 mmap(0, shmem_size, PROT_READ | PROT_WRITE,
214 MAP_SHARED, fd, 0)) < 0)
215 sys_error("mmap failed", __LINE__);
216 close(fd);
217
218 for (i = 0; i < num_children; i++)
219 *(checksum + (sizeof(unsigned long) * i)) = 0;
220
221 /*
222 * Create the "scratch" shared memory segment for storing
223 * a series of values by memory mapping /dev/zero.
224 */
225 if ((fd = open("/dev/zero", O_RDWR)) < 0)
226 sys_error("open failed", __LINE__);
227
228 printf("\n\tGet shared memory segment (%d bytes)\n", buffer_size);
229 if ((shmptr = mmap(0, buffer_size, PROT_READ | PROT_WRITE,
230 MAP_SHARED, fd, 0)) < 0)
231 sys_error("mmap failed", __LINE__);
232 close(fd);
233
234 /*
235 * Obtain an exclusive "write" lock on the shared memory data
236 * segment -- get lock now to insure the parent process gets
237 * first access to the segment.
238 */
239 lock_resource(WRITE);
240
241 /*
242 * Spawn all N child processes. Each child process will compute
243 * the checksum of the shared memory segment and will store
244 * the results in the other shared memory segment accessible
245 * by the parent.
246 */
247 printf("\n\tSpawning %d child processes ... \n", num_children);
248 for (i = 0; i < num_children; i++) {
249
250 if ((pid[i] = fork()) == (pid_t) 0) {
251 child(i, shmptr);
252 exit(0);
253 } else if (pid[i] < (pid_t) 0)
254 sys_error("fork failed", __LINE__);
255 }
256
257 /*
258 * Fill the "scratch" shared memory segment up with data and
259 * compute the segments checksum. Release "write" lock after
260 * completing so that the child processes may begin to read the
261 * data.
262 */
263 printf("\n\tParent: calculate shared memory segment checksum\n");
264 cksum = data = 0;
265
266 for (ptr = shmptr; ptr < (shmptr + buffer_size); ptr++) {
267 *ptr = (data++) % (UCHAR_MAX + 1);
268 cksum += *ptr;
269 }
270 printf("\t shared memory checksum %08lx\n", cksum);
271 unlock_resource(WRITE);
272
273 /*
274 * Wait for the child processes to compute the checksums and complete.
275 * After the processes complete, check their exit status to insure
276 * that they ran to completion and then verify the corresponding
277 * checksum.
278 */
279 for (i = 0; i < num_children; i++) {
280 waitpid(pid[i], &status, 0);
281
282 if (!WIFEXITED(status))
283 sys_error("child process terminated abnormally",
284 __LINE__);
285 if (cksum != *(checksum + (sizeof(unsigned long) * i))) {
286 printf("checksum [%d] = %08lx\n", i, checksum[i]);
287 error("checksums do not match", __LINE__);
288 }
289 }
290 printf("\n\tParent: children calculated segment successfully\n");
291
292 /*
293 * Program completed successfully, cleanup semaphores and exit.
294 */
295 delete_semaphores();
296 printf("\nsuccessful!\n");
297
298 return (0);
299 }
300
301 /*---------------------------------------------------------------------+
302 | child () |
303 | ==================================================================== |
304 | |
305 | Function: Waits for read access to the shared memory segment, |
306 | computes the shared memory segments checksum and releases |
307 | the read lock. Then stores the checksum. |
308 | |
309 | Updates: checksum - shared memory segment containing checksums |
310 | computed by child processes |
311 | |
312 +---------------------------------------------------------------------*/
child(int num,unsigned char * shmptr)313 static void child(int num, unsigned char *shmptr)
314 {
315 unsigned long cksum = 0; /* Shared memory regions checksum */
316 int i; /* Misc index */
317
318 /*
319 * Wait for a READ_COUNT lock on the shared memory segment, then
320 * compute the checksum and release the READ_COUNT lock.
321 */
322 lock_resource(READ_COUNT);
323 (*read_count)++;
324 if (*read_count == 1)
325 lock_resource(WRITE);
326 unlock_resource(READ_COUNT);
327
328 for (i = 0; i < buffer_size; i++)
329 cksum += *shmptr++;
330
331 lock_resource(READ_COUNT);
332 (*read_count)--;
333 if (*read_count == 0)
334 unlock_resource(WRITE);
335 unlock_resource(READ_COUNT);
336
337 /*
338 * Store the resulting checksum and print out a message
339 */
340 checksum[num] = cksum;
341 *(checksum + (sizeof(unsigned long) * num)) = cksum;
342 printf("\t\tchild (%02d): checksum %08lx\n", num,
343 *(checksum + (sizeof(unsigned long) * num)));
344 }
345
346 /*---------------------------------------------------------------------+
347 | create_semaphores () |
348 | ==================================================================== |
349 | |
350 | Function: Creates two semaphores: |
351 | |
352 | READ_COUNT: shared read semaphore |
353 | WRITE: exclusive write semaphore |
354 | |
355 | Updates: semid - system wide semaphore identifier |
356 | |
357 +---------------------------------------------------------------------*/
create_semaphores()358 static void create_semaphores()
359 {
360 int nsems = 2; /* Number of semaphores to create */
361
362 /*
363 * Create two system unique semaphores.
364 */
365 if ((semid = semget(IPC_PRIVATE, nsems, IPC_CREAT | 0666)) < 0)
366 sys_error("semget failed", __LINE__);
367
368 arg.val = 1;
369 if (semctl(semid, WRITE, SETVAL, arg) < 0)
370 sys_error("semctl (SETVAL) failed", __LINE__);
371 if (semctl(semid, READ_COUNT, SETVAL, arg) < 0)
372 sys_error("semctl (SETVAL) failed", __LINE__);
373 }
374
375 /*---------------------------------------------------------------------+
376 | delete_semaphores () |
377 | ==================================================================== |
378 | |
379 | Function: Deletes the two READ/WRITE semaphores. |
380 | |
381 +---------------------------------------------------------------------*/
delete_semaphores()382 static void delete_semaphores()
383 {
384 int nsems = 2;
385
386 /*
387 * Delete both READ_COUNT and WRITE semaphores.
388 */
389
390 arg.val = 0;
391 if (semctl(semid, nsems, IPC_RMID, arg) < 0)
392 sys_error("semctl (IPC_RMID) failed", __LINE__);
393 }
394
395 /*---------------------------------------------------------------------+
396 | lock_resource () |
397 | ==================================================================== |
398 | |
399 | Function: Obtains a READ/WRITE semaphore lock. |
400 | |
401 +---------------------------------------------------------------------*/
lock_resource(int semaphore)402 static void lock_resource(int semaphore)
403 {
404 struct sembuf buf;
405
406 buf.sem_op = -1; /* Obtain resource */
407 buf.sem_num = semaphore;
408 buf.sem_flg = 0;
409
410 if (semop(semid, &buf, 1) < 0)
411 sys_error("semop (LOCK) failed", __LINE__);
412 }
413
414 /*---------------------------------------------------------------------+
415 | unlock_resource () |
416 | ==================================================================== |
417 | |
418 | Function: Releases a READ/WRITE semaphore lock. |
419 | |
420 +---------------------------------------------------------------------*/
unlock_resource(int semaphore)421 static void unlock_resource(int semaphore)
422 {
423 struct sembuf buf;
424
425 buf.sem_op = 1; /* Release resource */
426 buf.sem_num = semaphore;
427 buf.sem_flg = 0;
428
429 if (semop(semid, &buf, 1) < 0)
430 sys_error("semop (UNLOCK) failed", __LINE__);
431 }
432
433 /*---------------------------------------------------------------------+
434 | parse_args () |
435 | ==================================================================== |
436 | |
437 | Function: Parse the command line arguments & initialize global |
438 | variables. |
439 | |
440 | Updates: (command line options) |
441 | |
442 | [-s] size: shared memory segment size |
443 | |
444 | [-c] num_children: number of child processes |
445 | |
446 +---------------------------------------------------------------------*/
parse_args(int argc,char ** argv)447 void parse_args(int argc, char **argv)
448 {
449 int i;
450 int errflag = 0;
451 char *program_name = *argv;
452 extern char *optarg; /* Command line option */
453
454 while ((i = getopt(argc, argv, "c:s:?")) != EOF) {
455 switch (i) {
456 case 'c':
457 num_children = atoi(optarg);
458 break;
459 case 's':
460 buffer_size = atoi(optarg);
461 break;
462 case '?':
463 errflag++;
464 break;
465 }
466 }
467 if (num_children >= MAX_CHILDREN) {
468 errflag++;
469 fprintf(stderr, "ERROR: num_children must be less than %d\n",
470 MAX_CHILDREN);
471 }
472
473 if (errflag) {
474 fprintf(stderr, USAGE, program_name);
475 exit(2);
476 }
477 }
478
479 /*---------------------------------------------------------------------+
480 | setup_handler () |
481 | ==================================================================== |
482 | |
483 | Function: Setup the signal handler for SIGINT. |
484 | |
485 +---------------------------------------------------------------------*/
setup_signal_handlers()486 void setup_signal_handlers()
487 {
488 struct sigaction invec;
489
490 invec.sa_handler = (void (*)(int))handler;
491 sigemptyset(&invec.sa_mask);
492 invec.sa_flags = 0;
493
494 if (sigaction(SIGINT, &invec, NULL) < 0)
495 sys_error("sigaction failed", __LINE__);
496
497 }
498
499 /*---------------------------------------------------------------------+
500 | handler () |
501 | ==================================================================== |
502 | |
503 | Function: Signal catching function for SIGINT signal |
504 | |
505 | o SIGINT: Parent process calls cleanup, child processes |
506 | simply exit |
507 | |
508 | o Other: Print message and abort program... |
509 | |
510 +---------------------------------------------------------------------*/
handler(int sig,int code,struct sigcontext * scp)511 void handler(int sig, int code, struct sigcontext *scp)
512 {
513 char msg[100]; /* Buffer for error message */
514
515 if (sig == SIGINT) {
516 if (getpid() == parent_pid) {
517
518 fprintf(stderr, "Received SIGINT -- cleaning up...\n");
519 fflush(stderr);
520
521 cleanup();
522 } else
523 exit(-1);
524 } else {
525 sprintf(msg, "Received an unexpected signal (%d)", sig);
526 error(msg, __LINE__);
527 }
528 }
529
530 /*---------------------------------------------------------------------+
531 | cleanup () |
532 | ==================================================================== |
533 | |
534 | Function: Closes all of the pipes, kills all of the child |
535 | processes and exits the program... |
536 | |
537 +---------------------------------------------------------------------*/
cleanup()538 void cleanup()
539 {
540 int i;
541
542 if (getpid() == parent_pid) {
543 delete_semaphores();
544 for (i = 0; i < num_children; i++) {
545 if (pid[i] > (pid_t) 0 && kill(pid[i], SIGKILL) < 0)
546 sys_error("signal failed", __LINE__);
547 }
548 }
549
550 exit(-1);
551 }
552
553 /*---------------------------------------------------------------------+
554 | sys_error () |
555 | ==================================================================== |
556 | |
557 | Function: Creates system error message and calls error () |
558 | |
559 +---------------------------------------------------------------------*/
sys_error(const char * msg,int line)560 void sys_error(const char *msg, int line)
561 {
562 char syserr_msg[256];
563
564 sprintf(syserr_msg, "%s: %s\n", msg, strerror(errno));
565 error(syserr_msg, line);
566 }
567
568 /*---------------------------------------------------------------------+
569 | error () |
570 | ==================================================================== |
571 | |
572 | Function: Prints out message and exits... |
573 | |
574 +---------------------------------------------------------------------*/
error(const char * msg,int line)575 void error(const char *msg, int line)
576 {
577 fprintf(stderr, "ERROR [line: %d] %s\n", line, msg);
578 cleanup();
579 }
580