Previous Lecture Lecture 14 Next Lecture

Lecture 14, Tue 05/19

Unix Processes, Fork / Exec

Linux Command / Process Management

MacBook-Pro-38:lecture Richert$ exec ls -l
total 1736
-rw-r--r--@  1 Richert  staff      92 May 28 18:58 Makefile
-rwxr-xr-x   1 Richert  staff    4248 May 28 19:42 main
-rw-r--r--@  1 Richert  staff     108 May 28 19:41 main.cpp
-rw-r--r--   1 Richert  staff     608 May 28 19:42 main.o
-rwxr-xr-x   1 Richert  staff  866700 May 17 13:35 my_googletest
drwxr-xr-x  17 Richert  staff     578 May 28 18:55 previous_examples

[Process completed]
1. bash (fork) - A copy of the bash shell process is made with fork()
    2. bash_copy (exec `ls -l`) - `ls -l` command / application replaces bash_copy process
        3. `ls –l` terminates. OS removes bash_copy memory space. Control is resumed to original bash shell.

Example of fork with a simple C++ program

// makefile
CXX=g++

clean:
	rm -f *.o forkIt

forkIt: forkIt.o
	${CXX} -o forkIt -std=c++11 forkIt.o

// forkIt.cpp
#include <unistd.h> // sleep(), fork(), pid_t (in sys/types.h)
#include <iostream>
#include <string>

using namespace std;

int main() {
    cout << "Before fork, " << __FILE__ << " " << __LINE__ << " "  << __FUNCTION__ << " PID: " << getpid() << " PPID: " << getppid() << endl;
    sleep(10);

    pid_t result = fork(); // child_result == 0, parent_result == PID of child

    cout << endl << "Return result of fork: " << result << endl;

    cout << "After fork, " << __FILE__ << " " << __LINE__ << " "  << __FUNCTION__ << " PID: " << getpid() << " PPID: " << getppid() << endl;

    sleep(10);

    cout << endl;
    cout << "After sleep, " << __FILE__ << " " << __LINE__ << " "  << __FUNCTION__ << " PID: " << getpid() << " PPID: " << getppid() << endl;

    return 0;
}

Example with fork / exec

// Modify makefile
clean:
	rm -f *.o  forkExec

forkExec: forkExec.o
	${CXX} -o forkExec -std=c++11 forkExec.o
// hello.cpp
#include <unistd.h>
#include <iostream>

using namespace std;

int main() {
    cout << "Hello World!" << endl;
    sleep(15);
    return 0;
}
// Create executable
g++ -o hello hello.cpp
// forkExec.cpp
#include <unistd.h>
#include <iostream>

using namespace std;

int main() {
    // path to some executable
    char* const HELLO_EXECUTABLE = (char*) "./hello";
    
    cout << "Before fork, " << __FILE__ << ", " << __LINE__ << " " << __FUNCTION__ << endl;
    sleep(10);

    // parent receives child PID, child_result == 0
    pid_t result = fork();

    cout << "After fork, " << __FILE__ << ", " << __LINE__ << " " << __FUNCTION__ << endl;

    cout << "RESULT_PID = " << result << endl;
    cout << "PID: " << getpid() << " PPID: " << getppid() << endl;
    cout << endl;
    sleep(10);

	

    // Following if block executed ONLY by child process
    if (result == 0) {
        cout << "---" << endl;
        cout << "RESULT_PID = " << result << endl;
        cout << "PID: " << getpid() << " PPID: " << getppid() << endl;
		cout << endl;

        int execvResult;
        char* const path[] = { HELLO_EXECUTABLE , NULL};
        execvResult = execv(HELLO_EXECUTABLE, path); //if success, run then terminate

        // THIS LINE OF CODE NEVER REACHED IN CHILD
        // (unless execv returned an error)

        perror("execv seems to have failed");
        cerr << "execvResult=" << execvResult << endl;
        exit(1);
    }

    // parent process executes this
    // wait to check if no child process exists anymore
    // 	https://linux.die.net/man/2/waitpid
    while (waitpid(result, NULL, 0)) {
        if (errno == ECHILD) { // all children of process terminated
            cout << "pid: " << getpid() << " has no children" << endl;
            break;
        }
    }
    cout << "After waiting, " << __FILE__ ", " << __LINE__ << " " << __FUNCTION__ << endl;
    return 0;
} // Play around with ps –l between sleep to see PPID and PID