Skip to content

Commit 94d0eb5

Browse files
author
XuhuaHuang
committed
Refactor large integer multiplication program
1 parent e7f7429 commit 94d0eb5

File tree

1 file changed

+118
-99
lines changed

1 file changed

+118
-99
lines changed

OperatingSystem/large_integer_multiplication.c

Lines changed: 118 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* @brief Demonstrates large integer multiplication via recursive decomposition,
55
* delegating sub-multiplications to a child process over a pair of pipes.
66
*
7-
* Usage: ./large_mul <a> <b> (both values must be in [1000, 9999])
7+
* Usage: ./large_mul <a> <b> (both values must be in [1000, 9999])
88
*
99
* @version 0.1
1010
* @date 2026-02-20
@@ -20,58 +20,26 @@
2020
#include <unistd.h>
2121

2222
/**
23-
* @brief Signed 64-bit integer for intermediate values
23+
* @brief Signed 64-bit integer used for all intermediate and final values.
2424
*/
2525
typedef long long ll_t;
2626

27-
/* A simple two-element message sent across either pipe. */
28-
typedef struct {
29-
ll_t x;
30-
ll_t y;
31-
} pair_t;
32-
33-
/**
34-
* @brief Write a pair_t struct atomically to a file descriptor.
35-
*
36-
* @param fd The file descriptor to write to.
37-
* @param p The pair_t struct to write.
38-
* @return int 0 on success, -1 on failure
39-
*/
40-
static int write_pair(int fd, pair_t p) {
41-
// `write` may not write all bytes in one call, but for small structs it is atomic on pipes.
42-
// it returns the number of bytes actually written, which may be less than sizeof(pair_t) if an error occurs.
43-
ssize_t n = write(fd, &p, sizeof(pair_t));
44-
if (n != (ssize_t)sizeof(pair_t)) {
45-
perror("write_pair: write");
46-
return -1;
47-
}
48-
return 0;
27+
static inline void print_calc(const char* label) {
28+
printf("\n");
29+
printf("#################\n");
30+
printf("# Calculating %s #\n", label);
31+
printf("#################\n");
32+
return;
4933
}
5034

5135
/**
52-
* @brief Read a pair_t struct atomically from a file descriptor.
36+
* @brief Write a single ll_t value atomically to a file descriptor.
5337
*
54-
* @param fd The file descriptor to read from.
55-
* @param p The pair_t struct to store the read values.
56-
* @return int 0 on success, -1 on failure
38+
* @param fd Destination file descriptor.
39+
* @param v Value to transmit.
40+
* @return int 0 on success, -1 on failure.
5741
*/
58-
static int read_pair(int fd, pair_t* p) {
59-
ssize_t n = read(fd, p, sizeof(pair_t));
60-
if (n != (ssize_t)sizeof(pair_t)) {
61-
perror("read_pair: read");
62-
return -1;
63-
}
64-
return 0;
65-
}
66-
67-
/**
68-
* @brief Write a signed 64-bit integer atomically to a file descriptor.
69-
*
70-
* @param fd The file descriptor to write to.
71-
* @param v The value to write.
72-
* @return int 0 on success, -1 on failure
73-
*/
74-
static int write_ll(int fd, ll_t v) {
42+
static inline int write_ll(int fd, ll_t v) {
7543
ssize_t n = write(fd, &v, sizeof(ll_t));
7644
if (n != (ssize_t)sizeof(ll_t)) {
7745
perror("write_ll: write");
@@ -81,13 +49,13 @@ static int write_ll(int fd, ll_t v) {
8149
}
8250

8351
/**
84-
* @brief Read a signed 64-bit integer atomically from a file descriptor.
52+
* @brief Read a single ll_t value atomically from a file descriptor.
8553
*
86-
* @param fd The file descriptor to read from.
87-
* @param v The point to store the read value in.
88-
* @return int 0 on success, -1 on failure
54+
* @param fd Source file descriptor.
55+
* @param v Pointer to store the received value.
56+
* @return int 0 on success, -1 on failure.
8957
*/
90-
static int read_ll(int fd, ll_t* v) {
58+
static inline int read_ll(int fd, ll_t* v) {
9159
ssize_t n = read(fd, v, sizeof(ll_t));
9260
if (n != (ssize_t)sizeof(ll_t)) {
9361
perror("read_ll: read");
@@ -97,28 +65,45 @@ static int read_ll(int fd, ll_t* v) {
9765
}
9866

9967
/**
100-
* @brief The child waits for four pair_t messages, multiplies each pair, and
101-
* returns the ll_t result back to the parent. After the fourth result it
102-
* closes its pipe ends and exits cleanly.
68+
* @brief Child event loop: receives two operands individually, multiplies
69+
* them, and returns the product. Repeats for @p rounds iterations before
70+
* closing its pipe ends and exiting cleanly.
10371
*
104-
* @param pipe_in read-end of the parent to child pipe
105-
* @param pipe_out write-end of the child to parent pipe
72+
* @param pipe_in Read-end of the parent-to-child pipe.
73+
* @param pipe_out Write-end of the child-to-parent pipe.
74+
* @param rounds Number of multiplication jobs to process.
10675
*/
107-
static void run_child(int pipe_in, int pipe_out) {
108-
for (int i = 0; i < 4; ++i) {
109-
pair_t operands;
110-
if (read_pair(pipe_in, &operands) != 0) {
111-
fprintf(stderr, "child: failed to read operands (round %d)\n", i + 1);
76+
static void run_child(int pipe_in, int pipe_out, int rounds) {
77+
pid_t curr_pid = getpid();
78+
pid_t parent_pid = getppid();
79+
80+
for (int i = 0; i < rounds; ++i) {
81+
ll_t x = 0, y = 0;
82+
83+
if (read_ll(pipe_in, &x) != 0) {
84+
fprintf(
85+
stderr, "Child (PID %d, PPID %d): failed to read first operand (round %d)\n", curr_pid, parent_pid, i + 1
86+
);
11287
exit(EXIT_FAILURE);
11388
}
89+
printf("Child (PID %d, PPID %d): Received %lld from parent\n", curr_pid, parent_pid, x);
90+
fflush(stdout);
11491

115-
ll_t product = operands.x * operands.y;
92+
if (read_ll(pipe_in, &y) != 0) {
93+
fprintf(
94+
stderr, "Child (PID %d, PPID %d): failed to read second operand (round %d)\n", curr_pid, parent_pid, i + 1
95+
);
96+
exit(EXIT_FAILURE);
97+
}
98+
printf("Child (PID %d, PPID %d): Received %lld from parent\n", curr_pid, parent_pid, y);
99+
fflush(stdout);
116100

117-
printf("[child] round %d: %lld * %lld = %lld\n", i + 1, operands.x, operands.y, product);
101+
ll_t product = x * y;
102+
printf("Child (PID %d, PPID %d): Sending %lld to parent\n", curr_pid, parent_pid, product);
118103
fflush(stdout);
119104

120105
if (write_ll(pipe_out, product) != 0) {
121-
fprintf(stderr, "child: failed to write result (round %d)\n", i + 1);
106+
fprintf(stderr, "Child (PID %d PPID %d): failed to write result (round %d)\n", curr_pid, parent_pid, i + 1);
122107
exit(EXIT_FAILURE);
123108
}
124109
}
@@ -128,23 +113,48 @@ static void run_child(int pipe_in, int pipe_out) {
128113
exit(EXIT_SUCCESS);
129114
}
130115

131-
static ll_t send_and_receive(int pipe_out, int pipe_in, ll_t x, ll_t y) {
132-
pair_t job = {x, y};
133-
if (write_pair(pipe_out, job) != 0) {
134-
fprintf(stderr, "parent: failed to send job (%lld, %lld)\n", x, y);
116+
/**
117+
* @brief Send two operands to the child individually and await the product.
118+
*
119+
* Each operand is transmitted as a distinct ll_t write so the child can
120+
* log its receipt of each value independently, matching the expected output
121+
* protocol.
122+
*
123+
* @param p_write Write-end of the parent-to-child pipe.
124+
* @param p_read Read-end of the child-to-parent pipe.
125+
* @param x First operand.
126+
* @param y Second operand.
127+
* @param parent_pid PID of the parent process, for logging.
128+
* @return ll_t The product returned by the child.
129+
*/
130+
static ll_t send_and_receive(int p_write, int p_read, ll_t x, ll_t y, pid_t parent_pid) {
131+
printf("Parent (PID %d): Sending %lld to child\n", parent_pid, x);
132+
fflush(stdout);
133+
if (write_ll(p_write, x) != 0) {
134+
fprintf(stderr, "Parent (PID %d): failed to send %lld\n", parent_pid, x);
135+
exit(EXIT_FAILURE);
136+
}
137+
138+
printf("Parent (PID %d): Sending %lld to child\n", parent_pid, y);
139+
fflush(stdout);
140+
if (write_ll(p_write, y) != 0) {
141+
fprintf(stderr, "Parent (PID %d): failed to send %lld\n", parent_pid, y);
135142
exit(EXIT_FAILURE);
136143
}
137144

138145
ll_t result = 0;
139-
if (read_ll(pipe_in, &result) != 0) {
140-
fprintf(stderr, "parent: failed to read result for (%lld, %lld)\n", x, y);
146+
if (read_ll(p_read, &result) != 0) {
147+
fprintf(stderr, "Parent (PID %d): failed to read result for (%lld, %lld)\n", parent_pid, x, y);
141148
exit(EXIT_FAILURE);
142149
}
150+
printf("Parent (PID %d): Received %lld from child\n", parent_pid, result);
151+
fflush(stdout);
152+
143153
return result;
144154
}
145155

146156
int main(int argc, char* argv[]) {
147-
/* --- Argument validation ------------------------------------------ */
157+
/* Argument validation */
148158
if (argc != 3) {
149159
fprintf(stderr, "Usage: %s <a> <b>\n", argv[0]);
150160
fprintf(stderr, " Both arguments must be integers in the range [1000, 9999].\n");
@@ -164,17 +174,16 @@ int main(int argc, char* argv[]) {
164174
return EXIT_FAILURE;
165175
}
166176

167-
/* --- Decompose a and b -------------------------------------------- */
168-
ll_t a1 = a / 100; /* high two digits of a */
169-
ll_t a2 = a % 100; /* low two digits of a */
170-
ll_t b1 = b / 100; /* high two digits of b */
171-
ll_t b2 = b % 100; /* low two digits of b */
172-
173-
printf("[parent] a=%lld => a1=%lld, a2=%lld\n", a, a1, a2);
174-
printf("[parent] b=%lld => b1=%lld, b2=%lld\n", b, b1, b2);
177+
printf("Your integers are %lld %lld\n", a, b);
175178
fflush(stdout);
176179

177-
/* --- Establish pipes ----------------------------------------------- */
180+
/* Decompose a and b */
181+
ll_t a1 = a / 100; // first two digits of a, integer division truncates
182+
ll_t a2 = a % 100; // last two digits of a
183+
ll_t b1 = b / 100; // firsttwo digits of b
184+
ll_t b2 = b % 100; // last two digits of b
185+
186+
/* Establish pipes */
178187
int parent_to_child[2]; /* parent writes, child reads */
179188
int child_to_parent[2]; /* child writes, parent reads */
180189

@@ -183,49 +192,60 @@ int main(int argc, char* argv[]) {
183192
return EXIT_FAILURE;
184193
}
185194

186-
/* --- Fork ---------------------------------------------------------- */
195+
/* Fork */
187196
pid_t pid = fork();
188197
if (pid < 0) {
189198
perror("fork");
190199
return EXIT_FAILURE;
191200
}
192201

202+
/* Most importanat part of the program */
203+
// Identify the child process and run the child event loop
193204
if (pid == 0) {
194-
/* ---- Child ---- */
205+
/* Child */
206+
// Close unused pipe ends in the child thread for hygiene and to avoid accidental misuse.
195207
close(parent_to_child[1]); /* child does not write to this pipe */
196208
close(child_to_parent[0]); /* child does not read from this pipe */
197209

198-
run_child(parent_to_child[0], child_to_parent[1]);
210+
run_child(parent_to_child[0], child_to_parent[1], /* rounds = */ 4);
199211
/* run_child never returns */
200212
}
201213

202-
/* ---- Parent ---- */
214+
/* Parent */
215+
pid_t parent_pid = getpid();
216+
203217
close(parent_to_child[0]); /* parent does not read from this pipe */
204218
close(child_to_parent[1]); /* parent does not write to this pipe */
205219

206220
int p_write = parent_to_child[1];
207221
int p_read = child_to_parent[0];
208222

209-
/* Round 1: A = a1 * b1 => X = A * 10^4 */
210-
ll_t A = send_and_receive(p_write, p_read, a1, b1);
223+
printf("Parent (PID %d): created child (PID %d)\n", parent_pid, pid);
224+
fflush(stdout);
225+
226+
/* Round 1: X = (a1 * b1) * 10^4 */
227+
print_calc("X");
228+
fflush(stdout);
229+
230+
ll_t A = send_and_receive(p_write, p_read, a1, b1, parent_pid);
211231
ll_t X = A * 10000LL;
212-
printf("[parent] A = a1*b1 = %lld => X = A*10^4 = %lld\n", A, X);
213232

214-
/* Round 2: B = a2 * b1 */
215-
ll_t B = send_and_receive(p_write, p_read, a2, b1);
216-
printf("[parent] B = a2*b1 = %lld\n", B);
233+
/* Round 2 & 3: Y = (a1*b2 + a2*b1) * 10^2 */
234+
print_calc("Y");
235+
fflush(stdout);
217236

218-
/* Round 3: C = a1 * b2 => Y = (B + C) * 10^2 */
219-
ll_t C = send_and_receive(p_write, p_read, a1, b2);
237+
ll_t B = send_and_receive(p_write, p_read, a1, b2, parent_pid);
238+
ll_t C = send_and_receive(p_write, p_read, a2, b1, parent_pid);
220239
ll_t Y = (B + C) * 100LL;
221-
printf("[parent] C = a1*b2 = %lld => Y = (B+C)*10^2 = %lld\n", C, Y);
222240

223-
/* Round 4: D = a2 * b2 => Z = D * 10^0 */
224-
ll_t D = send_and_receive(p_write, p_read, a2, b2);
241+
/* Round 4: Z = a2 * b2 */
242+
print_calc("Z");
243+
fflush(stdout);
244+
245+
ll_t D = send_and_receive(p_write, p_read, a2, b2, parent_pid);
225246
ll_t Z = D;
226-
printf("[parent] D = a2*b2 = %lld => Z = D = %lld\n", D, Z);
227247

228-
/* --- Close pipe ends and reap child -------------------------------- */
248+
/* Close pipe ends and reap child */
229249
close(p_write);
230250
close(p_read);
231251

@@ -235,11 +255,10 @@ int main(int argc, char* argv[]) {
235255
fprintf(stderr, "Warning: child process did not exit cleanly.\n");
236256
}
237257

238-
/* --- Final computation -------------------------------------------- */
258+
/* Final result */
239259
ll_t result = X + Y + Z;
240260

241-
printf("\n[parent] X + Y + Z = %lld + %lld + %lld = %lld\n", X, Y, Z, result);
242-
printf("[parent] Verification: %lld * %lld = %lld\n", a, b, a * b);
261+
printf("\n%lld * %lld == %lld + %lld + %lld == %lld\n", a, b, X, Y, Z, result);
243262

244263
return EXIT_SUCCESS;
245264
}

0 commit comments

Comments
 (0)