Previous Lecture Lecture 15 Next Lecture

Lecture 15, Tue 11/27

Unix 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 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

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__ << endl;
  sleep(10);

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

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

  sleep(10);

  cout << "After sleep, " << __FILE__ << " " <<
   __LINE__ << " "  << __FUNCTION__ << endl;
  
  return 0;
}

Example with fork / exec

// hello.cpp
#include <unistd.h>
#include <iostream>

using namespace std;

int main() {
  cout << "Hello World!" << endl;
  sleep(15);
  return 0;
}
------
g++ -o hello hello.cpp
------
forkExec: forkExec.o
	${CXX} -o forkExec -std=C++11 forkExec.o
------
// forkExec.cpp
#include <unistd.h>
#include <iostream>

using namespace std;

// path to some executable
#define HELLO_EXECUTABLE "/Users/Richert/Desktop/lecture/hello"

int main() {
	cout << "Before fork, " << __FILE__ << ", " << __LINE__ << " " \
	<< __FUNCTION__ << endl;

	// 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() << endl;
	cout << "PPID: " << getppid() << endl;

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

		int execvResult;
		char* const path[] = { HELLO_EXECUTABLE };
		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 executes this
	// wait to check if no child process exists)
	// 	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;
} // Play around with ps –l between sleep to see PPID and PID