 All right welcome back. So no snow day for us unfortunately which is kind of weird because even Waterloo got a snow day but they announced it like 6 p.m. so that kind of sucks for students. Really unfortunate how the university lets you commute all the way here and then it's like yeah snow day don't worry about it. Helpful. All right so today we are doing some advanced scheduling and I was keeping lab due Monday at midnight hopefully that's coming along well if you haven't at least set up the environment I would suggest you do that like immediately after the course just in our class especially if you have Windows 10 because some weird things might happen. But we'll probably have some time after class if there's any general questions about processes or anything like that because you will be encountering them more on lab 2. So we will talk today about advanced scheduling and what you do with multiple CPUs. But before even having multiple CPUs one of the things we could add are priorities which seem like a good idea because some processes you might care about more than others even on a single core CPU system so you could assign each of them a priority and then use that to enforce some type of ordering on them. So if we had round robin you know we could just always run the higher processes first whatever the priority whatever higher means either lower or higher number we'll get into that and then you can round robin between processes of equal priority but always always take the higher priority processes over the load lower priority ones and you could do this also in a preemptive way or non preemptive way. So typically a priority is just assigned an integer or a number so you can pick a lower or higher number to mean high priority and confusingly Linux actually picks both so we'll get into why they do that and kind of the mess of it. So in Linux for normal processes the lower number means it has a higher priority means it runs ahead of the other ones. So negative 20 is the highest priority so if all else being equal it would run that first and yeah 19 is the lowest priority which is if I have some spare time I'll go ahead and run you. So because of priorities while we also have that case of there's starvation if there's a lot of higher priority processes and you just prefer to run higher priority over lower priority then you have a case where you could have starvation which is just some process that might not ever be able to execute. Essentially it has like an infinite it's possible for it to have an infinite response time. So one solution you could do to combat that starvation problem is you could have the OS like dynamically change the priority so if a low priority process hasn't been running for a very long time well you'll temporarily bump it up as a high priority process until it finally gets its turn and then it finally gets to run so then you can bump it back down to its original priority. But if you have something like priorities there's this new issue called priority inversion which essentially means you accidentally change the priority of a low priority process to a high one. So this is caused by dependencies so if you have a high priority process depending on a low priority process well the task you depend on should be treated as high priority since it's actually preventing the high priority one from running so the solution to do that is something called priority inheritance so if there is some type of dependency between two processes you would take the highest priority between them in order to resolve that dependency and then when that dependency is resolved you can go ahead and knock them back down to their original priority. And this is possible because the kernel is the thing controlling all the processes so all the inner process communication which would be where the dependencies between processes come from well the kernel knows about all of them and who is talking to who and everything so it is actually able to do this and change priorities dynamically and actually check to see if there are some dependents. So this can happen recursively too so if A depends on B B depends on C C depends on D well I could go ahead and make all the processes A through D whatever the highest priority is between all of them so you could do this recursively too. And yeah so there's a question can you explain what inheritance is and inheritance is just you get the value of all the other things so you just inherit so you just get the value of the rest of the things so yeah so if you have a high priority and you're a low priority so you would inherit the highest priority or you just get the highest priority it's just another way to say that. So there's also a thing you can do to differentiate between you know what you might want to have good response time and what you want and processes you might want just good throughput on. So one thing that was easier to do in the past when you had a dumb terminal is there's a thing called a foreground process which your terminal is connected to which you type in the keyboard and get responses back so that would be called a foreground process that you interact with so that can receive user input and a background task just runs in the back you can't you don't really interact with it it's doing some computation and it's doing whatever. So in Unix you know you can background a process when the process group ID differs from its terminal group ID that's like really low-level stuff we don't need to know that for lab 2 you don't need to know that you yeah you don't need to know that but that's like the definition. So the basic idea is to separate which processes users interact with so foreground processes are things users interact with that need good response time when you click on something you want to you want to see whatever you changed and then background spawn background processes you don't really care about response time you just want good throughput you want to do as much computation as physically possible. So what we could do is you could have multiple cues and different scheduling strategies depending on what the process is so what you could do is hey if you actually can separate out foreground and background tasks well maybe you just use different algorithms for both of them so for foreground tasks I'll just use round robin because that has good response time assuming I pick a good quanta length and for the rest of the tasks I'll just do first come first serve get the most throughput I can have then not waste time context switching between processes especially if this is like a very low-powered machine the context switch may take a lot of time so maybe you just don't even bother for background tasks you just try and complete them as soon as possible. So now you have some you have two different algorithms depending on if your process is like a foreground process or a background process so then you have to schedule between both of them so maybe you round robin between them so maybe I'll do a round robin I'll do a bit of foreground tasks and then that would round robin between those and then after a little bit of time I'll switch and then I'll do some background tasks so you could split off you could have different quanta lengths for each of them too there's really there's really no one true answer to scheduling it's all just different trade-offs and you could also use a priority for each cue you could just stack these together make them as complicated as you want so yeah like I said just scheduling there's no right answer it can be as complicated as you want there's only trade-offs there's just heuristics which are just educated guesses just a fancy word for educated guesses that you can make that just makes things feel better and so far we haven't talked about multiprocessor scheduling yet so we're going to assume something called symmetric multi-processing or SMP and that is basically a model where all the CPUs or all the CPU cores are connected to the same physical memory so they can all see the same memory and the CPUs all have some type of their own private cache at least that like L1 or L2 if you've heard I imagine you've taken architecture courses before so those would be per CPU private kind of like processes and then the largest cache may be shared but the point of caches is you don't see them anyways and then they all see the same view of memory so that actually completes our string if we go to our terminal and you do something like uname dash a which tells you all about the kernel you're running we can now actually kind of understand everything about because scheduling so important it actually tells you about it so this just tells you what the kernel is and then your computer name what version is running and then you can see that's the model SMP symmetric multiprocessing so we know what that means now and it also says it's preemptible so that's what preempt means so it's scheduling so important it actually tells you about it and the rest of it is like that's when the kernel was built and that's the architecture it runs on and that's the full name of the operating system so we actually understand the whole version string now of the Linux kernel so that's good so if we have multiple CPU cores or multiple CPUs well pretty much multiple cores what approach you can do is just use the same scheduling for all CPUs so you don't treat anything different so you just have your one round robin scheduler but the only addition is you just keep on scheduling tasks while there's still CPU cores that are sitting around idle so in that case where i had four processes well if i have four CPUs i don't have to round robin between them because i could pick them off my queue assign each one to its own CPU and then just kind of let them go so the advantage of this is well you have good CPU utilization so it if there's a process to schedule it will schedule it on any idle CPU so the idea is no CPU is idle as long as there's some work to do and this is also fair to all the processes because it would you know use the same scheduling algorithm so we're not fundamentally changing everything we just have more things to run on which is great but there's some disadvantages of this so it's not scalable so everything blocks on the global scheduler so we'll see this more when we get into more more running multiple processes and more running essentially multiple virtual CPUs within a process but because all them are using one scheduler they all have to communicate together so they don't you know they don't screw up so think of like you shuffling a deck of cards with your friend and all you do is just kind of randomly throw out cards and you want to make them in order well if you don't have some type of algorithm or some type of way to synchronize between each other it's probably going to be a mess and probably take longer than it would if you just did it by yourself so it doesn't take that in consideration there's has to be a lot of synchronization between the CPUs and it also has really poor cash locality because we'd be using round robin well if we schedule process one on CPU you know like core one well whenever we go through the next round of round robin we might not actually schedule it on the same CPU core so all of the things that were cashed for that process that were private to that core might just would be completely obliterated now because we'd be running it again say on core three the next time it comes up so you ideally don't want to do that you want to maintain your caches so everything is nice and fast so it essentially doesn't consider what CPU core anything runs in in order to make things go faster but back in the day this was the actual scheduling algorithm used in Linux 2.4 which is really really old now since we're on like Linux 6.0 something although the version number doesn't mean anything anymore usually for software the first number means like something major happened so like if Linux went from you know five to six that should mean something really important happened some killer feature or they broke something well they just kind of increment the version every so often now and if the lead developer run like they increment the number such that he runs out of fingers and toes to count it he just increments the other number so it's literally meaningless yeah it's that arbitrary so if you look up the release day of links 2.4 I don't know when that was probably way before multi-core CPUs were common probably like you know 20 years ago um so the next thing you could do is create per CPU schedulers so you have a whole scheduler just dedicated for one CPU so when there's a new process your job is you just assign it to CPU and then that process will always run on that CPU core no matter what so so for instance like if we did round robin and you had four CPU cores each CPU core would have its own round robin scheduler and then when a new process comes in you just assign that process to a core and then it just gets round robin on that core over and over again so this is also pretty easy to implement if we can do it once we can do it one time for each CPU core that's not bad it's also scalable because there's no dependencies between CPU cores you just get the same set of processes and you manage your own set of processes and you don't have to talk to anyone else and there's also good cash locality because that same process is always going to run on that CPU so even if you switch in and out a lot of the cash is going to be maintained so nothing you know you're not going to obliterate your cashes completely the disadvantages of though is that well there's really you could get really really unlucky and have really bad load imbalance so if you think of the case where like you have four CPU cores and say I don't know four or like eight processes come in and you assign you know two to each but on the first CPU core say both tasks are done in like two milliseconds well and the other ones run for like seconds and seconds and nothing else comes in well that one CPU core that happened to get the shortest processes is just going to sit there idle and not do anything so you're going to waste some performance there so the idea there's a fairly easy solution to that well we could make some compromise where we do some synchronization between different CPU cores in order to actually utilize all our cores if a situation like that handles so you could keep a global scheduler and its only job is to rebalance per CPU cores so if it sees a CPU is idle and not doing anything well it could just take a process from another CPU it could just steal that and move it to the idle CPU so it has something to do and that's just called work stealing you can think of it as it's just moving a process to the idle CPU so you're going yep yep so when you move so when you move a process to another CPU core while all the register values would be the same just running on another you know another CPU core and if it's executing instructions it's reading that from memory right so if you have a process running you know it would cache like a lot of the code and then if you switch it to another CPU it wouldn't have any of that code cache for memory anymore so it would have to as soon as it executed it moves and then as soon as it executes the next instruction it'd be like well that's not in my cache so i have to go read that program from memory again and load it onto this CPU core yeah yeah you assume each core has its own cache at least for like the lowest levels like l1 l2 something like that most modern cpus now will have like l3 like a shared l3 cache so it's not as bad but you're still losing some cache all right any other questions about that it's a good question yep so the scheduler is always running in the kernel so it's in kernel mode so it's deciding what process runs when and where so the kernel is kind of controlling everything so it mostly tries to get out of the way but it has control over every core so we'll see mechanisms for that but it essentially has control over every core so if your process is running it can it can stop your process whenever it feels like and that's typically what happens so the kernel always maintains control over all cpu cores uh yep oh no no question not really okay yeah so we'll see how like the mechanism of the kernel maintaining control is and in lab probably three you'll also be doing something similar to what the kernel does where you'll implement some user threads and you will maintain control of everything so we'll see how to do that um yeah so you you might want more control over what processes could switch so this essentially means if a cpu core is idle then the it's just going to randomly decide to steal a process from another cpu core that has multiples and just move it to this one and you might want to have some control over that so that's what something called processor affinity is so that's just a fancy name of saying the preference of process has to be scheduled on the same core so if a process has a high affinity that means it really really really wants to stay on the same core and if you're like in windows task manager you can actually set you know you can actually see the processor affinity and set that so if you had something like a game you really really care about to be really really high performance well you could set processor affinity to high on that process and that means it won't swap between cpu cores you'll get that good casual quality and other things performance will suffer so yeah and then question how does a process actually get moved to a different core like is there something at a higher level that controls the cpu cores yeah so how it would actually get moved is each cpu core essentially has its own set of registers and that's like the whole state of the cpu so if you have control over the cpu's and i just stop one that's running it would have a program counter all your register values and everything well if i just copied whatever values were in that registers and just put them on a different core and then told that core to start executing while one of the registers is a program counter so it would just happily continue as if you know it would just continue executing your program as if nothing's different because you don't actually care what core your program's running as so if you just take a cpu core copy all the register values and then just uh and then just load them on another cpu core and tell it to continue executing just keeps on going yep so no they wouldn't transfer cache information it would all just get repopulated when it starts executing again so you would yeah you wouldn't bother transferring cache to because some of it you might not ever use again and it would just essentially make things slower because you'd have to wait for that cache to be transferred and everything too so it's easier just to let it execute again and let it repopulate whatever it needs and then question uh but what actually does that copying so the kernel is the thing that does that copying so the kernel can so the full thing it could stop your process from running it would have all of its register values it could save them on the stack which we'll get into at a later lecture and then swap whatever and then just load them on another cpu core and then it could just start executing again and the kernel is the thing that has ultimate control so it can you know it can change any register values it can do whatever it wants to the cpu and you'll get much more familiar with this in lab three as well so the kernel is the thing that controls everything all the register values will be copied there and you'll be doing essentially like a pseudo version of what the kernel does in lab three okay so this one with job stealing this scheduler with job stealing is essentially the scheduler in Linux 2.6 so we're getting a little bit more modern here because i think 2.6 actually survived for like 10 years or something like that so it's called the o1 scheduler and this is basically what it is okay and if you ever get into like high performance computing there's also something called gang scheduling don't ask me why they called it that but sometimes you want multiple processes to be scheduled as one big unit hence the word gang i don't know why they use that instead of like group or something but whatever so the scheduler cannot just treat each cpu as being independent each each process needs to be context switched in at the same time so this is just called gang scheduling or co-scheduling so it allows you to run a set of processes simultaneously that just acts as one big unit so this would require a global context switch across every single cpu at the whole time and you'll only see this in like high performance computing but it's a worthy thing to mention so there's also something else called real-time scheduling which you might care about if you do any embedded work where you have like a really simple computer that is managing something that you really care about like i don't know like the brakes to a car or a train or something like that so real-time all that means is that there's like time constraints like a deadline or rate so audio is an example of real-time so you hear audio signals at a certain rate so you have to be able to output them at that same rate otherwise if you fall behind you'll hear like scratches and other distortion and other you know things that the signals props would really hate and another thing would be like autopilot or something like that like you have to make a decision within a certain time frame or otherwise you know you'll hit that kid in front of you which isn't good so there's two different times of real-time there's hard real-time which means you have to absolutely guarantee a task completes within a certain amount of time and this is only really feasible on fairly simple CPUs where you can actually count the number of clock cycles and how long the clock is so you can get the timing down to like the microseconds and you're absolutely certain that it will happen within a certain time frame then there's another thing called soft real-time which just means critical processes have a high priority and the deadlines met in practice because it can't guarantee anything because the system is essentially way too complicated so that would be an example of like the Linux kernel so your CPU at 5 gigahertz can clearly do audio stuff fairly well but there's no way to absolutely 100 guarantee things because the kernel has so many different things that it does that you could theoretically be insanely unlucky and you would miss that deadline but in practice it's always met so Linux is would be example of soft real-time unless you like fully analyze all of like the tens of millions of lines of code it can guarantee no matter what happens everything will execute within you know a few microseconds so really not feasible only like really embedded CPUs you can do that on and that probably controls something important so we talked about first come first serve and round robin as being like toys but they're not the Linux kernel implements them so you can search the source tree and read the source code if you really want they just kind of name them different things so first come first serve they call FIFO and they just prefix everything by sketch short for scheduling or schedule and then they have round robin so it's just sketch underscore rr so it's actually used actually implemented in the kernel and that's for what we'll see is like the more real-time task where you have to have something that's more consistent so what they use is a multi-level queue scheduler for processes with the same priority so everything with the same priority it would either use FIFO or round robin and then the operating system can again dynamically adjust the priority if it needs to so now the Linux kernel has two types of processes so there's soft real-time processes and normal processes so the soft real-time processes will be scheduled either on FIFO or round robin and they always do highest priority first and the idea is they typically they do something that's really really critical but that doesn't take that much amount of time and you want to be consistent so you're not going to do any like fancy scheduling or anything for it and then normal processes will the like 10 000 foot view of it is is the normal processes will adjust the priority based off aging more or less and we'll see what it actually does but it's a bit weird so real-time processes will always be prioritized the soft real-time scheduling there's different priority numbers depending on what it is for historic reasons don't ask me why so for soft real-time the scheduling policy is either first come first serve FIFO or round robin and there's 100 static priority levels 0 to 99 where the higher priority means it's the higher number so if I have priority 99 and a 0 I'm always going to run 99 over 0 now for normal processes that everyone is running well they would be given the sketch normal thing which you know there's different scheduling algorithms for normal processes and we'll see the the actual one in a little bit but they'll be given the sketch normal and their default priority is a zero and their range is from negative 20 to 19 where the lower number means higher priority why this number don't ask me but you can actually do system calls to adjust your processes own priority so there's a system call called nice which will adjust your priority so that number negative 20 to 19 is called your niceness number instead of a priority because they already use the word priority for that 0 to 99 one so that's niceness so you can use nice to set your scheduling policy you can also set the scheduler through system call so if you really want to you could adjust the priority of your processes to be real time and you know you'll hog the CPU which is great right so so we can go ahead and explore a little bit and we can actually understand what these numbers are so we saw the PID you did in lab one or you are currently doing and then the name and then the user running the process and then the commands all the way at the right there then the first number here this PIR that's the priority so that's that normal that's like supposed to be a uniform line and i'll show what that means in a bit and then the other one is this niceness so it's ni so that means you know it goes from negative 20 to 19 and if it's within that range it's a normal process the default value of that is 0 so all these processes here just have a default niceness of zero all in this column here and if we go through them we see that there's a bunch of zero zero zero nothing important there so there's some we can see that are less important because they have a niceness number of 19 19 means they are really low priority and they're things like i don't my shell or things are just waiting around not doing anything so we can get a little bit of insight into that see if we can find something more interesting there is a lot of things running on this there's good old vs code using 43 gigs of memory obviously that's not super possible and we'll figure out why it says things as silly as that before but yeah visual studio using 46 gigs of ram always good in multiple multiple copies of itself so each one of those is using 36 gigs of ram too so you know my laptop is really really good obviously okay so there's one that is a 10 so that's one thing of visual studio that decided to be lower priority yeah yeah so you'll notice that this niceness and priority number are literally just uh essentially priority is just niceness plus 20 all the time so i'll show that as soon as i find one that is not a normal process because there is one here yeah yeah so so this priority is not that normal priority so that zero to 99 with you know the higher being more priority that's unfortunately not what that priority means so this is like a unified priority and we'll see that in a second let's see i found oh i found one okay so there's some lower ones and then you'll see here this priority just says rt which is real time so that would essentially have a priority of 99 but the priority there is some weird unified linux priority and i'll show you what it is so so everything on the right here so let's make that bigger so everything on the right here that's our niceness so that's our minus 20 to 19 number and then here is our old priority soft real time and i inverted the scale so it would generally be you know zero is the lowest priority 99 is the highest well the linux priority tries to unify these things across one number line so what they did is offset it by 20 so that's why you notice that it was always offset by 20 so their idea was okay well on our unified priority line we will make it such that a normal process will have a unified priority of zero or higher and a old real time soft real time process it will be negative and in order to map them well for normal processes it will just add 20 to it so negative 20 becomes zero and 19 becomes 39 and then for the zero to 99 priority where a higher number is a higher priority all they did was invert it so they just took the negative so before if you had an old priority of 99 as soft real time it would change into a negative 100 so that's all they do so if it was zero it'd be negative one yep this is the os actually using it so the one at the bottom the unified one is the os actually using it so the kernel cares about the priority one there so if i go back so i'll see here that if my priority there is positive it means it's a normal it's a normal process and i can actually just read the niceness if i want to so oh i lost it so yeah so in this case there's a few real times annoyingly real time just means negative 100 or 99 in the old priority and then we can see here there's a few there's some here that are negative 51 so negative 51 means it's soft real time and it would have been just a priority of like 50 back in that if you go back to the old ones and you know them inventing this priority number for linux to try and unify everything should makes everything easier right everything's unified and it's kind of like that xkcd comic of like you know there are two competing standards so let's make a third one to unify everything and now there's three competing standards so that's pretty much what that is so we can explore here so there's some there's actually some soft real time processes running on your machine you can kind of guess what they do not really their names are kind of weird watchdog is a watchdog we won't know what that is but that is something on your cpu and i don't think there's anything terribly interesting yeah so there's nothing else terribly interesting okay so this was the kind of evolution of the linux scheduler so you know back in the olden days when multi-core cpus weren't a thing it had that global queue which was just the same scheduling algorithm that just you know if there's an idle cpu it would just put you know schedule the process on that then they came along later and had per cpu schedulers so that was called the old one scheduler and that was a bit more complex to get right oh yeah there's a link to the xkcd comics so that's good so it was a bit complex to get right all that synchronization was kind of a pain and interactivity had some issues if you you know swapped over processes which didn't need to be performance really suffered and it was also not fair because it really depended on whatever process you just stole so what they use now which has been in place for a long time now since 2.6.23 you can look up the release date of that it's fairly long ago something called the completely fair scheduler so as the name implies it is fair and allows for good interactivity so to first get into why they did that that old one scheduler also has more problems with modern processors so back in the day when you could actually figure out what was foreground and background that was pretty easy you know it's easier with the terminal less so with GUI processes because things you would consider foreground the machine wouldn't have any idea like you might think oh maybe the window I have focused is the foreground one but at least most of you at least I know I have multiple windows open that I'm all watching I don't want any of them to be slow so they're all essentially foreground tasks so it's harder to figure out so the kernel has to detect if you do that it would just have to have some heuristic which are just educated guesses to figure out what you care about like for instance processes that do sleep a lot might be more interactive because they're just responding to user input and just doing something quick so maybe that has to you know maybe that's a user process and that should have better response time but that's just again guesses could be completely unfair so how would we introduce some fairness for different priority processes well what you can do is use different size time slices so instead of that just one time slice well I'll just have a different size time slice depending on if I care about the process or not and you can also adjust it based off the priority so instead of using priority to completely starve a process well I could have priority mean I give you a larger time slice instead of I just cut you off completely so what they do is higher priority just means larger the time slice so there's also situations where this could be unfair but it pretty much works but it ideally works better so the idea behind it is you know they just took the ideal fail scheduling so if you do a thought experiment and you assume that you have like an infinitely small time slice well if you have end processes the completely fair thing to do would just give them each you know one over end that CPU core so if you have one process it would just take up the whole all your CPU time and if you have three processes three processes they would all get a third of the CPU time so that would be fair everything's divided equally so for example if we just had four processes and they all arrived at time zero and their burst times were like eight four sixteen and four well how I could schedule them if I was completely fair and didn't care about context switches at all I could just assume each vertical slice here is four time units and I can give that time unit between the processes as I see fit so for the first block of four time units they all want to execute so I let them all execute for four time unit or one time unit each so they each get one fourth of the CPU then this would happen you know this would be four to eight time units they each get to go again for one more time unit then again then again now at time 16 when everything's divided by four process two and four are now done executing so I only have two processes left so if I want to be completely fair to the last two processes left well I would give them each two time units now or exactly half of that four so they would each increase from you know would each get two time units so they would go from four to six and then they're not done yet so I want to be fair and I would just schedule them again they each get half of that four block of time so they each get another two time units then process one's done and now all those four time units can just be dedicated to process three so it would go from eight to 12 then 12 to 16 and that that would be done so that would be like the fairest thing to do yep so the zero 16 24 32 are time units so each each group of boxes is essentially four time units so this would be zero to four here and I give each process one time unit from zero to four yeah no so they need four time units to execute right each of those processes and in the first four time units here if I'm being completely fair every process runs for one time unit so this first yeah so maybe I should have put more numbers on so this is zero to four so within zero to four each runs for one and then within four to eight each runs for another one so they've all run for a total of two and then here when you go up to 16 well each process if you're being fair you have 16 units of time and four processes while the fairest thing each of them runs for four time units and then they're done okay so that's a fair policy but also really really impractical so every process gets an equal amount of CPU time boost interactivity because of course we're assuming that like our time slices are infinitely small which would have the ideal response time but that's not feasible that'd be way too many context switches and you'd also have to like constantly scan all the processes which is big o n which is linear which is really really bad so you have to do a search every time and it's just awful so what they did is something called the completely fair scheduler so how it works is this so for each runnable process they have a virtual runtime and at each scheduling point where the process runs for some time unit you just essentially increase its virtual runtime so you're just counting for each process you're essentially accounting for how long it executes for and this virtual runtime is weighted by the priority so if you have a if you have a higher priority which means it's a lower number then that means when you get a time slice i contract that number and that is the time that counts against you so if you have a higher priority your virtual runtime increases slower than if you add a higher priority and that's how they do it so the virtual runtime it's a number that monotonically increases that's just a fancy math word all that means is it never decreases it only increases or stays the same so it never gets subtracted it's just an accounting you're just accounting for how long a process runs for so if you do something like this the scheduler has a really simple decision to make all it does is select the process that has the lowest virtual runtime so it's just the minimum of every single process that's what i select to run and you compute its dynamic time slice based off what it would be like if you had an ideal fare scheduler so you just pick how many see or you know how many processes are running on your machine so you make a time slice based off how many processes are currently running so this allows the process to run once its time slice is done you just repeat this process you retire it you keep track of you you know keep track of it's virtual runtime so it got the cpu course so you just increase it a bit and then you pick the next lowest virtual runtime and that's implemented with red black trees so if you learn this in an algorithm course here's where it's actually used so it's a red red black tree if you haven't done it before self-balancing binary search tree and the keys in this red black tree would be the virtual runtime so we have log n insertion updation and insertion deletion and update so whenever process is created whenever it dies and whenever we need to re-add it to the tree and then to make our scheduling decision it's just 01 constant time so we just find the minimum value so the implementation uses this red black tree and virtual runtime is accounted for in nanoseconds and nanoseconds are quite small so that's like a billionth of a second so you don't need to guess the interactivity of a process and this actually works very very well so what does is it tends to favor iobound processes by default because they'd be given a time slice that you know they'd be given their time slice and they wouldn't even use all of it they would just execute essentially do like a read system call or something like that that needs to wait for a slow device so it wouldn't use very much of its virtual runtime so whenever that data is ready again and that process can be scheduled well it would have increased its virtual runtime much so when it's done with doing if when the curl is done doing whatever device thing it needed to do it would get scheduled pretty much immediately again and have that good interactivity so the slope and that essentially just is another way of saying the CPU just has short little bursts of time so that wouldn't increase the virtual runtime very much and you would likely get scheduled again whenever that data is ready because you know you'd be falling further and further behind so if you did like the ideal fair thing where you share the CPU equally with everything well you know you haven't shared the CPU equally you're behind so you get it so that's to wrap it up scheduling this is this complicated scheduling git you can add stuff on top but it's fairly ad hoc and this is kind of as complex as it gets so we saw that if we introduced priority we introduced a problem called priority inversion and then we saw that some processes might there's like competing goals some processes might want good interactivity which means good response time and others might just care about throughput i want to go as fast as possible i don't even want a context switch just let me go then we saw if we have multiple cpus well then we might want to do something like per cpu queues per cpu schedulers then we saw some complications from real time that requires some predictability so that means you have a time constraint you must always meet then we saw the actual scheduling algorithm or the completely fair scheduler and that tries to model ideal fairness so yeah one more lecture to the lab so just remember pulling for you we're all in this together