1 /******************************************************************************/ 2 /* */ 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 21 /******************************************************************************/ 22 /* */ 23 /* History: Nov - 21 - 2001 Created - Manoj Iyer, IBM Austin TX. */ 24 /* email:manjo (at) austin.ibm.com */ 25 /* */ 26 /* Nov - 26 - 2001 Modified - Manoj Iyer, IBM Austin Tx. */ 27 /* - Added function rm_shared_mem. */ 28 /* */ 29 /* Dec - 03 - 2001 Modified - Manoj Iyer, IBM Austin Tx. */ 30 /* - Added code to spawn threads. */ 31 /* - Removed dead code. */ 32 /* - Checked in the initial version to CVS */ 33 /* */ 34 /* Feb - 27 - 2001 Modified - Manoj Iyer, IBM Austin TX. */ 35 /* - removed compiler warnings. */ 36 /* - removed compiler errors. */ 37 /* */ 38 /* File: shm_test.c */ 39 /* */ 40 /* Description: This program is designed to stress the Memory management sub -*/ 41 /* system of Linux. This program will spawn multiple pairs of */ 42 /* reader and writer threads. One thread will create the shared */ 43 /* segment of random size and write to this memory, the other */ 44 /* pair will read from this memory. */ 45 /* */ 46 /******************************************************************************/ 47 #include <pthread.h> /* required by pthread functions */ 48 #include <stdio.h> /* required by fprintf() */ 49 #include <stdlib.h> /* required by exit(), atoi() */ 50 #include <string.h> /* required by strncpy() */ 51 #include <unistd.h> /* required by getopt(), mmap() */ 52 #include <sys/types.h> /* required by open(), shmat(), shmdt() */ 53 #include <sys/stat.h> /* required by open() */ 54 #include <sys/ipc.h> /* required by shmat() shmdt(), shmctl() */ 55 #include <sys/shm.h> /* required by shmat() shmdt(), shmctl() */ 56 #include <sys/mman.h> /* required by mmap() */ 57 #include <fcntl.h> /* required by open() */ 58 #include <stdint.h> /* required by uintptr_t */ 59 60 void noprintf(char *string, ...) 61 { 62 } 63 64 #ifdef DEBUG 65 #define dprt printf 66 #else 67 #define dprt noprintf 68 #endif 69 70 #define PTHREAD_EXIT(val) do {\ 71 exit_val = val; \ 72 dprt("pid[%d]: exiting with %d\n", getpid(),exit_val); \ 73 pthread_exit((void *)(uintptr_t)exit_val); \ 74 } while (0) 75 76 #define OPT_MISSING(prog, opt) do{\ 77 fprintf(stderr, "%s: option -%c ", prog, opt); \ 78 fprintf(stderr, "requires an argument\n"); \ 79 usage(prog); \ 80 } while (0) 81 82 #define MAXT 30 /* default number of threads to create. */ 83 #define MAXR 1000 /* default number of repatetions to execute */ 84 #define WRITER 0 /* cause thread function to shmat and write */ 85 #define READER 1 /* cause thread function to shmat and read */ 86 87 /******************************************************************************/ 88 /* */ 89 /* Function: usage */ 90 /* */ 91 /* Description: Print the usage message. */ 92 /* */ 93 /* Return: exits with -1 */ 94 /* */ 95 /******************************************************************************/ 96 static void usage(char *progname) 97 { /* name of this program */ 98 fprintf(stderr, 99 "Usage: %s -d NUMDIR -f NUMFILES -h -t NUMTHRD\n" 100 "\t -h Help!\n" 101 "\t -l Number of repatetions to execute: Default: 1000\n" 102 "\t -t Number of threads to generate: Default: 30\n", 103 progname); 104 exit(-1); 105 } 106 107 /******************************************************************************/ 108 /* */ 109 /* Function: rm_shared_mem */ 110 /* */ 111 /* Description: This function removes the shared segments that were created */ 112 /* This function is called when shmat fails or logical end of */ 113 /* the while loop is reached in shmat_rd_wr function. */ 114 /* */ 115 /* Input: shm_id - id of the shared memory segment to be removed */ 116 /* shm_addr - address of the shared memory segment to be removed */ 117 /* cmd - remove id only or remove id and detach?? */ 118 /* 0 - remove id dont detach segment. */ 119 /* 1 - remove id and detach segment. */ 120 /* */ 121 /* Output: NONE. */ 122 /* */ 123 /* Return: exits with -1 on error, 0 on success */ 124 /* */ 125 /******************************************************************************/ 126 static int rm_shared_mem(key_t shm_id, /* id of shared memory segment to be removed */ 127 char *shm_addr, /* address of shared mem seg to be removed */ 128 int cmd) 129 { /* remove id only or remove id and detach seg */ 130 struct shmid *shmbuf = NULL; /* info about the segment pointed by shmkey */ 131 132 dprt("pid[%d]: rm_shared_mem(): shm_id = %d shm_addr = %#x cmd = %d\n", 133 getpid(), shm_id, shm_addr, cmd); 134 if (shmctl(shm_id, IPC_RMID, (struct shmid_ds *)shmbuf) == -1) { 135 dprt("pid[%d]: rm_shared_mem(): shmctl unable to remove shm_id[%d]\n", getpid(), shm_id); 136 perror("rm_shared_mem(): shmctl()"); 137 return -1; 138 } 139 140 if (cmd) { 141 if (shmdt((void *)shm_addr) == -1) { 142 dprt("pid[%d]:rm_shared_mem(): shmdt unable to detach addr = %#x\n", getpid(), shm_addr); 143 perror("rm_shared_mem(): shmdt()"); 144 return -1; 145 } 146 } 147 return 0; 148 } 149 150 /******************************************************************************/ 151 /* */ 152 /* Function: shmat_rd_wr */ 153 /* */ 154 /* Description: This function repeatedly attaches and detaches the memory */ 155 /* The size of the file is a multiple of page size. */ 156 /* The function acts as either reader or writer thread depending */ 157 /* on arg[3]. The reader and writer thread use the same key so */ 158 /* they get access to the same shared memory segment. */ 159 /* */ 160 /* Input: The argument pointer contains the following. */ 161 /* arg[0] - number of repatetions of the above operation */ 162 /* arg[1] - shared memory key. */ 163 /* arg[2] - size of the memory that is to be attached. */ 164 /* arg[3] - reader or writer. */ 165 /* */ 166 /* Return: exits with -1 on error, 0 on success */ 167 /* */ 168 /******************************************************************************/ 169 static void *shmat_rd_wr(void *args) 170 { /* arguments to the thread function */ 171 int shmndx = 0; /* index to the number of attach and detach */ 172 int index = 0; /* index to the number of blocks touched */ 173 int reader = 0; /* this thread is a reader thread if set to 1 */ 174 key_t shm_id = 0; /* shared memory id */ 175 long *locargs = /* local pointer to arguments */ 176 (long *)args; 177 volatile int exit_val = 0; /* exit value of the pthread */ 178 char *read_from_mem; /* ptr to touch each (4096) block in memory */ 179 char *write_to_mem; /* ptr to touch each (4096) block in memory */ 180 char *shmat_addr; /* address of the attached memory */ 181 char buff; /* temporary buffer */ 182 183 reader = (int)locargs[3]; 184 while (shmndx++ < (int)locargs[0]) { 185 dprt("pid[%d]: shmat_rd_wr(): locargs[1] = %#x\n", 186 getpid(), (int)locargs[1]); 187 188 /* get shared memory id */ 189 if ((shm_id = 190 shmget((int)locargs[1], (int)locargs[2], IPC_CREAT | 0666)) 191 == -1) { 192 dprt("pid[%d]: shmat_rd_wr(): shmget failed\n", 193 getpid()); 194 perror("do_shmat_shmadt(): shmget()"); 195 PTHREAD_EXIT(-1); 196 } 197 198 fprintf(stdout, "pid[%d]: shmat_rd_wr(): shmget():" 199 "success got segment id %d\n", getpid(), shm_id); 200 201 /* get shared memory segment */ 202 if ((shmat_addr = shmat(shm_id, NULL, 0)) == (void *)-1) { 203 rm_shared_mem(shm_id, shmat_addr, 0); 204 fprintf(stderr, 205 "pid[%d]: do_shmat_shmadt(): shmat_addr = %#lx\n", 206 getpid(), (long)shmat_addr); 207 perror("do_shmat_shmadt(): shmat()"); 208 PTHREAD_EXIT(-1); 209 } 210 dprt("pid[%d]: do_shmat_shmadt(): content of memory shmat_addr = %s\n", getpid(), shmat_addr); 211 212 fprintf(stdout, 213 "pid[%d]: do_shmat_shmadt(): got shmat address = %#lx\n", 214 getpid(), (long)shmat_addr); 215 216 if (!reader) { 217 /* write character 'Y' to that memory area */ 218 index = 0; 219 write_to_mem = shmat_addr; 220 while (index < (int)locargs[2]) { 221 dprt("pid[%d]: do_shmat_shmatd(): write_to_mem = %#x\n", getpid(), write_to_mem); 222 *write_to_mem = 'Y'; 223 index++; 224 write_to_mem++; 225 sched_yield(); 226 } 227 } else { 228 /* read from the memory area */ 229 index = 0; 230 read_from_mem = shmat_addr; 231 while (index < (int)locargs[2]) { 232 buff = *read_from_mem; 233 index++; 234 read_from_mem++; 235 sched_yield(); 236 } 237 } 238 239 sched_yield(); 240 241 /* remove the shared memory */ 242 if (rm_shared_mem(shm_id, shmat_addr, 1) == -1) { 243 fprintf(stderr, 244 "pid[%d]: do_shmat_shmatd(): rm_shared_mem(): faild to rm id\n", 245 getpid()); 246 PTHREAD_EXIT(-1); 247 } 248 } 249 250 PTHREAD_EXIT(0); 251 } 252 253 /******************************************************************************/ 254 /* */ 255 /* Function: main */ 256 /* */ 257 /* Description: This is the entry point to the program. This function will */ 258 /* parse the input arguments and set the values accordingly. If */ 259 /* no arguments (or desired) are provided default values are used*/ 260 /* refer the usage function for the arguments that this program */ 261 /* takes. It also creates the threads which do most of the dirty */ 262 /* work. If the threads exits with a value '0' the program exits */ 263 /* with success '0' else it exits with failure '-1'. */ 264 /* */ 265 /* Return: -1 on failure */ 266 /* 0 on success */ 267 /* */ 268 /******************************************************************************/ 269 int main(int argc, /* number of input parameters */ 270 char **argv) 271 { /* pointer to the command line arguments. */ 272 int c; /* command line options */ 273 int num_thrd = MAXT; /* number of threads to create */ 274 int num_reps = MAXR; /* number of repatitions the test is run */ 275 int thrd_ndx; /* index into the array of thread ids */ 276 void *th_status; /* exit status of LWP's */ 277 int map_size; /* size of the file mapped. */ 278 int shmkey = 1969; /* key used to generate shmid by shmget() */ 279 pthread_t thrdid[30]; /* maxinum of 30 threads allowed */ 280 long chld_args[4]; /* arguments to the thread function */ 281 char *map_address = NULL; 282 /* address in memory of the mapped file */ 283 extern int optopt; /* options to the program */ 284 285 while ((c = getopt(argc, argv, "hl:t:")) != -1) { 286 switch (c) { 287 case 'h': 288 usage(argv[0]); 289 break; 290 case 'l': /* how many repetitions of the test to exec */ 291 if ((num_reps = atoi(optarg)) == 0) 292 OPT_MISSING(argv[0], optopt); 293 else if (num_reps < 0) { 294 fprintf(stdout, 295 "WARNING: bad argument. Using default\n"); 296 num_reps = MAXR; 297 } 298 break; 299 case 't': 300 if ((num_thrd = atoi(optarg)) == 0) 301 OPT_MISSING(argv[0], optopt); 302 else if (num_thrd < 0) { 303 fprintf(stdout, 304 "WARNING: bad argument. Using default\n"); 305 num_thrd = MAXT; 306 } 307 break; 308 default: 309 usage(argv[0]); 310 break; 311 } 312 } 313 314 chld_args[0] = num_reps; 315 316 for (thrd_ndx = 0; thrd_ndx < num_thrd; thrd_ndx += 2) { 317 srand(time(NULL) % 100); 318 map_size = 319 (1 + (int)(1000.0 * rand() / (RAND_MAX + 1.0))) * 4096; 320 321 chld_args[1] = shmkey++; 322 chld_args[2] = map_size; 323 324 dprt("main(): thrd_ndx = %d map_address = %#x map_size = %d\n", 325 thrd_ndx, map_address, map_size); 326 327 chld_args[3] = WRITER; 328 329 if (pthread_create 330 (&thrdid[thrd_ndx], NULL, shmat_rd_wr, chld_args)) { 331 perror("shmat_rd_wr(): pthread_create()"); 332 exit(-1); 333 } 334 335 chld_args[3] = READER; 336 337 if (pthread_create 338 (&thrdid[thrd_ndx + 1], NULL, shmat_rd_wr, chld_args)) { 339 perror("shmat_rd_wr(): pthread_create()"); 340 exit(-1); 341 } 342 } 343 344 sync(); 345 346 for (thrd_ndx = 0; thrd_ndx < num_thrd; thrd_ndx++) { 347 if (pthread_join(thrdid[thrd_ndx], &th_status) != 0) { 348 perror("shmat_rd_wr(): pthread_join()"); 349 exit(-1); 350 } else { 351 dprt("WE ARE HERE %d\n", __LINE__); 352 if (th_status == (void *)-1) { 353 fprintf(stderr, 354 "thread [%ld] - process exited with errors\n", 355 (long)thrdid[thrd_ndx]); 356 exit(-1); 357 } 358 } 359 } 360 exit(0); 361 } 362