Home | History | Annotate | Download | only in ipc_stress
      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 +---------------------------------------------------------------------*/
    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 +---------------------------------------------------------------------*/
    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 +---------------------------------------------------------------------*/
    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 +---------------------------------------------------------------------*/
    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 +---------------------------------------------------------------------*/
    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 +---------------------------------------------------------------------*/
    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 +---------------------------------------------------------------------*/
    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 +---------------------------------------------------------------------*/
    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 +---------------------------------------------------------------------*/
    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 +---------------------------------------------------------------------*/
    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 +---------------------------------------------------------------------*/
    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 +---------------------------------------------------------------------*/
    575 void error(const char *msg, int line)
    576 {
    577 	fprintf(stderr, "ERROR [line: %d] %s\n", line, msg);
    578 	cleanup();
    579 }
    580