pipe_test_02.c revision 2c28215423293e443469a07ae7011135d058b671
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 */ 19/*---------------------------------------------------------------------+ 20| pipe_test_02 | 21| ==================================================================== | 22| | 23| Description: Max data transfer through pipe interprocess channel | 24| in non-blocking mode | 25| | 26| Algorithm: o Create a pipe | 27| o Make write & read end of pipe non-blocking | 28| o Spawn a child process | 29| o parent: | 30| - create & send data packets to the child | 31| - compute checksum on sent packets | 32| o child: | 33| - recieve packets from parent & compute checksum | 34| - send final checksum to parent | 35| o parent: | 36| - compare checksum of sent packets with the | 37| child's checksum | 38| | 39| System calls: The following system calls are tested: | 40| | 41| pipe () - Creates an interprocess channel | 42| fork () - Creates a new process | 43| fcntl () - | 44| waitpid () - Waits for a child process to stop or | 45| | 46| Usage: pipe_test_02 | 47| | 48| To compile: cc -o pipe_test_02 pipe_test_02.c | 49| | 50| Last update: Ver. 1.3, 3/3/94 12:06:38 | 51| | 52| Change Activity | 53| | 54| Version Date Name Reason | 55| 0.1 010393 DJK Initial version for AIX 4.1 | 56| 1.2 021394 DJK Move to "prod" directory | 57| | 58+---------------------------------------------------------------------*/ 59 60#include <errno.h> 61#include <fcntl.h> 62#include <signal.h> 63#include <stdio.h> 64#include <stdlib.h> 65#include <string.h> 66#include <sys/types.h> 67#include <sys/wait.h> 68#include <unistd.h> 69 70/* Defines: 71 * 72 * MB: one megabyte (MB) 73 * 74 * VALID_PACKET: value sent with each packet, used to verify that the 75 * packets contents were not garbled. 76 * 77 * DEFAULT_NUM_CHILDREN: default number of child processes spawned 78 * 79 * DEFAULT_PACKETS_TO_SEND: default number of packets sent to each child 80 * process. 81 * 82 * MAXCHILD: maximum number of child processes which may be spawned 83 * (based upon the number of file descriptors that a process may use) 84 * 85 * USAGE: usage statement 86 */ 87#define MB (1024*1024) 88#define DEFAULT_PACKETS_TO_SEND 1024 89#define DEFAULT_NUM_CHILDREN 1 90#define OPEN_MAX 256 91#define MAXCHILD (OPEN_MAX/2 - 2) 92#define VALID_PACKET 0xabcdef01 93#define USAGE "\nUsage: %s [-n] [-p nprocs] [{-m totmegs | -b totbytes}]\n\n" \ 94 "\t-n transfer data with NON-BLOCKING reads & writes\n" \ 95 "\t-p nprocs number of child processes to spawn\n" \ 96 "\t-m totmegs number of MB to send through pipe\n" \ 97 "\t-b totmegs number of bytes to send through pipe\n" \ 98 "\t (must be less than %d)\n\n" 99 100/* 101 * Function Prototypes: 102 * 103 * setup (): Parse command line arguments and intialize variables 104 * child (): Child process 105 * cleanup (): Close all pipes and kill child processes 106 * sys_error (): System error message function 107 * error (): Error message function 108 * setup_signal_handlers (): Sets up signal catching functions 109 * handler (): Signal catching function 110 */ 111void setup (int, char **); 112void child (int [], int []); 113void cleanup (); 114void sys_error (const char *, int); 115void error (const char *, int); 116void setup_signal_handlers (); 117void handler (int, int, struct sigcontext *); 118 119/* 120 * Structures & Global variables 121 * 122 * num_children: number of child processes to be spawned 123 * 124 * num_packets: number of packets to be sent to each child process 125 * 126 * non_blocking_flag: uses NON-BLOCKING 127 * 128 * pid: process id's of the spawned processes 129 * 130 * p2child: half duplex pipes from parent to child (parent writes, 131 * child reads). 132 * 133 * p2parent: half duplex pipe from child to parent (child writes, 134 * parent reads). 135 */ 136 137enum { READ, WRITE }; /* Pipe read & write end indices */ 138 139struct data_packet { 140 pid_t pid; /* Child process id */ 141 int last; /* Indicates last packet when set */ 142 long valid; /* Insure packet was not garbled */ 143 long seq_number; /* Packet sequence number */ 144 unsigned long checksum; /* Cumulative checksum so far */ 145 unsigned char data; /* Data sent in packet */ 146}; 147typedef struct data_packet data_packet; 148 149int num_children = DEFAULT_NUM_CHILDREN; 150long num_packets = DEFAULT_PACKETS_TO_SEND; 151int non_blocking_flag = 0; /* Uses NON-BLOCKING pipes when set */ 152int bflg = 0; /* Data quantity flag (MB) */ 153int mflg = 0; /* Data quantity flag (bytes) */ 154 155pid_t parent_pid; /* Parent's process id */ 156pid_t pid [MAXCHILD]; /* Process id's of spawned processes */ 157int p2child [MAXCHILD][2]; /* Pipes from parent to child processes */ 158int p2parent [2]; /* Pipe from child processes to parent */ 159char err_msg [256]; /* Generic error message buffer */ 160 161/*---------------------------------------------------------------------+ 162| main () | 163| ==================================================================== | 164| | 165| Function: Main program (see prolog for more details) | 166| | 167| Returns: (0) Successful completion | 168| (-1) Error occurred | 169| | 170+---------------------------------------------------------------------*/ 171int main (int argc, char **argv) 172{ 173 int i; 174 int n; /* Number of bytes written */ 175 int status; /* Child's exit status */ 176 long packets_sent; 177 unsigned char data; 178 unsigned long cksum_parent = 0; 179 data_packet packet; 180 181 /* 182 * Parse command line arguments, initialize global variables and 183 * print program header 184 */ 185 setup (argc, argv); 186 printf ("%s: IPC Pipe TestSuite program\n", *argv); 187 fflush (stdout); 188 189 /* 190 * Create two sets of half duplex pipes: 191 * 192 * p2child: for sending packets from the parent process to the child 193 * processes and 194 * p2parent: for sending checksums from the child processes to the 195 * parent 196 * 197 * If the non-blocking command line option was specified, use fcntl () 198 * to set the O_NONBLOCK file descriptor status flags. This will 199 * prevent reads & and writes from blocking if the data is not yet 200 * available 201 */ 202 printf ("\n\tCreating pipes...\n"); 203 fflush (stdout); 204 205 if (pipe (p2parent) < 0) 206 sys_error ("pipe failed", __LINE__); 207 208 if (non_blocking_flag) { 209 printf ("\n\tSending data NON-BLOCKING!\n"); 210 fflush (stdout); 211 } 212 213 for (i=0; i<num_children; i++) { 214 if (pipe (&p2child [i][0]) < 0) 215 sys_error ("pipe failed", __LINE__); 216 if (non_blocking_flag) { 217 if (fcntl (p2child [i][READ], F_SETFL, O_NONBLOCK) < 0) 218 sys_error ("fcntl (O_NONBLOCK) failed", __LINE__); 219 if (fcntl (p2child [i][WRITE], F_SETFL, O_NONBLOCK) < 0) 220 sys_error ("fcntl (O_NONBLOCK) failed", __LINE__); 221 } 222 } 223 224 /* 225 * Spawn num_children processes 226 * 227 * Fork of the child process & record the newly created process's 228 * id for future reference. 229 * 230 * Then close the READ end of the p2child pipe, since the parent 231 * process will be writing into this pipe rather than reading. 232 * Also close the WRITE end of the p2parent pipe, for just the 233 * the reverse reasons... 234 */ 235 printf ("\n\tSpawning %d child processes ... \n", num_children); 236 fflush (stdout); 237 238 for (i=0; i<num_children; i++) { 239 240 if ((pid [i] = fork()) == 0) { 241 242 /* Child process */ 243 child (&p2child[i][0], p2parent); 244 exit (0); 245 246 } else if (pid [i] < (pid_t)0) 247 sys_error ("fork failed", __LINE__); 248 249 if (close (p2child [i][READ]) < 0) 250 sys_error ("close failed", __LINE__); 251 } 252 if (close (p2parent [WRITE]) < 0) 253 sys_error ("close failed", __LINE__); 254 255 /* 256 * Send data packets to the child processes 257 * 258 * Build packets (initialize all of the packets fields) and then 259 * send the packets to all of the child processes. 260 * 261 * Might have to make several attempts with the NON-BLOCKING writes 262 * if the resource is not immediately available. 263 */ 264 printf ("\n\tParent: sending %ld packets (%ld bytes) to child processes ...\n", 265 num_packets, num_packets * sizeof (struct data_packet)); 266 267 packet.last = 0; 268 packet.valid = VALID_PACKET; 269 270 for (packets_sent = data = 0; num_packets > 0; num_packets--) { 271 272 packet.seq_number = ++packets_sent; 273 packet.data = data++; 274 packet.pid = pid [i]; 275 packet.checksum = cksum_parent += packet.data; 276 277 for (i=0; i<num_children; i++) { 278 try_write_ETXN_again: 279 if ((n = write (p2child [i][WRITE], &packet, 280 sizeof (packet))) < 0) { 281 if (non_blocking_flag && errno == EAGAIN) { 282 goto try_write_ETXN_again; 283 } else { 284 sys_error ("write failed", __LINE__); 285 } 286 } 287 } 288 } 289 290 /* 291 * Send the last packet to the child processes 292 * 293 * [ Upon receiving this packet, the child process will know that all 294 * of the packets have been sent and that the parent process is 295 * expecting the child to send it's checksum back. ] 296 * 297 * After sending the last packet, close the WRITE end of the p2child 298 * pipe as we are finish sending packets to the child processes. 299 * 300 * Then wait for all of the child processes to send the checksum 301 * packets. Upon receiving the checksum packets verify that the 302 * child's checksum matches that of the parent. 303 * 304 * Might have to make several attempts with the NON-BLOCKING writes 305 * if the resource is not immediately available. 306 * 307 * Finally, close READ end of p2parent pipe as we have finished 308 * receiving checksums from the child. 309 */ 310 packet.last = 1; 311 printf ("\n\tParent: done sending packets & waiting for children to complete!\n"); 312 for (i=0; i<num_children; i++) { 313 try_read_again: 314 if (write (p2child [i][WRITE], &packet, sizeof (packet)) < 0) { 315 if (non_blocking_flag && errno == EAGAIN) { 316 goto try_read_again; 317 } else { 318 sys_error ("write failed", __LINE__); 319 } 320 } 321 if (close (p2child [i][WRITE]) < 0) 322 sys_error ("close failed", __LINE__); 323 324 if (read (p2parent [READ], &packet, sizeof (packet)) <= 0) 325 sys_error ("read failed", __LINE__); 326 327 if (packet.valid != VALID_PACKET) 328 error ("received packet with corrupted data from child!", 329 __LINE__); 330 331 if (cksum_parent != packet.checksum) { 332 sprintf (err_msg, "checksum of data sent by parent " \ 333 "does not match checksum of data received by " \ 334 "child [pid %d]\n" \ 335 "\tchild's checksum: %08lx\n" \ 336 "\tparent's checksum: %08lx\n", 337 packet.pid, packet.checksum, cksum_parent); 338 error (err_msg, __LINE__); 339 } 340 } 341 if (close (p2parent [READ]) < 0) 342 sys_error ("close failed", __LINE__); 343 344 /* 345 * Wait for all of the child processes to complete & check their 346 * exit status. 347 * 348 * Upon completion of the child proccesses, exit program with success. 349 */ 350 for (i=0; i<num_children; i++) { 351 waitpid (pid [i], &status, 0); 352 353 if (!WIFEXITED (status)) 354 sys_error ("child process terminated abnormally", 355 __LINE__); 356 } 357 printf ("\n\tParent: children received all packets & exited successfully\n"); 358 359 /* Program completed successfully -- exit */ 360 printf ("\nsuccessful!\n"); 361 362 return (0); 363} 364 365/*---------------------------------------------------------------------+ 366| child () | 367| ==================================================================== | 368| | 369| Function: Receive packets from the parent, insure they are valid | 370| and not out of sequence, and calculate a running | 371| checksum. Upon receiving the last packet from the | 372| parent, build a checksum packet and send it to the parent.| 373| | 374| Args: p2child - Pipe from parent to child | 375| p2parent - Pipe from child to parent | 376| | 377| Returns: Exits with (-1) if an error occurs | 378| | 379+---------------------------------------------------------------------*/ 380void child (int p2child [], int p2parent []) 381{ 382 int n; /* Bytes read */ 383 pid_t pid = getpid (); /* Process id of child */ 384 int end_of_transmission = 0; 385 long packets_received = 0; /* Number of packets received 386 * from parent 387 */ 388 389 data_packet packet; /* Packet used to transmiting data */ 390 unsigned long cksum_child = 0;/* Checksum of data fields received */ 391 392 /* 393 * Close the WRITE end of the p2child pipe, since the child 394 * process will be reading from this pipe rather than writing. 395 * Also close the READ end of the p2parent pipe, for just the 396 * the reverse reasons... 397 */ 398 if (close (p2child [WRITE]) < 0) 399 sys_error ("close failed", __LINE__); 400 if (close (p2parent [READ]) < 0) 401 sys_error ("close failed", __LINE__); 402 403 /* 404 * Receive packets from parent & insure packets are valid 405 * 406 * Read packets from the parent through p2child pipe. Upon 407 * recieving the packet, verify that it is valid, in sequence 408 * and that both the parent's and child's checksums match. 409 * 410 * Might have to make several attempts with the NON-BLOCKING 411 * reads if the resource is not immediately available. 412 * 413 * Continue reading packets until the "last" packet is received 414 * from the parent. Upon receiving the last packet, close 415 * the p2child READ pipe, as we are finished receiving packets 416 * from the parent. 417 */ 418 while (!end_of_transmission) { 419 try_write_again: 420 n = read (p2child [READ], &packet, sizeof (packet)); 421 if (n < 0) { 422 /* Resource not available */ 423 if (non_blocking_flag && errno == EAGAIN) 424 goto try_write_again; 425 else 426 sys_error ("read failed", __LINE__); 427 } else if (n > 0) { 428 /* Insure packet is valid */ 429 if (packet.valid != VALID_PACKET) { 430 sprintf (err_msg, 431 "child received invalid packet " \ 432 "from parent:\n\tpacket #: %ld\n", 433 packets_received); 434 error (err_msg, __LINE__); 435 } 436 /* Received last packet */ 437 if (packet.last) { 438 end_of_transmission = 1; 439 } else { 440 441 /* Insure packet was not received out of sequence */ 442 packets_received++; 443 if (packets_received != packet.seq_number) { 444 sprintf (err_msg, 445 "child received packet out of sequence\n" \ 446 "\texpecting packet: %ld\n" \ 447 "\treceived packet: %ld\n", 448 packets_received, packet.seq_number); 449 error (err_msg, __LINE__); 450 } 451 452 /* Insure checksums still match */ 453 cksum_child += packet.data; 454 if (cksum_child != packet.checksum) { 455 sprintf (err_msg, 456 "child & parent checksums do not match\n" \ 457 "\tchild checksum: %08lx\n" \ 458 "\tparent checksum: %08lx\n" \ 459 "\tpacket number: %ld\n", 460 cksum_child, packet.checksum, packets_received); 461 error (err_msg, __LINE__); 462 } 463 } 464 } 465 } 466 if (close (p2child [READ]) < 0) 467 sys_error ("close failed", __LINE__); 468 469 /* 470 * Send parent packet containing child's checksum 471 * 472 * Build a checksum packet (initialize packet fields) and then 473 * send the packet to the parent. 474 * 475 * Then close the WRITE p2parent pipe as we have finished sending packets 476 * to the parent. 477 */ 478 printf ("\t\tChild: pid [%d] received %ld packets from parent\n", 479 pid, packets_received); 480 481 packet.pid = pid; 482 packet.valid = VALID_PACKET; 483 packet.checksum = cksum_child; 484 485 if (write (p2parent [WRITE], &packet, sizeof (packet)) < 0) 486 sys_error ("write failed", __LINE__); 487 if (close (p2parent [WRITE]) < 0) 488 sys_error ("close failed", __LINE__); 489} 490 491/*---------------------------------------------------------------------+ 492| setup () | 493| ==================================================================== | 494| | 495| Function: Parse the command line arguments & initialize global | 496| variables. | 497| | 498| Updates: (command line options) | 499| | 500| [-n] non_blocking_flag: prevents read & write calls from | 501| from blocking if the resource is not available. | 502| | 503| [-p] num_packets: number of packets ... | 504| | 505| [-c] num_children: number of child processes to spawn ... | 506| | 507+---------------------------------------------------------------------*/ 508void setup (int argc, char **argv) 509{ 510 int i; 511 int errflag = 0; 512 int bytes = 0, megabytes = 0; 513 char *program_name = *argv; 514 extern char *optarg; /* Command line option */ 515 516 while ((i = getopt(argc, argv, "nm:b:p:?")) != EOF) { 517 switch (i) { 518 case 'n': /* NON-BLOCKING flag */ 519 non_blocking_flag++; 520 break; 521 case 'm': /* MB */ 522 mflg++; 523 megabytes = atoi (optarg); 524 break; 525 case 'b': /* bytes */ 526 bflg++; 527 bytes = atoi (optarg); 528 break; 529 case 'p': /* number of child procs */ 530 num_children = atoi (optarg); 531 break; 532 case '?': 533 errflag++; 534 break; 535 } 536 } 537 if (mflg) { 538 num_packets = megabytes * MB / sizeof (struct data_packet); 539 } else if (bflg) { 540 num_packets = bytes / sizeof (struct data_packet); 541 } 542 543 if (num_packets == 0 || num_children == 0 || num_children > MAXCHILD) 544 errflag++; 545 546 if (errflag) { 547 fprintf (stderr, USAGE, program_name, MAXCHILD); 548 exit (2); 549 } 550 /* 551 * Setup signal catching function for SIGPIPE & SIGINT, record 552 * the process id of the parent and initialize the child process 553 * id array. 554 */ 555 setup_signal_handlers (); 556 557 parent_pid = getpid (); 558 559 for (i=0; i<num_children; i++) { 560 pid [i] = (pid_t)0; 561 } 562} 563 564/*---------------------------------------------------------------------+ 565| setup_handler () | 566| ==================================================================== | 567| | 568| Function: Setup the signal handler for SIGPIPE. | 569| | 570+---------------------------------------------------------------------*/ 571void setup_signal_handlers () 572{ 573 struct sigaction invec; 574 575 invec.sa_handler = (void (*)(int)) handler; 576 sigemptyset (&invec.sa_mask); 577 invec.sa_flags = 0; 578 579 if (sigaction (SIGINT, &invec, (struct sigaction *) NULL) < 0) 580 sys_error ("sigaction failed", __LINE__); 581 582 if (sigaction (SIGPIPE, &invec, (struct sigaction *) NULL) < 0) 583 sys_error ("sigaction failed", __LINE__); 584} 585 586/*---------------------------------------------------------------------+ 587| handler () | 588| ==================================================================== | 589| | 590| Function: Signal catching function for SIGPIPE signal. | 591| | 592| o SIGPIPE: Print message and abort program... | 593| | 594| o SIGINT: Parent process calls cleanup, child processes | 595| simply exit | 596| | 597| o Other: Print message and abort program... | 598| | 599+---------------------------------------------------------------------*/ 600void handler (int sig, int code, struct sigcontext *scp) 601{ 602 char msg [100]; /* Buffer for error message */ 603 604 if (sig == SIGPIPE) { 605 error ("wrote to pipe with closed read end", __LINE__); 606 } else if (sig == SIGINT) { 607 if (getpid () == parent_pid) { 608 609 fprintf (stderr, "Received SIGINT -- cleaning up...\n"); 610 fflush (stderr); 611 612 cleanup (); 613 } 614 else 615 exit (-1); 616 } else { 617 sprintf (msg, "Received an unexpected signal (%d)", sig); 618 error (msg, __LINE__); 619 } 620} 621 622/*---------------------------------------------------------------------+ 623| cleanup () | 624| ==================================================================== | 625| | 626| Function: Closes all of the pipes, kills all of the child | 627| processes and exits the program... | 628| | 629+---------------------------------------------------------------------*/ 630void cleanup () 631{ 632 int i; 633 634 if (getpid () == parent_pid) { 635 for (i=0; i<num_children; i++) { 636 if (pid [i] > (pid_t)0 && kill (pid [i], SIGKILL) < 0) 637 sys_error ("signal failed", __LINE__); 638 639 close (p2child [i][READ]); 640 close (p2child [i][WRITE]); 641 close (p2parent [READ]); 642 close (p2parent [WRITE]); 643 } 644 } 645 646 exit (-1); 647} 648 649/*---------------------------------------------------------------------+ 650| sys_error () | 651| ==================================================================== | 652| | 653| Function: Creates system error message and calls error () | 654| | 655+---------------------------------------------------------------------*/ 656void sys_error (const char *msg, int line) 657{ 658 char syserr_msg [256]; 659 660 sprintf (syserr_msg, "%s: %s\n", msg, strerror (errno)); 661 error (syserr_msg, line); 662} 663 664/*---------------------------------------------------------------------+ 665| error () | 666| ==================================================================== | 667| | 668| Function: Prints out message and calls cleanup... | 669| | 670+---------------------------------------------------------------------*/ 671void error (const char *msg, int line) 672{ 673 fprintf (stderr, "ERROR [line: %d] %s\n", line, msg); 674 fflush (stderr); 675 cleanup (); 676}