Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Creating Child Processes with fork and Replacing Them Using exec

Tech 3

The project directory structure contains two source files and a Makefile for building the executables.

Makefile:

all: controller worker

controller: controller.cpp
	g++ controller.cpp -o controller

worker: worker.cpp
	g++ worker.cpp -o worker

clean:
	rm -f controller worker

controller.cpp (Master Process):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

#define DELAY_MS(ms) usleep((ms) * 1000)

int main(int argc, char* argv[]) {
    printf("Controller PID: %d, Arguments: ", getpid());
    for (int idx = 0; idx < argc; idx++) {
        printf("[%d]=%s ", idx, argv[idx]);
    }
    printf("\n");

    struct sigaction child_signal_action;
    child_signal_action.sa_handler = SIG_DFL;
    child_signal_action.sa_flags = SA_NOCLDWAIT;
    sigaction(SIGCHLD, &child_signal_action, NULL);

    pid_t child_pid = fork();
    if (child_pid == 0) {
        int exec_result = execl("./worker", "param1=value1", "param2=value2", "param3=98765", NULL);
        if (exec_result == -1) {
            perror("execl failed");
            return EXIT_FAILURE;
        }
        printf("This line should not be printed after successful exec.\n");
        return 0;
    }

    for (int counter = 0; counter < 10; counter++) {
        DELAY_MS(2000);
        printf("Controller iteration: %d\n", counter);
    }
    printf("Controller process completed.\n");
    return 0;
}

worker.cpp (Slave Process):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define DELAY_MS(ms) usleep((ms) * 1000)

int main(int argc, char* argv[]) {
    printf("Worker PID: %d, Arguments: ", getpid());
    for (int idx = 0; idx < argc; idx++) {
        printf("[%d]=%s ", idx, argv[idx]);
    }
    printf("\n");

    for (int loop_counter = 0; loop_counter < 40; loop_counter++) {
        DELAY_MS(800);
        printf("Worker output: ---------------------------------------- count=%d\n", loop_counter);
    }
    printf("Worker process completed.\n");
    return 0;
}

Compile and run the program:

$ make
$ ./controller

Sample output:

Controller PID: 8921, Arguments: [0]=./controller 
Worker PID: 8922, Arguments: [0]=param1=value1 [1]=param2=value2 [2]=param3=98765 
Worker output: ---------------------------------------- count=0
Worker output: ---------------------------------------- count=1
Controller iteration: 0
Worker output: ---------------------------------------- count=2
Worker output: ---------------------------------------- count=3
Controller iteration: 1
Worker output: ---------------------------------------- count=4
Worker output: ---------------------------------------- count=5
Worker output: ---------------------------------------- count=6
Controller iteration: 2
...
Controller iteration: 9
Controller process completed.
Worker output: ---------------------------------------- count=24
...
Worker output: ---------------------------------------- count=39
Worker process completed.

Key observations:

  1. The controller process (PID 8921) exits after its loop completes.
  2. The worker process (PID 8922) continues running and exits independent.
  3. When using execl to launch the worker, the first argument passed (param1=value1) becomes argv[0] in the worker's main function, replacing the typical program file path.

Process relationship can be verified:

ps -A | grep -E "controller|worker"

Understanding fork:

  • When the parent process executes fork(), it creates a duplicate child process.
  • Both processes initially share identical memory content.
  • The child process inherits open file dsecriptors and other kernel objects from the parent.
  • However, after exec is called in the child, its memory image is complete replaced by the new program, while inherited resources like file descriptors may remain open unless explicitly closed.

For example, if the parent opens a file and the child closes it, subsequent write operations by the parent may not produce errors but might not write data correctly to the file.

Tags: forkexec

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.