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
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 */
2525typedef 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
146156int 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