CS139-lecture-20210916

Processes cont. #

Scheduling cont. #

image_2021-09-16-13-40-13 image_2021-09-16-13-46-55 image_2021-09-16-13-49-03 image_2021-09-16-13-50-23

A single linked list works well for ready and wait queues. Note the addition of a tail pointer. The tail pointer gives us a constant time complexity to get to the end of the queue. New processes are added at the tail.

image_2021-09-16-13-54-42

Schedulers #

image_2021-09-16-13-57-16

Note the difference between short-term and long-term schedulers. Long-term scheduling handles multiprogramming.

image_2021-09-16-13-59-51 image_2021-09-16-14-01-26 image_2021-09-16-14-02-21

Note: Read more on medium term scheduling in the text.

Context switch #

image_2021-09-16-14-03-40 image_2021-09-16-14-04-31

Process operations #

image_2021-09-16-14-13-43 image_2021-09-16-14-13-48

So what is the relationship between the parent and child’s memory space? Are there things in common? Can a parent and child process talk to each other? It depends:

image_2021-09-16-14-13-58 image_2021-09-16-14-19-09

How can we see the process tree in linux:

ps auxf

f is the option to show process tree, or

pstree

image_2021-09-16-14-20-58

  • Directly after the fork() system call, both parent and child are both the same program, ie bash. They will share their entire memory space, even the program counter (the child doesn’t start from the beginning of the program).
  • Once the child calls exec() it replaces the process’ memory space with a new program, ie ps.

image_2021-09-16-14-23-13

How do we tell the child and parent apart before the exec() call?

  • The child gets a zero value for return
  • The parent gets the child’s PID for return
Read more at the man page of fork()

Example program using fork() #

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

int number = 7;

int main(void)
{
	pid_t pid;
	printf("\nRunning the fork example\n");
	printf("The initial value of number is %d\n", number);

	pid = fork();
	printf("PID is %d\n", pid);
	
	if (pid == 0)
    {
		number *= number;
		printf("\tIn the child, the number is %d -- PID is %d\n", number, pid);
		return 0;
	}
    else if (pid > 0)
    {
		wait(NULL);
		printf("In the parent, the number is %d\n", number);
	}

	return 0;
}

Output

Running the fork example
The initial value of number is 7
PID is 15839
PID is 0
    In the child, the number is 49 -- PID is 0
In the parent, the number is 7

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

int number = 7;

int main(void)
{
	pid_t pid;
	printf("\nRunning the fork example\n");
	printf("The initial value of number is %d\n", number);

	pid = fork();
	printf("PID is %d\n", pid);
	
	if (pid == 0)
    {
		number *= number;
		fork();
		printf("\tIn the child, the number is %d -- PID is %d\n", number, pid);
		return 0;
	}
    else if (pid > 0)
    {
		wait(NULL);
		printf("In the parent, the number is %d\n", number);
	}

	return 0;
}

Output

Running the fork example
The initial value of number is 7
PID is 15847
PID is 0
    In the child, the number is 49 -- PID is 0
    In the child, the number is 49 -- PID is 0
In the parent, the number is 7

We can determine which print line belongs to the child and which belongs to the grandchild by storing the value of the second fork().