add: assigment 2

This commit is contained in:
Kiana Sheibani 2025-10-06 23:27:46 -04:00
parent 3a0b8a5c3f
commit 32a6e07e56
Signed by: toki
GPG key ID: 6CB106C25E86A9F7
11 changed files with 437 additions and 0 deletions

26
assignment2/default.nix Normal file
View file

@ -0,0 +1,26 @@
let
basic-c = subdir: name: { stdenv }:
stdenv.mkDerivation {
inherit name;
src = ./.;
buildPhase = ''
runHook preBuild
gcc ${subdir}/${name}.c -o ${name}
runHook postBuild
'';
installPhase = ''
runHook preInstall
mkdir -p $out/bin
install ${name} $out/bin/
runHook postInstall
'';
};
in {
a2-p1-producer = basic-c "part1" "producer";
a2-p1-consumer = basic-c "part1" "consumer";
a2-p2-bidirectional = basic-c "part2" "bidirectional";
a2-p3-producer = basic-c "part3" "producer";
a2-p3-consumer = basic-c "part3" "consumer";
}

View file

@ -0,0 +1,47 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <getopt.h>
#include <time.h>
int main(int argc, char *argv[]) {
int max_lines = -1; // -1 means unlimited
int verbose = 0;
// Parse arguments (-n max_lines, -v verbose)
char opt;
while ((opt = getopt(argc, argv, "n:v")) != -1) {
switch (opt) {
case 'n':
max_lines = atoi(optarg); break;
case 'v':
verbose = 1; break;
default:
printf("Usage: %s [-n max_lines] [-v]\n", argv[0]);
return 1;
}
}
// Read from stdin line by line
// Count lines and characters
// If verbose, echo lines to stdout
char buffer[256];
int lines = 0;
int chars = 0;
while (fgets(buffer, sizeof(buffer), stdin) != NULL) {
int size = strlen(buffer);
if (verbose)
fwrite(buffer, sizeof(char), size, stdout);
chars += size;
if (size > 0 && buffer[size - 1] == '\n')
lines++;
if (lines == max_lines)
break;
}
// Print statistics to stderr
fprintf(stderr, "Lines: %d\n", lines);
return 0;
}

View file

@ -0,0 +1,44 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <getopt.h>
int main(int argc, char *argv[]) {
FILE *input = stdin;
int buffer_size = 4096;
// Parse command line arguments
// -f filename (optional)
// b buffer_size (optional)
char opt;
while ((opt = getopt(argc, argv, "f:b:")) != -1) {
switch (opt) {
case 'f':
input = fopen(optarg, "r");
if (input == NULL) {
fprintf(stderr, "Error: Could not open input file\n");
return EXIT_FAILURE;
}
break;
case 'b':
buffer_size = atoi(optarg); break;
default:
printf("Usage: %s [-f filename] [-b buffer_size]\n", argv[0]);
return 1;
}
}
// Allocate buffer
char *buffer = malloc(buffer_size);
// Read from input and write to stdout
while (fgets(buffer, buffer_size, input) != NULL) {
fwrite(buffer, sizeof(char), strlen(buffer), stdout);
}
// Cleanup
free(buffer);
return 0;
}

47
assignment2/part1/test Executable file
View file

@ -0,0 +1,47 @@
#!/usr/bin/env bash
echo "Building executables..."
cd "${FLAKE:-$(dirname $0)}"
if command -v nix; then
producer="$(nix build --no-link --print-out-paths .#a2-p1-producer)/bin/producer"
consumer="$(nix build --no-link --print-out-paths .#a2-p1-consumer)/bin/consumer"
else
gcc producer.c -o producer
gcc consumer.c -o consumer
producer="./producer"
consumer="./consumer"
fi
echo -e "\nTEST - Sanity Check"
out="$(seq -s " " 1 10 | "$producer" | "$consumer" -v)"
if test "$out" = "$(seq -s " " 1 10)"; then
echo "SUCCESS"
fi
echo -e "\nTEST - Smaller Buffer Size"
out="$(seq -s " " 1 10 | "$producer" -b 16 | "$consumer" -v)"
if test "$out" = "$(seq -s " " 1 10)"; then
echo "SUCCESS"
fi
echo -e "\nTEST - Large External File"
head -c 10000 </dev/urandom | od -An -tx >random.txt
out="$("$producer" -f random.txt | "$consumer" -v)"
if test "$out" = "$(cat random.txt)"; then
echo "SUCCESS"
fi
rm random.txt
echo -e "\nTEST - Maximum Lines (5)"
head -c 1000 </dev/urandom | od -An -tx >random.txt
out="$("$producer" -f random.txt | "$consumer" -v -n 5)"
if test "$out" = "$(cat random.txt | head -n 5)"; then
echo "SUCCESS"
fi
rm random.txt

View file

@ -0,0 +1,56 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
int main () {
int pipe1[2]; // Parent to child
int pipe2[2]; // Child to parent
// Create both pipes
if (pipe(pipe1) == -1 || pipe(pipe2) == -1) {
fprintf(stderr, "Could not open pipes");
return 1;
}
// Fork process
pid_t pid = fork();
if (pid == 0) {
// Child process
// Close unused pipe ends
close(pipe1[1]) ; // Close write end of pipe1
close(pipe2[0]) ; // Close read end of pipe2
// Read message, send response
char *message = malloc(100);
read(pipe1[0], message, 100);
printf("%s", message);
free(message);
char response[] = "Response from child to parent\n";
write(pipe2[1], response, sizeof(response));
} else {
// Parent process
// Close unused pipe ends
close(pipe1[0]) ; // Close read end of pipe1
close(pipe2[1]) ; // Close write end of pipe2
// Send message, read response
char message[] = "Sending message from parent to child\n";
write(pipe1[1], message, sizeof(message));
char *response = malloc(100);
read(pipe2[0], response, 100);
printf("%s", response);
free(response);
// Wait for child process to terminate
waitpid(pid, NULL, 0);
}
return 0;
}

20
assignment2/part2/test Executable file
View file

@ -0,0 +1,20 @@
#!/usr/bin/env bash
echo "Building executable..."
cd "${FLAKE:-$(dirname $0)}"
if command -v nix; then
bidirectional="$(nix build --no-link --print-out-paths .#a2-p2-bidirectional)/bin/bidirectional"
else
gcc bidirectional.c -o bidirectional
bidirectional="./bidirectional"
fi
out="$("$bidirectional")"
read -r -d '' expected <<EOF
Sending message from parent to child
Response from child to parent
EOF
if test "$out" = "$expected"; then
echo "SUCCESS"
fi

View file

@ -0,0 +1,88 @@
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <getopt.h>
#include <time.h>
volatile sig_atomic_t shutdown_flag = 0;
volatile sig_atomic_t stats_flag = 0;
void handle_sigint(int sig) {
shutdown_flag = 1;
}
void handle_sigusr1(int sig) {
stats_flag = 1;
}
int main(int argc, char *argv[]) {
struct sigaction sa_sigint;
sa_sigint.sa_handler = handle_sigint;
sigemptyset(&sa_sigint.sa_mask);
sa_sigint.sa_flags = 0;
sigaction(SIGINT, &sa_sigint, NULL);
struct sigaction sa_sigusr1;
sa_sigusr1.sa_handler = handle_sigusr1;
sigemptyset(&sa_sigusr1.sa_mask);
sa_sigusr1.sa_flags = 0;
sigaction(SIGUSR1, &sa_sigusr1, NULL);
int max_lines = -1; // -1 means unlimited
int verbose = 0;
// Parse arguments (-n max_lines, -v verbose)
char opt;
while ((opt = getopt(argc, argv, "n:v")) != -1) {
switch (opt) {
case 'n':
max_lines = atoi(optarg); break;
case 'v':
verbose = 1; break;
default:
printf("Usage: %s [-n max_lines] [-v]\n", argv[0]);
return 1;
}
}
clock_t start = clock();
// Read from stdin line by line
// Count lines and characters
// If verbose, echo lines to stdout
char buffer[256];
int lines = 0;
int chars = 0;
while (fgets(buffer, sizeof(buffer), stdin) != NULL) {
int size = strlen(buffer);
if (verbose)
fwrite(buffer, sizeof(char), size, stdout);
chars += size;
if (size > 0 && buffer[size - 1] == '\n')
lines++;
if (lines == max_lines)
break;
// Handle shutdown
if (shutdown_flag) {
fprintf(stderr, "Cancelled\n");
return 1;
}
}
// Print statistics to stderr (handle SIGUSR1)
if (stats_flag) {
clock_t end = clock();
double time = ((double)(end - start)) / CLOCKS_PER_SEC;
double throughput = (double)chars / 1024.0 / 1024.0 / time;
printf("Stats:\n");
printf("File Size: %d bytes\n", chars);
printf("Total Time: %.3f secs\n", time);
printf("Throughput: %.3f MB/s\n", throughput);
}
fprintf(stderr, "Lines: %d\n", lines);
return 0;
}

View file

@ -0,0 +1,63 @@
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <getopt.h>
volatile sig_atomic_t shutdown_flag = 0;
void handle_sigint(int sig) {
shutdown_flag = 1;
}
int main(int argc, char *argv[]) {
struct sigaction sa;
sa.sa_handler = handle_sigint;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGINT, &sa, NULL);
FILE *input = stdin;
int buffer_size = 4096;
// Parse command line arguments
// -f filename (optional)
// b buffer_size (optional)
char opt;
while ((opt = getopt(argc, argv, "f:b:")) != -1) {
switch (opt) {
case 'f':
input = fopen(optarg, "r");
if (input == NULL) {
fprintf(stderr, "Error: Could not open input file\n");
return EXIT_FAILURE;
}
break;
case 'b':
buffer_size = atoi(optarg); break;
default:
printf("Usage: %s [-f filename] [-b buffer_size]\n", argv[0]);
return 1;
}
}
// Allocate buffer
char *buffer = malloc(buffer_size);
// Read from input and write to stdout
while (fgets(buffer, buffer_size, input) != NULL) {
fwrite(buffer, sizeof(char), strlen(buffer), stdout);
// Handle shutdown
if (shutdown_flag) {
fprintf(stderr, "Cancelled\n");
return 1;
}
}
// Cleanup
free(buffer);
return 0;
}

33
assignment2/part3/test Executable file
View file

@ -0,0 +1,33 @@
#!/usr/bin/env bash
echo "Building executables..."
cd "${FLAKE:-$(dirname $0)}"
if command -v nix; then
producer="$(nix build --no-link --print-out-paths .#a2-p3-producer)/bin/producer"
consumer="$(nix build --no-link --print-out-paths .#a2-p3-consumer)/bin/consumer"
else
gcc producer.c -o producer
gcc consumer.c -o consumer
producer="./producer"
consumer="./consumer"
fi
seq 1 10000000 >large.txt
echo -e "\nTEST - Buffer size 1024"
"$producer" -b 1024 -f large.txt | "$consumer" &
kill -USR1 "$(pidof consumer)"
wait "$(jobs -rp)"
echo -e "\nTEST - Buffer size 4096"
"$producer" -b 4096 -f large.txt | "$consumer" &
kill -USR1 "$(pidof consumer)"
wait "$(jobs -rp)"
echo -e "\nTEST - Buffer size 16384"
"$producer" -b 16384 -f large.txt | "$consumer" &
kill -USR1 "$(pidof consumer)"
wait "$(jobs -rp)"
rm large.txt

12
assignment2/test.nix Normal file
View file

@ -0,0 +1,12 @@
let
bash-test = subdir: { runCommandLocal }:
runCommandLocal "${subdir}-test" { FLAKE = ../.; }
''
mkdir -p $out/bin
install ${subdir}/test $out/bin/
'';
in {
a2-p1 = bash-test "part1";
a2-p2 = bash-test "part2";
a2-p3 = bash-test "part3";
}

View file

@ -10,6 +10,7 @@
let
subdirs = [
"assignment1"
"assignment2"
];
eachSystem = nixpkgs.lib.genAttrs (import systems);