diff --git a/pipes/ping-pong/Makefile b/pipes/ping-pong/Makefile new file mode 100644 index 0000000..c1ea72a --- /dev/null +++ b/pipes/ping-pong/Makefile @@ -0,0 +1,11 @@ +SHELL:=/usr/bin/env bash + +CC = gcc +CFLAGS = +LDFLAGS = + +pipong: pipong.c + $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) + +clean: + rm -f pipong diff --git a/pipes/ping-pong/pipong.c b/pipes/ping-pong/pipong.c new file mode 100644 index 0000000..aa2de37 --- /dev/null +++ b/pipes/ping-pong/pipong.c @@ -0,0 +1,131 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * pipong is a program that uses UNIX system calls to “ping-pong” a byte between + * two processes over a pair of pipes, one for each direction. Measure the + * program’s performance, in ex- changes per second. + * + * it's an exercise from: + * + * */ + + +//struct pp_child { +// int out_fd[2]; +// int in[2]; +// int result_pipe_fd[2]; +//}; + +/* + * this is one side of the pingpong game, it + * out -> end of the pipe to write output (fd) + * */ +void timed_pingpong(float delayInSeconds, int in_fd, int out_fd, int result_fd) { + time_t startTime; + time_t now; + float elapsedTime = 0; + float setTime = delayInSeconds; + + char c; + int count = 0; + + time(&startTime); + while (elapsedTime < setTime) { + if (read(in_fd, &c, 1) != 1) /* read 1 byte from in_fd (ping) */ + break; + write(out_fd, &c, 1); /* write 1 byte to out_fd (pong) */ + count++; + now = time(NULL); + elapsedTime = difftime(now, startTime); + } + // write the total number of bytes written + write(result_fd, &count, sizeof(count)); + close(result_fd); +} + +int main(int argc, char *argv[]) +{ + pid_t c1, c2; + + // pipe descriptors + int p2c[2]; /* parent to child */ + int up[2]; + int down[2]; + int r1[2], r2[2]; + + if ((pipe(up) == -1) || (pipe(down)) || + (pipe(r1) == -1) || (pipe(r2))) { + perror("pipe"); + exit(EXIT_FAILURE); + } + + // internal buffers for children + char buf1, buf2; + + // counters for checking how many bytes are written + int counter = 0; + + if (argc != 3) { + fprintf(stderr, "Usage: %s \n", argv[0]); + exit(EXIT_FAILURE); + } + + /* could add check here to make sure arv[1] is only a single byte */ + /* but maybe it's easier to always just play with one byte */ + + float delay_sec = atof(argv[2]); + + if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) { + perror("signal"); + exit(EXIT_FAILURE); + } + /* spawn two child processes */ + c1 = fork(); + if (c1 == -1) { + perror("fork"); + } + if (c1 == 0) { + // close fds that we don't need here + close(down[1]); + close(up[0]); + close(r1[0]); + // read down stdin, write up stdout + timed_pingpong(delay_sec, down[0], up[1], r1[1]); + exit(0); + } + + + c2 = fork(); + if (c2 == -1) { + perror("fork"); + } + if (c2 == 0) { + // close unneeded fds + close(up[1]); + close(down[0]); + close(r2[0]); + // read up stdin, write down stdout + timed_pingpong(delay_sec, up[0], down[1], r2[1]); + exit(0); + } + + /* Parent writes the initial byte from argv[1] to up_pipe */ + write(up[1], argv[1], 1); + + long c1_count = 0, c2_count = 0; + read(r1[0], &c1_count, sizeof(c1_count)); + read(r2[0], &c2_count, sizeof(c2_count)); + + waitpid(c1, NULL, 0); + waitpid(c2, NULL, 0); + + printf("Child 1: %ld, child 2: %ld bytes in %.2f seconds\n", c1_count, c2_count, delay_sec); + exit(EXIT_SUCCESS); +} diff --git a/pipes/ping-pong/readme.md b/pipes/ping-pong/readme.md new file mode 100644 index 0000000..148d580 --- /dev/null +++ b/pipes/ping-pong/readme.md @@ -0,0 +1,17 @@ +# ping pong with pipes + +Note to self... remember to fix the hole in your pipe! + +Two child processes talk to each other via a set of pipes, ping-ponging +a byte back and fourth. The program measures how many times the byte is +transferred between processes in a time interval specified by the user. + +``` ++->ifd > [ c1 ] > ofd->--| +| +-------<-----+ ++----<-----|---<------+--+ ++-----<----+ | +| | ++-> ifd > [ c2 ] > ofd --+ + +``` diff --git a/pipes/ping-pong/result.txt b/pipes/ping-pong/result.txt new file mode 100644 index 0000000..ad1a9c2 --- /dev/null +++ b/pipes/ping-pong/result.txt @@ -0,0 +1,2 @@ +Child 1 21172600 bytes in 100.00 seconds +Child 2 21172601 bytes in 100.00 seconds diff --git a/pipes/ping-pong/scripts/benchmark.sh b/pipes/ping-pong/scripts/benchmark.sh new file mode 100755 index 0000000..ba287c1 --- /dev/null +++ b/pipes/ping-pong/scripts/benchmark.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +if [ "$#" -ne 1 ]; then + echo "usage: ${BASH_SOURCE[0]} " + exit 1 +fi +LOCATION="$1" + +echo "running on ${LOCATION} - pipong will transfer a byte back and forth between two processes via a set of pipes..." + +count=0 +total=0 +for sec in 1.0 2.0 4.0 8.0 16.0 32.0; +do + run=$(./pipong A "$sec" | awk '{tot = $3 + $6} END {print tot / $9}' ) + total=$((total + run)) + count=$((count+1)) +done + + +# Average +result=$((total / count)) +printf "no of bytes transferred: %s (b/sec) \n" "$result" + diff --git a/pipes/ping-pong/scripts/pingpong_a.sh b/pipes/ping-pong/scripts/pingpong_a.sh new file mode 100755 index 0000000..734d560 --- /dev/null +++ b/pipes/ping-pong/scripts/pingpong_a.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# two named pipes +if [ ! -f p1 ]; then + mkfifo p1 +fi +if [ ! -f p2 ]; then + mkfifo p2 +fi + +# proc A +while true; +do + while IFS= read -r msg; + do + echo "$msg" + if [ "$msg" == "pong" ]; then + echo ping > ./p2 + sleep 1 + fi + done <<<$(cat ./p1) +done diff --git a/pipes/ping-pong/scripts/pingpong_b.sh b/pipes/ping-pong/scripts/pingpong_b.sh new file mode 100755 index 0000000..9252c75 --- /dev/null +++ b/pipes/ping-pong/scripts/pingpong_b.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +# proc B +while true; +do + while IFS= read -r msg; + do + echo "$msg" + if [ "$msg" == "ping" ]; then + echo pong > ./p1 + sleep 1 + fi + done <<<$(cat ./p2) +done diff --git a/pipes/ping-pong/scripts/repl.sh b/pipes/ping-pong/scripts/repl.sh new file mode 100755 index 0000000..a3bec19 --- /dev/null +++ b/pipes/ping-pong/scripts/repl.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +PID="" + +while inotifywait -q ./pipong.c; do + echo -e "\n\n" + + # If a previous instance is running, kill it + if [[ -n "$PID" ]] && kill -0 "$PID" 2>/dev/null; then + echo "Killing running instance ($PID)…" + kill "$PID" + wait "$PID" 2>/dev/null + fi + + echo "Starting new instance…" + make clean && make + ./pipong A & + PID=$! +done + diff --git a/pipes/ping-pong/scripts/single.sh b/pipes/ping-pong/scripts/single.sh new file mode 100755 index 0000000..1f35060 --- /dev/null +++ b/pipes/ping-pong/scripts/single.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +player1 () { + while true + do read n + if "$n" == "ping"; then + echo pong > fifo2 + fi + done <<<$(cat fifo1) +} + +player2 () { + while true + do read n + if "$n" == "pong"; then + echo ping > fifo1 + fi + done <<<$(cat fifo2) +} + +mkfifo fifo1 +mkfifo fifo2 + +(echo "ping" > fifo1; (player1; player2))