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_02 |
21 | ==================================================================== |
22 | |
23 | Description: Verify anonymous shared memory mapping, with exclusive |
24 | writes and shared reads |
25 | |
26 | Algorithm: o Obtain two shared memory segments using |
27 | mmap (MAP_ANON), one for random data created by the |
28 | the parent, and another for the childs checksums |
29 | o Spawn N child processes |
30 | o Parent: |
31 | - obtain write lock on data |
32 | - fill shared memory segment with data |
33 | - compute data checksum |
34 | - release lock |
35 | o Child: |
36 | - obtain read lock on data |
37 | - compute data checksum |
38 | - release lock |
39 | - store checksum in other shared memory segment |
40 | o Parent: |
41 | - wait for child proceeses to complete |
42 | - compare child's checksum (from shared memory) |
43 | with that of the parent |
44 | |
45 | System calls: The following system calls are tested: |
46 | |
47 | mmap () - Maps a file-system object into memory |
48 | |
49 | Usage: shmem_test_02 [-c num_children] [-s shmem_size] |
50 | |
51 | To compile: cc -o shmem_test_02 shmem_test_02.c -lbsd |
52 | |
53 |**********************************************************************|
54 |******!!!!!! LINUX PORT - must be using kernel >= 2.4.3 !!!!!!*******|
55 |**********************************************************************|
56 | |
57 | Last update: Ver. 1.2, 2/8/94 00:08:39 |
58 | |
59 | Change Activity |
60 | |
61 | Version Date Name Reason |
62 | 0.1 111593 DJK Initial version for AIX 4.1 |
63 | 1.2 020794 DJK Moved to "prod" directory |
64 | 1.3 060601 VHM "ported" to linux |
65 | |
66 +---------------------------------------------------------------------*/
67
68 #include <errno.h>
69 #include <limits.h>
70 #include <unistd.h>
71 #include <stdio.h>
72 #include <stdlib.h>
73 #include <string.h>
74 #include <fcntl.h>
75 #include <sys/file.h>
76 #include <sys/mman.h>
77 #include <sys/types.h>
78 #include <sys/signal.h>
79 #include <sys/wait.h>
80
81 /* Defines
82 *
83 * USAGE: usage statement
84 */
85 #define LOCK_FILE "lockfile"
86 #define NLOOPS 20
87 #define SHMEM_MODE (SHM_R | SHM_W)
88 #define USAGE "\nUsage: %s [-c num_children] [-s shmem_size]\n\n" \
89 "\t-c num_children number of child processes to spawn\n" \
90 "\t-s shmem_size size of shared memory segment (bytes)\n" \
91 "\t (must be less than 256MB!)\n\n"
92
93 #define GOTHERE printf("got to line %d\n", __LINE__);
94
95 /*
96 * Function prototypes
97 *
98 * parse_args (): Parse command line arguments
99 * sys_error (): System error message function
100 * error (): Error message function
101 */
102
103 struct data {
104 int who;
105 int value;
106 };
107
108 void cleanup();
109 void handler(int, int, struct sigcontext *);
110 void setup_signal_handlers();
111 static void child(int, unsigned char *);
112 static int create_lock_file(char *);
113 static void unlock_file(int);
114 static void write_lock(int);
115 static void read_lock(int);
116 void parse_args(int, char **);
117 void sys_error(const char *, int);
118 void error(const char *, int);
119 void loop(int, char);
120 void tell(int, char *);
121
122 enum { READ, WRITE }; /* Pipe read & write end indices */
123 enum { PARENT, CHILD }; /* Pipe read & write end indices */
124
125 #define MAX_CHILDREN 400
126 #define BUFFER_SIZE 50
127 #define DEFAULT_NUM_CHILDREN 2
128 #define DEFAULT_SHMEM_SIZE 100000
129
130 int num_children = DEFAULT_NUM_CHILDREN;
131 int buffer_size = DEFAULT_SHMEM_SIZE;
132
133 unsigned long *checksum; /* Shared memory segment address */
134 int lockfd;
135 pid_t parent_pid;
136 pid_t pid[MAX_CHILDREN];
137
138 /*---------------------------------------------------------------------+
139 | main |
140 | ==================================================================== |
141 | |
142 | Function: Main program (see prolog for more details) |
143 | |
144 | Returns: (0) Successful completion |
145 | (-1) Error occurred |
146 | |
147 +---------------------------------------------------------------------*/
main(int argc,char ** argv)148 int main(int argc, char **argv)
149 {
150 unsigned char *shmptr, /* Shared memory segment address */
151 value = 0; /* Value written into shared memory segment */
152 int i;
153 unsigned char *ptr;
154 int status;
155 int shmem_size;
156 unsigned long cksum;
157
158 lockfd = create_lock_file(LOCK_FILE);
159 setup_signal_handlers();
160
161 /*
162 * Parse command line arguments and print out program header
163 */
164 parse_args(argc, argv);
165 printf("%s: IPC Shared Memory TestSuite program\n", *argv);
166
167 parent_pid = getpid();
168 for (i = 0; i < num_children; i++)
169 pid[i] = (pid_t) 0;
170
171 /*
172 * Get chunk of shared memory for storing num_children checksum
173 */
174 shmem_size = sizeof(unsigned long) * num_children;
175 if ((checksum = (unsigned long *)
176 mmap(0, shmem_size, PROT_READ | PROT_WRITE,
177 MAP_ANON | MAP_SHARED, -1, 0)) == MAP_FAILED)
178 sys_error("mmap failed", __LINE__);
179
180 for (i = 0; i < num_children; i++)
181 *(checksum + (sizeof(unsigned long) * i)) = 0;
182
183 /*
184 * Get chunk of memory for writing scratch data
185 */
186 printf("\n\tGet shared memory segment (%d bytes)\n", buffer_size);
187 if ((shmptr = mmap(0, buffer_size, PROT_READ | PROT_WRITE,
188 MAP_ANON | MAP_SHARED, -1, 0)) == MAP_FAILED)
189 sys_error("mmap failed", __LINE__);
190
191 /*
192 * Parent:
193 *
194 * Fill buffer with data..
195 */
196 cksum = value = 0;
197
198 printf("\n\tParent: calculate shared memory segment checksum\n");
199 write_lock(lockfd);
200 for (ptr = shmptr; ptr < (shmptr + buffer_size); ptr++) {
201 *ptr = (value++) % (UCHAR_MAX + 1);
202 cksum += *ptr;
203 }
204 unlock_file(lockfd);
205 printf("\t shared memory checksum %08lx\n", cksum);
206
207 printf("\n\tSpawning %d child processes ... \n", num_children);
208 for (i = 0; i < num_children; i++) {
209
210 if ((pid[i] = fork()) == (pid_t) 0) {
211 child(i, shmptr);
212 exit(0);
213 } else if (pid[i] < (pid_t) 0)
214 sys_error("fork failed", __LINE__);
215 }
216
217 /*
218 * Wait for child processes to compute checksum and complete...
219 */
220 for (i = 0; i < num_children; i++) {
221 waitpid(pid[i], &status, 0);
222
223 if (!WIFEXITED(status))
224 sys_error("child process terminated abnormally",
225 __LINE__);
226 if (cksum != *(checksum + (sizeof(unsigned long) * i))) {
227 printf("checksum [%d] = %08lx\n", i, checksum[i]);
228 error("checksums do not match", __LINE__);
229 }
230 }
231 printf("\n\tParent: children calculated segment successfully\n");
232 /*
233 * Program completed successfully -- exit
234 */
235 printf("\nsuccessful!\n");
236
237 return (0);
238 }
239
child(int num,unsigned char * shmptr)240 static void child(int num, unsigned char *shmptr)
241 {
242 unsigned long cksum = 0;
243 int i;
244
245 read_lock(lockfd);
246 for (i = 0; i < buffer_size; i++)
247 cksum += *shmptr++;
248 unlock_file(lockfd);
249
250 *(checksum + (sizeof(unsigned long) * num)) = cksum;
251 printf("\t\tchild (%02d): checksum %08lx\n", num,
252 *(checksum + (sizeof(unsigned long) * num)));
253 }
254
write_lock(int fd)255 static void write_lock(int fd)
256 {
257 if (lockf(fd, F_LOCK, 0) < 0)
258 sys_error("lockf (LOCK) failed", __LINE__);
259 }
260
read_lock(int fd)261 static void read_lock(int fd)
262 {
263 if (lockf(fd, F_TEST, 0) < 0)
264 sys_error("lockf (LOCK) failed", __LINE__);
265 }
266
unlock_file(int fd)267 static void unlock_file(int fd)
268 {
269 if (lockf(fd, F_ULOCK, 0) < 0)
270 sys_error("lockf (UNLOCK) failed", __LINE__);
271 }
272
273 /*---------------------------------------------------------------------+
274 | setup_handler () |
275 | ==================================================================== |
276 | |
277 | Function: Setup the signal handler for SIGPIPE. |
278 | |
279 +---------------------------------------------------------------------*/
setup_signal_handlers()280 void setup_signal_handlers()
281 {
282 struct sigaction invec;
283
284 invec.sa_handler = (void (*)(int))handler;
285 sigemptyset(&invec.sa_mask);
286 invec.sa_flags = 0;
287
288 if (sigaction(SIGINT, &invec, NULL) < 0)
289 sys_error("sigaction failed", __LINE__);
290
291 }
292
293 /*---------------------------------------------------------------------+
294 | handler () |
295 | ==================================================================== |
296 | |
297 | Function: Signal catching function for SIGPIPE signal. |
298 | |
299 | o SIGPIPE: Print message and abort program... |
300 | |
301 | o SIGINT: Parent process calls cleanup, child processes |
302 | simply exit |
303 | |
304 | o Other: Print message and abort program... |
305 | |
306 +---------------------------------------------------------------------*/
handler(int sig,int code,struct sigcontext * scp)307 void handler(int sig, int code, struct sigcontext *scp)
308 {
309 char msg[100]; /* Buffer for error message */
310
311 if (sig == SIGINT) {
312 if (getpid() == parent_pid) {
313
314 fprintf(stderr, "Received SIGINT -- cleaning up...\n");
315 fflush(stderr);
316
317 cleanup();
318 } else
319 exit(-1);
320 } else {
321 sprintf(msg, "Received an unexpected signal (%d)", sig);
322 error(msg, __LINE__);
323 }
324 }
325
326 /*---------------------------------------------------------------------+
327 | cleanup () |
328 | ==================================================================== |
329 | |
330 | Function: Closes all of the pipes, kills all of the child |
331 | processes and exits the program... |
332 | |
333 +---------------------------------------------------------------------*/
cleanup()334 void cleanup()
335 {
336 int i;
337
338 if (getpid() == parent_pid) {
339 for (i = 0; i < num_children; i++) {
340 if (pid[i] > (pid_t) 0 && kill(pid[i], SIGKILL) < 0)
341 sys_error("signal failed", __LINE__);
342 }
343 }
344
345 exit(-1);
346 }
347
create_lock_file(char * lock_name)348 static int create_lock_file(char *lock_name)
349 {
350 int fd;
351
352 if ((fd = open(lock_name, O_RDWR)) < 0) {
353 if ((fd = open(lock_name, O_RDWR | O_CREAT | O_EXCL, 0666)) < 0) {
354 perror("cannot create lock file");
355 exit(-1);
356 }
357 }
358 return (fd);
359 }
360
361 /*---------------------------------------------------------------------+
362 | parse_args () |
363 | ==================================================================== |
364 | |
365 | Function: Parse the command line arguments & initialize global |
366 | variables. |
367 | |
368 | Updates: (command line options) |
369 | |
370 | [-s] size: shared memory segment size |
371 | |
372 | [-c] num_children: number of child processes |
373 | |
374 +---------------------------------------------------------------------*/
parse_args(int argc,char ** argv)375 void parse_args(int argc, char **argv)
376 {
377 int i;
378 int errflag = 0;
379 char *program_name = *argv;
380 extern char *optarg; /* Command line option */
381
382 while ((i = getopt(argc, argv, "c:s:?")) != EOF) {
383 switch (i) {
384 case 'c':
385 num_children = atoi(optarg);
386 break;
387 case 's':
388 buffer_size = atoi(optarg);
389 break;
390 case '?':
391 errflag++;
392 break;
393 }
394 }
395
396 if (errflag) {
397 fprintf(stderr, USAGE, program_name);
398 exit(2);
399 }
400 }
401
402 /*---------------------------------------------------------------------+
403 | sys_error () |
404 | ==================================================================== |
405 | |
406 | Function: Creates system error message and calls error () |
407 | |
408 +---------------------------------------------------------------------*/
sys_error(const char * msg,int line)409 void sys_error(const char *msg, int line)
410 {
411 char syserr_msg[256];
412
413 sprintf(syserr_msg, "%s: %s\n", msg, strerror(errno));
414 error(syserr_msg, line);
415 }
416
417 /*---------------------------------------------------------------------+
418 | error () |
419 | ==================================================================== |
420 | |
421 | Function: Prints out message and exits... |
422 | |
423 +---------------------------------------------------------------------*/
error(const char * msg,int line)424 void error(const char *msg, int line)
425 {
426 fprintf(stderr, "ERROR [line: %d] %s\n", line, msg);
427 cleanup();
428 }
429