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