| /******************************************************************************/ |
| /* */ |
| /* Copyright (c) International Business Machines Corp., 2001 */ |
| /* */ |
| /* This program is free software; you can redistribute it and/or modify */ |
| /* it under the terms of the GNU General Public License as published by */ |
| /* the Free Software Foundation; either version 2 of the License, or */ |
| /* (at your option) any later version. */ |
| /* */ |
| /* This program is distributed in the hope that it will be useful, */ |
| /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ |
| /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */ |
| /* the GNU General Public License for more details. */ |
| /* */ |
| /* You should have received a copy of the GNU General Public License */ |
| /* along with this program; if not, write to the Free Software */ |
| /* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ |
| /* */ |
| /******************************************************************************/ |
| |
| /******************************************************************************/ |
| /* */ |
| /* History: Nov - 21 - 2001 Created - Manoj Iyer, IBM Austin TX. */ |
| /* email:manjo@austin.ibm.com */ |
| /* */ |
| /* Nov - 26 - 2001 Modified - Manoj Iyer, IBM Austin Tx. */ |
| /* - Added function rm_shared_mem. */ |
| /* */ |
| /* Dec - 03 - 2001 Modified - Manoj Iyer, IBM Austin Tx. */ |
| /* - Added code to spawn threads. */ |
| /* - Removed dead code. */ |
| /* - Checked in the initial version to CVS */ |
| /* */ |
| /* Feb - 27 - 2001 Modified - Manoj Iyer, IBM Austin TX. */ |
| /* - removed compiler warnings. */ |
| /* - removed compiler errors. */ |
| /* */ |
| /* File: shm_test.c */ |
| /* */ |
| /* Description: This program is designed to stress the Memory management sub -*/ |
| /* system of Linux. This program will spawn multiple pairs of */ |
| /* reader and writer threads. One thread will create the shared */ |
| /* segment of random size and write to this memory, the other */ |
| /* pair will read from this memory. */ |
| /* */ |
| /******************************************************************************/ |
| #include <pthread.h> /* required by pthread functions */ |
| #include <stdio.h> /* required by fprintf() */ |
| #include <stdlib.h> /* required by exit(), atoi() */ |
| #include <string.h> /* required by strncpy() */ |
| #include <unistd.h> /* required by getopt(), mmap() */ |
| #include <sys/types.h> /* required by open(), shmat(), shmdt() */ |
| #include <sys/stat.h> /* required by open() */ |
| #include <sys/ipc.h> /* required by shmat() shmdt(), shmctl() */ |
| #include <sys/shm.h> /* required by shmat() shmdt(), shmctl() */ |
| #include <sys/mman.h> /* required by mmap() */ |
| #include <fcntl.h> /* required by open() */ |
| #include <stdint.h> /* required by uintptr_t */ |
| |
| void noprintf(char* string, ...) { |
| } |
| |
| #ifdef DEBUG |
| #define dprt printf |
| #else |
| #define dprt noprintf |
| #endif |
| |
| #define PTHREAD_EXIT(val) do {\ |
| exit_val = val; \ |
| dprt("pid[%d]: exiting with %d\n", getpid(),exit_val); \ |
| pthread_exit((void *)(uintptr_t)exit_val); \ |
| } while (0) |
| |
| #define OPT_MISSING(prog, opt) do{\ |
| fprintf(stderr, "%s: option -%c ", prog, opt); \ |
| fprintf(stderr, "requires an argument\n"); \ |
| usage(prog); \ |
| } while (0) |
| |
| #define MAXT 30 /* default number of threads to create. */ |
| #define MAXR 1000 /* default number of repatetions to execute */ |
| #define WRITER 0 /* cause thread function to shmat and write */ |
| #define READER 1 /* cause thread function to shmat and read */ |
| |
| /******************************************************************************/ |
| /* */ |
| /* Function: usage */ |
| /* */ |
| /* Description: Print the usage message. */ |
| /* */ |
| /* Return: exits with -1 */ |
| /* */ |
| /******************************************************************************/ |
| static void |
| usage(char *progname) /* name of this program */{ |
| fprintf(stderr, |
| "Usage: %s -d NUMDIR -f NUMFILES -h -t NUMTHRD\n" |
| "\t -h Help!\n" |
| "\t -l Number of repatetions to execute: Default: 1000\n" |
| "\t -t Number of threads to generate: Default: 30\n", |
| progname); |
| exit(-1); |
| } |
| |
| /******************************************************************************/ |
| /* */ |
| /* Function: rm_shared_mem */ |
| /* */ |
| /* Description: This function removes the shared segments that were created */ |
| /* This function is called when shmat fails or logical end of */ |
| /* the while loop is reached in shmat_rd_wr function. */ |
| /* */ |
| /* Input: shm_id - id of the shared memory segment to be removed */ |
| /* shm_addr - address of the shared memory segment to be removed */ |
| /* cmd - remove id only or remove id and detach?? */ |
| /* 0 - remove id dont detach segment. */ |
| /* 1 - remove id and detach segment. */ |
| /* */ |
| /* Output: NONE. */ |
| /* */ |
| /* Return: exits with -1 on error, 0 on success */ |
| /* */ |
| /******************************************************************************/ |
| static int |
| rm_shared_mem(key_t shm_id, /* id of shared memory segment to be removed */ |
| char *shm_addr, /* address of shared mem seg to be removed */ |
| int cmd) /* remove id only or remove id and detach seg */ |
| { |
| struct shmid *shmbuf=NULL; /* info about the segment pointed by shmkey */ |
| |
| dprt("pid[%d]: rm_shared_mem(): shm_id = %d shm_addr = %#x cmd = %d\n", |
| getpid(), shm_id, shm_addr, cmd); |
| if (shmctl(shm_id, IPC_RMID, (struct shmid_ds *)shmbuf) == -1) |
| { |
| dprt("pid[%d]: rm_shared_mem(): shmctl unable to remove shm_id[%d]\n", |
| getpid(), shm_id); |
| perror("rm_shared_mem(): shmctl()"); |
| return -1; |
| } |
| |
| if (cmd) |
| { |
| if (shmdt((void *)shm_addr) == -1) |
| { |
| dprt("pid[%d]:rm_shared_mem(): shmdt unable to detach addr = %#x\n", |
| getpid(), shm_addr); |
| perror("rm_shared_mem(): shmdt()"); |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| /******************************************************************************/ |
| /* */ |
| /* Function: shmat_rd_wr */ |
| /* */ |
| /* Description: This function repeatedly attaches and detaches the memory */ |
| /* The size of the file is a multiple of page size. */ |
| /* The function acts as either reader or writer thread depending */ |
| /* on arg[3]. The reader and writer thread use the same key so */ |
| /* they get access to the same shared memory segment. */ |
| /* */ |
| /* Input: The argument pointer contains the following. */ |
| /* arg[0] - number of repatetions of the above operation */ |
| /* arg[1] - shared memory key. */ |
| /* arg[2] - size of the memory that is to be attached. */ |
| /* arg[3] - reader or writer. */ |
| /* */ |
| /* Return: exits with -1 on error, 0 on success */ |
| /* */ |
| /******************************************************************************/ |
| static void * |
| shmat_rd_wr(void *args) /* arguments to the thread function */ |
| { |
| int shmndx = 0; /* index to the number of attach and detach */ |
| int index = 0; /* index to the number of blocks touched */ |
| int reader = 0; /* this thread is a reader thread if set to 1 */ |
| key_t shm_id = 0; /* shared memory id */ |
| long *locargs = /* local pointer to arguments */ |
| (long *)args; |
| volatile int exit_val = 0; /* exit value of the pthread */ |
| char *read_from_mem;/* ptr to touch each (4096) block in memory */ |
| char *write_to_mem; /* ptr to touch each (4096) block in memory */ |
| char *shmat_addr; /* address of the attached memory */ |
| char buff; /* temporary buffer */ |
| |
| reader = (int)locargs[3]; |
| while (shmndx++ < (int)locargs[0]) |
| { |
| dprt("pid[%d]: shmat_rd_wr(): locargs[1] = %#x\n", |
| getpid(), (int)locargs[1]); |
| |
| /* get shared memory id */ |
| if ((shm_id = shmget((int)locargs[1], (int)locargs[2], IPC_CREAT|0666)) |
| == -1) |
| { |
| dprt("pid[%d]: shmat_rd_wr(): shmget failed\n", getpid()); |
| perror("do_shmat_shmadt(): shmget()"); |
| PTHREAD_EXIT(-1); |
| } |
| |
| fprintf(stdout, "pid[%d]: shmat_rd_wr(): shmget():" |
| "success got segment id %d\n", |
| getpid(), shm_id); |
| |
| /* get shared memory segment */ |
| if ((shmat_addr = (char *)shmat(shm_id, NULL, 0)) == (void *)-1) |
| { |
| rm_shared_mem(shm_id, shmat_addr, 0); |
| fprintf(stderr, "pid[%d]: do_shmat_shmadt(): shmat_addr = %#lx\n", |
| getpid(), (long)shmat_addr); |
| perror("do_shmat_shmadt(): shmat()"); |
| PTHREAD_EXIT(-1); |
| } |
| dprt("pid[%d]: do_shmat_shmadt(): content of memory shmat_addr = %s\n", |
| getpid(), shmat_addr); |
| |
| fprintf(stdout, "pid[%d]: do_shmat_shmadt(): got shmat address = %#lx\n", |
| getpid(), (long)shmat_addr); |
| |
| if (!reader) |
| { |
| /* write character 'Y' to that memory area */ |
| index = 0; |
| write_to_mem = shmat_addr; |
| while (index < (int)locargs[2]) |
| { |
| dprt("pid[%d]: do_shmat_shmatd(): write_to_mem = %#x\n", |
| getpid(), write_to_mem); |
| *write_to_mem = 'Y'; |
| index++; |
| write_to_mem++; |
| sched_yield(); |
| } |
| } |
| else |
| { |
| /* read from the memory area */ |
| index = 0; |
| read_from_mem = shmat_addr; |
| while (index < (int)locargs[2]) |
| { |
| buff = *read_from_mem; |
| index++; |
| read_from_mem++; |
| sched_yield(); |
| } |
| } |
| |
| sched_yield(); |
| |
| /* remove the shared memory */ |
| if (rm_shared_mem(shm_id, shmat_addr, 1) == -1) |
| { |
| fprintf(stderr, |
| "pid[%d]: do_shmat_shmatd(): rm_shared_mem(): faild to rm id\n", |
| getpid()); |
| PTHREAD_EXIT(-1); |
| } |
| } |
| |
| PTHREAD_EXIT(0); |
| } |
| |
| /******************************************************************************/ |
| /* */ |
| /* Function: main */ |
| /* */ |
| /* Description: This is the entry point to the program. This function will */ |
| /* parse the input arguments and set the values accordingly. If */ |
| /* no arguments (or desired) are provided default values are used*/ |
| /* refer the usage function for the arguments that this program */ |
| /* takes. It also creates the threads which do most of the dirty */ |
| /* work. If the threads exits with a value '0' the program exits */ |
| /* with success '0' else it exits with failure '-1'. */ |
| /* */ |
| /* Return: -1 on failure */ |
| /* 0 on success */ |
| /* */ |
| /******************************************************************************/ |
| int |
| main(int argc, /* number of input parameters */ |
| char **argv) /* pointer to the command line arguments. */ |
| { |
| int c; /* command line options */ |
| int num_thrd = MAXT;/* number of threads to create */ |
| int num_reps = MAXR;/* number of repatitions the test is run */ |
| int thrd_ndx; /* index into the array of thread ids */ |
| int th_status; /* exit status of LWP's */ |
| int map_size; /* size of the file mapped. */ |
| int shmkey = 1969;/* key used to generate shmid by shmget() */ |
| pthread_t thrdid[30]; /* maxinum of 30 threads allowed */ |
| long chld_args[4]; /* arguments to the thread function */ |
| char *map_address=NULL; |
| /* address in memory of the mapped file */ |
| extern int optopt; /* options to the program */ |
| |
| while ((c = getopt(argc, argv, "hl:t:")) != -1) |
| { |
| switch(c) |
| { |
| case 'h': |
| usage(argv[0]); |
| break; |
| case 'l': /* how many repetitions of the test to exec */ |
| if ((num_reps = atoi(optarg)) == 0) |
| OPT_MISSING(argv[0], optopt); |
| else |
| if (num_reps < 0) |
| { |
| fprintf(stdout, |
| "WARNING: bad argument. Using default\n"); |
| num_reps = MAXR; |
| } |
| break; |
| case 't': |
| if ((num_thrd = atoi(optarg)) == 0) |
| OPT_MISSING(argv[0], optopt); |
| else |
| if (num_thrd < 0) |
| { |
| fprintf(stdout, |
| "WARNING: bad argument. Using default\n"); |
| num_thrd = MAXT; |
| } |
| break; |
| default : |
| usage(argv[0]); |
| break; |
| } |
| } |
| |
| chld_args[0] = num_reps; |
| |
| for (thrd_ndx = 0; thrd_ndx < num_thrd; thrd_ndx+=2) |
| { |
| srand(time(NULL)%100); |
| map_size = (1 + (int)(1000.0*rand()/(RAND_MAX+1.0))) * 4096; |
| |
| chld_args[1] = shmkey++; |
| chld_args[2] = map_size; |
| |
| dprt("main(): thrd_ndx = %d map_address = %#x map_size = %d\n", |
| thrd_ndx, map_address, map_size); |
| |
| chld_args[3] = WRITER; |
| |
| if (pthread_create(&thrdid[thrd_ndx], NULL, shmat_rd_wr, chld_args)) |
| { |
| perror("shmat_rd_wr(): pthread_create()"); |
| exit(-1); |
| } |
| |
| chld_args[3] = READER; |
| |
| if (pthread_create(&thrdid[thrd_ndx+1], NULL, shmat_rd_wr, chld_args)) |
| { |
| perror("shmat_rd_wr(): pthread_create()"); |
| exit(-1); |
| } |
| } |
| |
| sync(); |
| |
| for (thrd_ndx = 0; thrd_ndx < num_thrd; thrd_ndx++) |
| { |
| if (pthread_join(thrdid[thrd_ndx], (void *)&th_status) != 0) |
| { |
| perror("shmat_rd_wr(): pthread_join()"); |
| exit(-1); |
| } |
| else |
| { |
| dprt("WE ARE HERE %d\n", __LINE__); |
| if (th_status == -1) |
| { |
| fprintf(stderr, |
| "thread [%ld] - process exited with errors\n", |
| (long)thrdid[thrd_ndx]); |
| exit(-1); |
| } |
| } |
| } |
| exit(0); |
| } |