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