Previous Lecture Lecture 13 Next Lecture

Lecture 13, Mon 03/04

Basic OS Concepts, Unix Processes, Fork / Exec

Operating System

A (simplified) view of the Application / OS / Hardware Stack.

Processes

Threads

Process Status (ps) command

MacBook-Pro-38:lecture Richert$ ps -l
  UID   PID  PPID        F CPU PRI NI       SZ    RSS WCHAN     S             ADDR TTY           TIME CMD
  501 31029 31027     4006   0  31  0  2498932    492 -      R+                  0 ttys000   16:29.05 -bash
  501 68157 68156     4006   0  31  0  2489716   3428 -      S+                  0 ttys001    0:00.14 -bash
  501 68585 68584     4006   0  31  0  2489716   3368 -      S                   0 ttys002    0:00.07 -bash

top command

Processes: 395 total, 4 running, 391 sleeping, 2436 threads                                                          19:35:58
Load Avg: 1.92, 1.73, 1.65  CPU usage: 6.82% user, 15.52% sys, 77.64% idle
SharedLibs: 156M resident, 38M data, 37M linkedit. MemRegions: 215376 total, 6562M resident, 110M private, 1222M shared.
PhysMem: 16G used (4285M wired), 79M unused.
VM: 1096G vsize, 627M framework vsize, 1454320269(0) swapins, 1468765567(0) swapouts.
Networks: packets: 97637475/70G in, 65969671/12G out. Disks: 64971993/6343G read, 61258168/6352G written.

PID    COMMAND      %CPU TIME     #TH   #WQ  #PORTS MEM    PURG   CMPRS  PGRP  PPID  STATE    BOOSTS              %CPU_ME
97722  Google Chrom 0.0  00:32.88 18    2    138    5188K  0B     55M    47766 47766 sleeping *0[1]               0.00000
97702  Google Chrom 0.0  00:26.14 17    1    143    4988K  0B     43M    47766 47766 sleeping *0[1]               0.00000
97678  Google Chrom 0.0  00:40.43 18    2    138    3644K  0B     53M    47766 47766 sleeping *0[1]               0.00000
97675  Google Chrom 0.0  00:32.14 17    1    137    3428K  0B     55M    47766 47766 sleeping *0[1]               0.00000
97674  Google Chrom 0.0  00:30.36 9     1    94     8012K  0B     66M    47766 47766 sleeping *0[1]               0.00000
97673  Google Chrom 0.0  30:11.52 16    1    155    49M    0B     93M    47766 47766 sleeping *0[1]               0.00000
97670  Google Chrom 0.0  00:22.85 17    1    143    5192K  0B     41M    47766 47766 sleeping *0[1]               0.00000
97528  MTLCompilerS 0.0  00:00.73 2     2    31     120K   0B     6920K  97528 1     sleeping  0[4]               0.00000
97023  Slack Helper 0.0  44:29.52 5     2    139    50M    2608K  220M   2308  2308  sleeping *0[1]               0.00000
94576  com.apple.ap 0.0  29:25.52 3     1    394    32M    0B     22M    94576 1     sleeping *0[100448]          0.00000
94575  TextEdit     0.0  22:45.46 6     1    523    33M    0B     56M    94575 1     sleeping *0[59144]           0.00000
91340- dsAccessServ 0.0  00:14.24 5     1    57     1052K  0B     2324K  91327 91327 sleeping *0[1]               0.00000
91327- dsAccessServ 0.0  28:13.91 13    4    119    3504K  0B     3484K  91327 1     sleeping *0[1]               0.00000
91232- PulseTray    0.0  02:57.28 6     1    252    8892K  0B     26M    91232 1     sleeping *0[1]               0.00000
90135  familycircle 0.0  00:03.11 2     2    47     232K   0B     2644K  90135 1     sleeping  0[25]              0.00000
87331  Google Chrom 0.0  14:05.59 19    2    159    86M    0B     204M   47766 47766 sleeping *0[1]               0.00000

Example of creating our own process

# Makefile
CXX=g++
main: main.o
	${CXX} -o main -std=C++11 main.o
clean:
	rm -f *.o main
-----
// main.cpp
#include <unistd.h>
using namespace std;
int main() {
	while (true) {
	sleep(10000); }
}

Foreground / Background Processes

MacBook-Pro-38:lecture Richert$ make main
g++    -c -o main.o main.cpp
g++ -o main -std=C++11 main.o
MacBook-Pro-38:lecture Richert$ ./main
^C
MacBook-Pro-38:lecture Richert$ ./main&
[1] 68713
MacBook-Pro-38:lecture Richert$ ps -l
  UID   PID  PPID        F CPU PRI NI       SZ    RSS WCHAN     S             ADDR TTY           TIME CMD
  501 31029 31027     4006   0  31  0  2498932    492 -      R+                  0 ttys000   24:33.59 -bash
  501 68157 68156     4006   0  31  0  2489716   3428 -      S+                  0 ttys001    0:00.14 -bash
  501 68585 68584     4006   0  31  0  2489716   3380 -      S                   0 ttys002    0:00.10 -bash
  501 68713 68585     4006   0  31  0  2433800    664 -      R                   0 ttys002    0:01.72 ./main
MacBook-Pro-38:lecture Richert$ jobs
MacBook-Pro-38:lecture Richert$ ./main&
[1] 68726
MacBook-Pro-38:lecture Richert$ jobs
[1]+  Running                 ./main &
MacBook-Pro-38:lecture Richert$ ps
  PID TTY           TIME CMD
31029 ttys000   25:56.74 -bash
68157 ttys001    0:00.14 -bash
68585 ttys002    0:00.12 -bash
68726 ttys002    0:06.81 ./main
MacBook-Pro-38:lecture Richert$ kill 68726
[1]+  Terminated: 15          ./main

Suspend / Resume processes

MacBook-Pro-38:lecture Richert$ ./main
^Z
[1]+  Stopped                 ./main
MacBook-Pro-38:lecture Richert$ jobs
[1]+  Stopped                 ./main
MacBook-Pro-38:lecture Richert$ bg %1
[1]+ ./main &
MacBook-Pro-38:lecture Richert$ jobs
[1]+  Running                 ./main &
MacBook-Pro-38:lecture Richert$ ps
  PID TTY           TIME CMD
31029 ttys000   33:08.34 -bash
68157 ttys001    0:00.16 -bash
68768 ttys001    0:06.58 ./main
MacBook-Pro-38:lecture Richert$ kill 68768
[1]+  Terminated: 15          ./main

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