 Hello everyone welcome to the sixth lecture in the course design and engineering of computer systems. So, in this lecture we are going to study about how the operating system manages processes. So, in the next few lectures we are going to focus mostly on the operating system which is the building block for some of the later concepts we are going to study in this course. So, let us get started. So, we have already seen what is a process right a process is a running program. You write some program that has some code and data in it and then when you run it it becomes a process. And when you run a program multiple times at different times each different run of the program will be a different process right. So, a running program one instance of a running program is a process. So, we have already seen this concept that the main job of the operating system is to run multiple processes concurrently on the underlying CPU hardware. So, we also say this as the OS virtualizes the CPU across multiple processes. What does this mean? This means that each process thinks it has complete control over the CPU. If you have processes P1, P2, Pn each things it is running on the CPU on its own. Whereas the operating system is involved in multiplexing all of these processes on the same underlying CPU. So, we have also seen a brief overview of what the operating system does in process management right. We have seen that the operating system manages the life cycle of processes. That is it creates processes executes them terminates them. It also has a scheduling policy that decides which process has to run when on the CPU. It also performs context switching. Once the policy decides that this process has to run or that process has to run the operating system is involved in switching the context of the process. And it also implements various system calls. So, these system calls are the services that the operating system provides to user processes and it also handles interrupts right. So, a brief overview of all of this that the operating system does we have seen in the previous lecture. In the next few lectures we are going to go into a lot of detail to understand how exactly the operating system does all of these things. All right. So, now let us come back to our you know definition of what is a process right. Let us understand this in a little bit more detail. So, every process has a unique process identifier or PID. This is sort of like if you are a student this is your role number right. So, this is your unique identifier that every process has in the system. And what else defines a process? Every process has a memory image that is in RAM. A process occupies some memory in RAM and you know the code, data, all of these things of a process. Code, data, stack, heap and various other code, various other pieces of a process are stored in its memory image in RAM right. The other thing that a process has is its execution context on the CPU. So, when the CPU is running a process the program counter is pointing to some address you know the instruction that has to be run next. The various CPU registers have some data stored from the process memory image into temporary storage in the CPU. So, all of this the value of all of these CPU registers constitutes the context of a process right whether it is the program counter or other general purpose registers and so on. So, when a process is executing on the CPU this context is present in the CPU registers. And when we want to stop running this process temporarily when we want to pause this process all of this context will be saved somewhere. So, that when we want to resume the process this context will be restored again right. So, this is how the operating system multiplexes multiple processes by saving the context of a process and restoring it later on. And every process is also defined by some kind of communication with IO devices right. So, a process is you know has some files open on disk it is talking over the network to some other computers all of this information about IO devices is also maintained with respect to the process right. All of these things define a process. So, a process also can exist in one of multiple different states right. An operating system is managing multiple active processes and each of these processes can be in different states or you know situations in its execution right. What are these are states of a process? A process can be running right at any point of time on a CPU core exactly one process is actually running on the CPU. What does it mean? It is the CPU registers contain the context of a process the program counter is pointing to some instruction of the process registers have data from the process right. So, a process can be in a running state and on any CPU core you can only have one process in a running state at any point of time. Next a process can be in what is called a blocked state it is also called a suspended or a sleeping state. What is this? This happens if a process cannot run for some time temporarily but can run again in the future that is called a blocked process. For example, a process has you know made a system call and has you know started reading some data from the disk ok reading data from the disk this is a blocking operation. So, when this happens the operating system will start the command will issue the command to the disk hardware we have seen how this happens but the disk hardware takes a long time to return the data. So, in this meantime this process cannot run right it is the next line of code depends on this data being available. So, the process cannot run. So, in such cases what do we do? This process is blocked right it is suspended or it is put to sleep its context is saved and the operating system will run some other process right. So, that is the blocked state then there is also what is called the ready or the runnable state. So, some processes are ready to run but the operating system can only run one process at a time on a CPU core. So, these ready processes are waiting for their turn to run right they are waiting for the OS scheduler to context switch them in at some point in the future ok. So, note that there can be many ready processes and the scheduler has this choice to pick one of them to run on a CPU core. And as we have already seen if you are a blocked process or a ready process that is not currently running on the CPU the context is saved somewhere in memory. So, that it can be restored later on when the process can be run on the CPU ok. So, I would just like to give you some examples of what are these process state transitions right just taking a simple example of a system which has two processes say P1 and P2 ok. So, initially this process P1 is in running state it is running on the CPU and this process P2 is also ready, but the scheduler has picked P1 for some reason and P1 is running and P2 is ready right. So, now what happens suppose P1 makes a read system call it wants to read some data from the disk right. So, P1 opens a file it wants to read some data from the disk and the operating system handles this operation handles the system call and gives the command to the disk, but the data is not ready. So, in this situation what happens P1 moves into the blocked state ok P1 was running and it moved into the blocked state why because it cannot proceed until the disk data is available and at this point the operating system will switch to process P2 and process P2 will become the running process right. The operating system will save the context of P1 restore the context of P2 and P2 starts to run and after sometime you know after P2 runs for sometime maybe the interrupt from the disk happens the disk says the data is ready it raises an interrupt. At this point when the operating system handles the interrupt the data for P1 is ready P1 is ready to run then at this point P1 can move to the ready state and P2 can continue to run for some more time you know it is still running running and at a later point of time once again the operating system can switch to P1 right. So, it is not necessary that as soon as the interrupt arrives the operating system will drop P2 and switch to P1 this really depends on the scheduler policy right even if P1 has you know obtained its data and it is ready to run operating system can wait and continue to run P2 for some more time and at a later point of time it can switch to P1 right. So, these are some examples of process state transitions where P1 went from running state to a block state when it made a system call then from the block state it went to the ready state once again when the interrupt occurred right and P2 was in a ready state and when P1 got blocked P2's turn came it went into running state right all of these are some examples of how processes move across these various states. So, now as we have seen the operating system has many different processes each of them is doing different things is in different states. So, how does the operating system keep track of all of these processes right. So, for every process the operating system maintains a special data structure called the process control block or PCB ok. So, this is one data structure in which all the information about a process is stored and there will be the several such blocks for the different processes this is the information about process P1 about process P2 about process P3 and so on in some bigger data structure all of these will be maintained. So, for every process we have a PCB and what is all the information that is stored in the PCB about a process we have things like the process identifier, the process state, what state is the process in is it ready to be run is it blocked right the scheduler must know this information when it is trying to do a context switch and various other pointers to other related processes. So, as we will see processes also have some you know parents children and other such relationships and you know information about all the relations of a process is also maintained in the PCB of the process. And the other important thing that the PCB has is the saved CPU context. So, when a process is not running when it is either blocked or it is ready waiting for its turn in such cases the context of the process has to be saved somewhere. So, that it can be restored later right where do we save that we save that in one of the fields of the PCB. So, that this is one place right where all the information about a process is kept track of. So, the CPU context is also saved in the PCB when the process is not running. And the PCB also has other things information related to the memory locations of a process where is the process located in RAM for the OS to you know find it when required and information related to other IO communications there are many other fields in the PCB as we keep studying in the course as we learn more about operating systems you will find more information about the PCB that will come across later in the course. And all of these PCBs of all the processes are stored in some other bigger data structure say it could be a link list an array a heap or whatever right if you have taken a data structures course there are many different ways of organizing multiple pieces of information. And the OS also maintains all the list of active PCBs in some such data structure. And whenever a new process is created a new entry is added you know to this list of PCBs when a process terminates and its memory is deleted its information is deleted then this entry goes away and so on right. So, this is a dynamic data structure that is maintained by the operating system. So, now let us understand the life cycle of processes right how a process is created how do they die all of these information we will begin to study in a moment. So, how a process is created? So, the first process when any system starts the operating system creates the first process which is also called the init process. And from then on all subsequent processes are created by forking from the parent process right. So, you have the init process that is created the init process does what is called a fork and creates another process say P1 and this process P1 once again does a fork at a later point of time creates another process P2 and P1 creates a fork later creates another process P3 then P2 can fork and create P4 and so on right in this way the processes one process forks itself and creates a child process. So, processes are always in this kind of a family tree right you cannot just create a process in isolation it has to come from some parent process by forking from a parent process. And how is this done how does a process create a child process like this there is a system call called the fork system call using which a parent process can create a child process okay. So, this is an example of some code using the fork system call. So, here is some code when the parent process says fork at this point two copies of the parent are created a new child process is created with a new PID but the memory image of the child is exactly the same as the memory image of the parent that is you have this code running in your parent process and when it calls fork another new child process is created but it has the exact same code right the same code and data are there in both the parent and the child right the memory image is copied this is a separate copy you know the child can do its own execution the parent can do its execution but it is an exact copy of the parent right the parent and child run different copies of the same code plus data right after fork okay. So fork is nothing but just duplicate a process create another copy and the parent and child can then run on their copies and both the parent and child after the fork system calls so here the parent was running this code and it does fork at this point right after fork both the parent and the child resume execution at this exact line of code after fork okay so then will they run the exact same code no they do not they both resume after fork but the return value from fork is different okay so this fork system call returns zero in the child and returns a non-zero value returns the PID of the child in the parent right. So the parent and child both have two copies of the exact same code in the parent's code this return value will be the PID of the child it will be a non-zero value and in the child code this return value from fork will be equal to zero right. So whatever code you write in this return value equals zero in this if clause this will be run by the child and whatever code you write here will be run by the parent right. So parent resumes execution with the return value of child PID child resumes execution with the return value of zero and after fork they both run independently and if you change any data in the parent's process it does not impact the child and vice versa right. So this is how any process in your system is created from forking from a previous parent process. So now you might be asking the question isn't it just impractical right I mean to run the exact same code in multiple processes what if I want to you know run a different process I want to run a browser I want to open a computer game right. I don't want to fork an existing process that is already running so that is a valid doubt. Therefore even though a process is created as an exact copy of the parent the child process can actually then go ahead and change its memory image right. So sometimes the child might want to run different code from the parent so it is valid that sometimes the parent and child might do the similar work right suppose if it is a web server process and the parent web server process might want to do more work process more requests and therefore it will you know have a child process that is doing the exact same thing that is valid but sometimes your child might want to do something else in which case we have what is called the exact system call right. So what is the exact system call going back to the same code here the parent has forked a child now both the parent and the child resume execution here the child runs this code and the parent runs this code and you can see the child is now calling the exact system call with some other executable. So all of this code is one executable the child is saying I want to execute some other executable. So what is happening here initially you have your parent process its code is copied into the child process the exact same copy is made and then when you do the exact system call the child removes this memory image and adds its different piece of code a different executable in its memory image right. So that is the exact system call it allows a process to switch to running a different executable a different set of code plus data from what its parent was doing and the exact system call it takes some executable as an argument and it will reinitialize the memory image the code data stack heap everything is reinitialized to run this new program this new executable okay and then the child code no longer runs anything after exact right whatever you put after exact this is gone the child code will not execute any of these things why because the this memory image has been erased and a new memory image has come okay. So this print statement will never run if your exec works fine the only way this print statement will print is if this exec fails if for some reason the child could not reinitialize its memory image then it will go back to whatever memory image it had before the parent's memory image or that's old memory image and it will continue to run that. So in this code if you do exact if this exact works then all of this code is gone right you will never run anything that's there after exec but if the exec fails in the child then it doesn't have a new memory mesh to go to so then it will continue to execute whatever code is there after exec like it will print this error message for example okay. So this is how the exec system call works the fork system call creates a copy of the memory image from parent the child and the exec system call allows a child or any other process to rewrite its memory image with a different program. So now this is how a process is created how it executes any programs and then when a process has finished execution it will call the exit system call okay. So this exit system call will basically terminate the execution of the process it will go back to the operating system and the operating system will find some other process to run right. So this exit system call terminates the process. So this exit is automatically called at the end of main if you have written a program and you wondered wait I have never you know done any exit when you reach the end of your code the exist exit system call is automatically invoked okay but an interesting thing about exit is that when a process exits it does not fully disappear it is not you know removed from the list of active processes and so on but it only becomes a zombie it goes into this you know weird state called a zombie state that is it can no longer run but it is still around as a zombie and what do we do with these zombies the parent has to do another system call called the wait system call which will what is called reap the zombie child that is once the child exits and becomes a zombie the parent has to call another system call called wait which will then clean up this zombie which will fully terminate the process okay. So let us see this example here your parent has done a fork right it has created another child process and this child process has you know its own memory image and here the child returns with the value of 0 the child executes some code here it just prints something and then it exits at this point the child process has finished execution now but the memory and everything of the child process still exists right it no longer runs on the CPU the OS will run some other process but the child is still there as a zombie then at some point you want the parent process to call wait at this point when the parent calls wait in its code this wait system call will block until the child dies and once the child exits that is when this wait system call will return the parent will wait literally that is why it is called the wait system call the parent will wait for the child to finish its job and then the parent can continue and when the parent does this wait that is when the memory of the child process is cleaned up okay if a parent does not do wait then the child has terminated it is not running but it is still existing in the system as a zombie. So why is this complication when a process exits why do not we just you know clean up its memory what is this you know parent has to wait and all of that why do we need this well there are certain subtle reasons why this happens that we cannot you know go over in this course but it turns out that the exiting child cannot clean up its memory on its own okay due to how the memory is set up in operating systems this cannot be done it is not easy to be done therefore when a process exits some other process has to clean up its memory right. So that is why we have the parent perform the wait system call and we know the parent knows that a child exists it has forked a child and therefore it will also call wait on the child process. So a little bit more detail on zombies because this is really important when you build real life large systems right. So this wait system call reaps or you know cleans up the memory of this is also called reaping it reaps one dead child at a time. So if a parent has forked you know a process has forked multiple processes it has to call wait multiple times it has to call wait three times here in order to clean up the memory of all of these three child processes okay. So the next question comes up what if this parent itself has exited right this parent is gone then who will clean up the child. So such children whose parents have exited they are called orphans and these orphans will be adopted by some other process say you know the init process this is the convention. So the init process becomes the guardian or the pseudo parent of these child processes which do not have parents and when this orphan process is terminate then the init process will reap them right. So the zombies are reaped by the init process. So another important thing to notice the init process will only reap the children of other processes only if they are orphans right. But if the parent is still running and a child has terminated the init will not reap the child right. All such children where the parent is still arrive those children are still the responsibility of the parent and the parent must reap them by calling wait okay. If a parent forks a child but does not call wait for a long time then what will happen all of these child processes have terminated but they are still using up memory their memory images are there in RAM they are there in the list of PCBs right the system memory fills up with zombies and this is a very common programming error where your system memory can get exhausted. So this is something to keep in mind when you are building large systems if you as a process as an application you are creating multiple child processes to do some work then you have to reap them after sometime you have to ensure that their memory is cleaned up. So now a simple example let us put all of these concepts together and understand how the shell works right. So every operating system exposes an interface like a shell or a terminal in which you can run user programs for example you know if you run a program like eco hello then you know hello gets printed or if you run your a.out then your you know C program executes and the output from the C program gets printed right. So the shell is a way for users to run their user programs. So what happens when you you know execute a program in a shell or you type a command in a shell what exactly happens this is a good example to understand all of the system calls we have just seen right. So this is a very simple model of how any shell works right it will first read the input command from the user and then it will fork a child process. So this shell will fork a child process whenever the user gives an input it will fork a child process and in this child process it will execute the command right. So fork and then in the child execute whatever command the user has given if it is LS or eco or a.out or all of that is run in the child process and the parent itself what will it do it will simply wait right. So the shell forks a child the child runs exec with whatever program executable or command has been given as an argument to the shell right. So commands like LS or eco all of these are just other programs written by somebody else and compiled and kept for you right like your a.out out and Linux comes with all of these built-in programs that you can directly run. So what shell will do is it will simply fork and call exec with that command as the argument and the parent shell will wait and once this command finishes then wait returns and the shell will you know go back take the next command input run it again next command input and run it right. So this is how your simple terminal works. So you might have a question why does in the shell just directly execute the command itself why is it even forking a child? Well this is a subtle question think about it. So for example if I directly instead of doing fork if I just directly exact the command over here what will happen all of the shell logic will get destroyed and the shell cannot go back and you know display a command prompt and ask the user for the next input again right. So we want the shell process to remain we only want another process to change its code and run some command but we want the shell also to be running therefore we do this fork exec wait logic in the shell right. So this is a simple program a shell is a simple program that you can build using these fork exec wait system calls. So now finally we have all of these active processes and there is a piece of code in the operating system called the OS scheduler which just goes over this list of processes and it will pick processes to run right. So we are going to study this OS scheduler in a lot of detail in a future lecture but for now it is enough to know that what does the scheduler do it you know there is the list of processes in some data structure all the processes are maintained it will go to one process run it for some time stop it save its context jump to another ready process restore its context run it then again save context restore context of another process run it and so on in this way a list of all ready processes in the system are correctly executed by the OS scheduler and you have to understand what is the saving and restoring context right. I hope we have discussed this many times by now and I hope it is clear. So restoring context of a process simply means you just write some values into CPU registers that were saved before. So suppose you were running your program counter was pointing to some instruction before right you were about to execute some code before and then you paused execution then you save this value of the program counter and when you restore it back again what will happen the process will resume execution where you left off right. So registers like program counter other general purpose registers all of those values that were there when the process was paused when you restore those values the process will resume execution right. So this is a basic outline of what an OS scheduler does which we will study in more detail in the coming lectures. So that is all I have for this lecture in this lecture we have seen what is the process abstraction what are the various states of a process like running ready blocked and so on. What is a process control block or a PCB we have seen various system calls how a process is created with fork how it can run any new executable with exec how it terminates and how its memory is cleaned up by the parent right and putting together all of these system calls we have seen how the shell works. So a small exercise for you is you know use commands like top or ps if you are on a Linux machine and you can view all the processes in your system and by giving different arguments to this ps command you can actually see you know what are the various states the process is in right now how much memory is it occupying how much percentage of the CPU is it using right all of this information it is good for you to just open up a terminal run some commands and see for yourself. And as a programming exercise you can also try to write a simple shell right that takes a command and runs the command using these fork exec wait system calls you can build a simple shell for yourself to understand these concepts better. So that is all I have for this lecture thank you all and see you in the next lecture.