 So we'll talk about how the operating system interacts with the CPU. We'll talk about CPU mechanisms that exist to allow the operating system to do things it needs to do. We'll talk about some of the abstractions that the operating system creates to allow processes and other parts of the system to use the CPU and the illusions that the operating system is able to create about the CPU. And then we'll talk a little bit at the end about policies related to the CPU specifically about process scheduling. OK, so I posted the first three assignments on the website. The submission is not open for many questions, but if you guys kind of want to work ahead, it should give you a feeling for what's coming. So we're working on sort of getting the automatic grader set up. Once that's running, that will be what will grade your script and implementation submissions. For the code, so in general, for the questions we're asking you guys to submit through the website in order to get credit for the assignments, there's this sort of multi-step process that involves, potentially, if you want to, saving an answer. This allows you to kind of save your work and not necessarily submit right away. And then when you're ready to have your answer graded, you hit the submit button, and that will do one of two things. If the auto grader is involved, that'll kind of wake up the auto grader and tell it that you have something to grade, and that process might take a few hours, depending on what else is in the queue. If we're, as much as we would love to, there's certain questions that we can't grade using scripts, like your code reading answers and design documents for later assignments. Those will have to be graded by the core staff. So in that case, submission asks us to look at those things, and then the latency might be even longer, like several days maybe. Once your questions are graded, you'll have a chance to look at your score. And potentially, and I haven't decided about this yet, for some of the code reading and design documents, we may give you guys like two shots. So we may say, here's the answer you submitted, here's the grade we assigned. If you would like to keep that grade, you'll be able to finalize your score, which will show you the answers. So over the code reading questions, they're all, we have the correct answers all in the website somewhere, but clearly, we can't show them to you until you're willing to accept whatever score we gave you for those questions. But there might be, we might give you guys another shot at those. So there might be a chance where we say, okay, this is wrong. If you guys wanna go and keep thinking about it or go look in the code again and get it right, then you have that shot. Otherwise, you can hit finalize and then you take the score that you have and we show you what the answer is. Does that make sense? Okay, we probably aren't gonna give you like six or 10 tries on the code reading questions just because we're gonna get sick and tired of grading them, right? So it might be like two, okay? Same thing with the design documents, but we'll see. For the scripts and for the implementation, problems probably what we're gonna do is those won't be finalized until like a couple days before the end of the semester and then we'll just finalize everything at once, right? So those you are welcome to submit as many times as you want. There's no overhead to the core staff for grading. All right, and then any bugs you find on the website, please report on Piazza or that is one of the things that you can use the staff email list for. I've been trying to direct people off the staff email list onto Piazza because it's a better place for those sorts of things, especially when they're questions related to material or things on the assignment. If you send it to staff, then it's gonna sit there for six hours until we remember to look at it and then probably we'll tell you to post it on Piazza anyway. If you put it up on Piazza, another student might answer the question or that the TAs will get to it faster, okay? Questions about this stuff before we go on? All right, so we're pretty much done talking about the process abstraction. Does anyone have any questions before we do our, before we would go to the review round? Questions about processes? All right, so processes contain things that abstract core system components, CPU, memory, and the disk. So what specific parts of the process are used to abstract the CPU art? Threads, great. What about memory, Tim? What were we calling that rectangular thing that had some different areas lined up? Yeah, Jude. Address spaces, what about disk? We talked more about what we were using for the disk because it's Spencer? No, it's not Spencer clearly because he's not looking at me. File handles, what's your name? Amit, file handles, all right. One or more threads, right? An address space and a file tabbing map, mapping file descriptors, right? Which are those ints to file handles, which are the objects that are shared after fork, all right? System call interface, components of the interface that are used to create processes. You guys will have a chance to implement all four of these system calls for assignment two. How do I create a new process, fork? How do I replace a running process with an executable image, frame, exec, all right? What about exit a running process? These are probably some of the easier questions that I'm going to ask all term. AJ, ooh, return. Exit, exit a running process. Collect the executable of a child. Looking for someone who has an answer. Sam, wait, all right, good. File handles and the file table. So semantics of fork and exec. And again, these are kind of canonical semantics. There are variants of these system calls that do different things. But what does fork do to the process system table, Jeremy? Yeah? It makes a copy of it, which means that the parent and child are now sharing file handles, because my file table contains file descriptors that are references to file handles. So after I copy it, I'm sharing file handles, right? What about exec? What does exec do, Jim? So that's what exec does to the process image. But what does it do to the file table specifically? Thomas? Yeah? Nothing. Nothing. Just leaves it alone. These slides, if people are following along the last year's slides, the answers are all up there, so keep that in mind. So why do I have these sort of canonical semantics with respect to the file system system calls? What is this allowed? What is this designed to enable Sean? Someone want to help him out? Change starts with the right letter, but it's not the answer that I want. Surup. It's a rude introduction to class. This is what happens when you show up late. We're in the middle of the slide. What explains the semantics of fork and exec with respect to the process file table? Why are the semantics set up the way they are? What is this designed to allow the child? Jen? Yes, this is for IPC. Jen? All right. It's to enable inter-process communication. OK. So I told you that we would write this little simple shell, and this is it, essentially. This is not review. I don't think we looked at the slide last time. This is pretty much all we had left to do, so again, with our little C-like pseudocode, this is probably the simplest possible shell that you could write. This is a shell that doesn't do anything special, doesn't put prompts, but essentially just reads lines of input, calls fork. So in this particular shell, who runs exec and who runs wait? One way. Yeah, could I do this the other way around? Could I have the parent run exec and the child run wait? Would that work? Why not? I mean, I've got two processes afterwards. Jeremy, I'm ignoring you. I'm ignoring you too. Yeah. Right, right, because what is the return code here in this branch? I mean, it's in the case statement, so it's zero. OK. Where is exit? So of our family of process-related system calls, we see three up here on the slide. Where is exit? Why is exit? Who runs exit? Exit's involved, but where is exit? No, because the parent's here running wait. So who runs exit? Sean. Yeah, the new process that's forked has to run exit before wait will return. The assumption here is that this is a blocking call to wait, meaning that it will not return until the child calls exit. Questions about processes? Yeah. What do people think? So this is a great question. When I open a new file, I create the new file handle if it does not exist already. In fact, open always creates a file handle. Open creates a file handle, and then it creates a file descriptor in my file table pointing to that file handle. So that file handle will not be shared with my parent. Again, the idea behind the semantics of fork and exec with respect to the file table is I give the parent and child some way to communicate, but I don't lock them into this forced civility forever, like they can get away from each other. So again, let's say I'm the child, and for whatever reason, I want to have the same files open as my parent, but I don't wanna share the file handles. What can I do? Ignoring you, Jeremy. Yeah, so I can have the child go through its file table, close all those file handles, and just reopen the same file. So figure out the files I had open, close all the existing file handles, reopen then those files, and now the parent and child have the same files open, but they will not share offsets. Potentially, the file descriptors will be different too. That really depends on how the file descriptor allocation is done. So it could be that the parent and child both have the same files open, but their file descriptors are different, and their file handles will definitely not be shared. You guys will get a chance to work this out when you do assign it to, so you'll get some boots on the ground knowledge about this. Any other questions about processes? Is it a good one? Yeah, that's a good question. I think there might be cases, I'd have to look at this. I think the C library, for example, it's Akshay, right? The C library might install a signal handler that handles the signal that's generated by the child when it dies by just calling wait, right? The idea is the parent will get a signal when the child exits, and by default it's possible that the C library for C programs will install a signal handler that just calls wait immediately, and discards the exit code, right? But the idea is I don't wanna leave these zombies around, because if I don't, until the parent calls X that I had the zombie process, and I think the thing that prevents that normally is that normal processes are set up with the signal handler that will do that. So if, and again, that's just a nice way of sort of keeping things clean and keeping things. The other thing is if the parent dies, then the child is reparented to in it, and in it will just sit there calling wait, right? And it will figure out every, and it will receive those signals that just call wait over and over again, right? Those return codes are just discovered, yeah. Sure, yeah, I mean fork can fail. Yeah, fork frequently requires a fair amount of resources, and fork can definitely fail. So if fork fails, the idea is that I just return to the, if fork failed here, right, and again, this is why this isn't a great example, it would return a return code that would be not zero, probably negative, right? So I think it returns a negative return code on error, right, so what I would really need to do here is check if the return code is zero, I call exact, if their return code is greater than zero, I call wait, if the return code is less than zero, then I fail, then I need to do something about it. You're just twisting your hand in the error suggestively, guru. Yeah, oh, yeah, okay. So there is, I tried to slide on this or not, I wish I did. Yeah, I forgot to copy it over from last year's. There is a, I don't wanna get into this in great detail, but the C, so if you guys have programmed in C, you've used some of these calls, read, write, open, close, exact, fork, that are part of the system call interface. The functions that you use are frequently wrappers around the system calls that are provided by the C library. So when you call fork, you're actually not calling SysFork, you're calling a function in the standard library that eventually calls SysFork, right, and that function knows how to trap into the kernel, in fact, that's a good segue into what we're gonna talk about today, because so you guys may have wondered, I mean, how do I make function calls into the kernel in the first place, right? I mean, the kernel is not a library, right? The operating system is not a library that I can just make a function call into in the normal way. Today, we will talk about how I cross the privilege boundary between an unprivileged user process and the privileged kernel, right, and that involves hardware support and a very special instruction on the CPU provided for this purpose. Good questions, all right? Any other questions about processes? You're still doing that thing with the hand weight. Okay, so, okay, so today, again, just to review, we're gonna start looking at how the operating system manages the processor. So, you know, first of all, remember, I mean, we're trying to do a couple of things here with our abstractions and with our multiplex. Remember, those are the two goals of the operating system. Multiplex resources and provide useful abstractions, right? So when we start thinking about the CPU, which is what we're gonna be thinking about in the next three weeks, what are some of the limitations or problems with the CPU that the operating system might be trying to address, right? What are, what's the limitation of the CPU or the cores that the system might want to remove, yeah? Yeah, but I mean, specifically, what's wrong with the CPU? Yeah, I mean, the number of cores on my system, even if I have a multi-core system, is usually much smaller than the number of processes that are running on my system. Yeah, but the point is like, now we're in a multi-core world, right? So it, you know, I'm trying to make sure you guys are aware of that, that most machines you buy today will not have one processor, they'll have two cores or four cores, but it doesn't matter, right? The number of cores is still smaller than the number of applications running on your system. If you do PS and count the number of running processes, there's probably several hundred. So even if you have a 16-way system, it's not enough, right? So I still have multiple processes that are trying to share the same physical resource, right? Or set of cores, right? There's only one processor or 16 cores or whatever, so it's smaller than, all right. So the other thing we're looking at is, what are the mechanisms that are necessary to allow the processor to be shared, right? This is getting ahead of ourselves, but, so this is, our goal here is to look at what are the, first of all, hardware features that the CPU has to provide to help the operating system share the CPU and switch things back and forth, and then also the mechanism specifically in the kernel for saving state and starting another process, we call that process a context switch, right? And then finally, so, well, I guess there's two other things. So what are some of the consequences for programmers of the fact that I'm going to be sharing the CPU in this way, right? And this is what creates, especially in the kernel, but in any multi-threaded process, issues with concurrency and synchronization, because the big thing that's difficult for programmers to wrap their minds around is that in general, unless you are careful to make sure that this does not happen, your running process can be stopped and started at any point, right? So it looks like a sequential series of instructions to the programmer can be interrupted at any point and a bunch of other things can happen and then you can start running, right? This is what makes multi-threaded programming very difficult because our way of thinking about programming is inherently very single-threaded, but these systems are multi-threaded and therefore context switches and concurrency take place, right? And we'll talk about synchronization mechanisms that are designed to allow you to do this safely. This is particularly important in the operating system kernel, right? The operating system kernel is a big, complex, multi-threaded program and it also is one where you kind of got to get it right because if you don't, the whole system comes, is gonna crash, right? Like, if you have a bug in your multi-threaded web server and it crashes and you have to restart it and there's a few clients that sit there with the spinning wheel in their browser for a minute, who cares, right? I mean, that's not great, but it's not the end of the world, whereas blue screens tend to irritate you, right? And are a little bit. And then finally, so we're gonna look more at splits between policy and mechanism. So context switching is a mechanism that allows us to use the CPU to run multiple applications kind of at once, right? To share the CPU effectively. But how do we have good policies so that that happens well, right? What do I run? So now I have this problem, right? I've had this nice mechanism that allows me to switch things between the CPU, but now what do I run on the CPU at any given moment in time, right? Or what do I run on the cores that I have at any given moment of time? And this is essentially the problem of processor scheduling, right? And this is a very, very fun and sort of deep area and we won't spend too much time on it, but I hope we'll give you a little bit of an introduction to it. So this is kind of like, this is the overall game plan for the next few weeks. All right, so today, the first thing we need to talk about is, and we really haven't discussed this yet in the class, but because we've been looking so far, our description of the operating system has really, it's been like we've been talking about the C library, right? In many ways, we have kind of been talking about the C library because fork, exec, and wait and exit are C library calls that happen to use the operating system, but we've talked about how the fact that the operating system is like a normal program with some special privileges or it's like a library that you might use, right? So today we need to start actually being more specific about how it's different than a normal program or a normal library, right? And the thing that's interesting, right? So up until now, and maybe, you know, maybe people haven't noticed it, but the things that we've talked about, the process related system calls, none of them have actually required the kernel or the operating system to have any special powers. And, you know, if you find that difficult to believe, I would urge you to go out and look at a user space threading library, right? So if you go out and you look at like P threads or other user space threading libraries, they provide library calls that essentially do these things, right? They allow you to create new threads, allow you to change what a thread is running, allow you to, you know, exit a thread and allow you to weigh down a thread and collect exocodes from threads, right? And this is all done in user space, right? And in fact, it's all done without any or very little involvement by the kernel itself, right? So why does the operating system need special privileges? Remember, there were two things we talked about, the operating system doing fundamentally. One was, does anybody remember what they were? Two things, what's that? Yeah, this is a higher level, right? Multiplexing resources and what else? Abstractions, right? So multiplexing resources and abstractions. Processes are an abstraction and we've seen, and again, I'm asserting here and maybe you guys don't believe me, but I would go urge you to look at P threads that to some degree, abstractions don't require privilege, right? So why do I need privilege? Why does the operating system have to have powers that other applications don't? Yeah, what's that? No, not necessarily. In fact, I don't think it will, right? I mean, user space threading libraries are designed to provide user space threads, right? So you can provide threads in user space again without the kernel having any idea that your process has multiple threads, right? All the threading and thread switching is done in user space and there are advantages and disadvantages. I don't want to go too far down that road, right? So I'm gonna tell you today, we're gonna start talking about some of the special powers that the kernel has, but why does it need these? Yeah. Okay, so that's the beginning of a good answer, right? So one of the things I want to be able to do is to keep one process from harming another, but what does that involve, Jim? Okay, that's also a variant of what I'm getting at, Wembley. Okay, I still think that what I'm trying to accomplish is something that would be difficult, if not impossible, if I allowed processes to access the hardware directly. So multiplexing, right? I just claim that in general, I don't need privilege to provide abstractions, right? And most of the libraries you have on your system prove this to be true, right? They provide abstractions, they provide new features, they provide nice wrappers around stuff that's gooey and gross. They don't require any special privilege, right? But multiplexing resources does, and the reason is pretty simple, right? I've got resources on my system, okay? I'm gonna divide them between the applications, right? And so there's two things that the applications on the system have to trust the operating system do, right? So who can guess what these are? Were you raising your hand? What would you wanna venture against, anyone? Yeah, because we can leave it to the process to decide what the difference is that we have to leave it on the system. Okay, so I will accept that, right? So the first thing that processes have to trust the operating system to do is divvy up the resources effectively. So when you write a program that runs on Linux or runs on Windows or whatever, you are trusting that the operating system will provide you the resources that you need to do whatever it is that you're trying to do, right? And this is not an easy task because there's other things running and the system may be very overloaded or whatever, right? But you are trusting the operating system to give you what you need, right? And to look at what else is happening on the system and trying to give you as much as you can, right? So there's fixed resources, there's a fixed amount of memory, fixed number, of course, that can run at basically maybe a variable speed, but we can imagine a fixed speed. So there's some fixed amount of capability within the system and processes have to trust the operating system to divide up that capability in an effective way, okay? And then once I've done that, what else do I need to do? Yeah. Do you think that monitor and reclaim fits in this box? One word. But I mean, yeah, I just gotta like smack those together and we'll have something that works. Trust, well the processes have to trust the division, so that's more what we've been talking about, yeah. Look at our allocate prior, oh, I'll just give it away, enforce. Once I've made the divisions, I have to make sure that the processes don't take more than I asked them for, right? I said, I'm giving you this much resource right now and I can't have applications saying, well, I want more, right? And the reason for this, right, is processes can be some mixture of malicious and or buggy, right? So, you know, a malicious process might say, again, I'm not satisfied with the amount of memory that I've been allocated. I really know that that other process doesn't need that much memory and if he's just wasted, he's not doing anything with it, right? So I'm just gonna take it away from him, right? And because I know that I'm the most important process on the system and if I get more memory, I'll run faster and I really know this will make the user happy, right? And we just don't trust processes to make that decision, right? And then also, you know, processes can be buggy, right? So if I don't enforce these divisions well, then a process might just be like, oh, well, I'm just gonna use this memory over here and hope that nobody else is using it and I'm really not sure and, you know, maybe, you know, maybe I'll just use it for a little while and nobody will notice and stuff like that and we don't allow processes to do this stuff, right? So this is what creates the need for this privileged arbiter, right? Which is the operating system, girl, Jeremy. Yes, and we will get there. Bring that, return to that question in like a month when we talk about memory, yeah, yeah. What is beeping over here? It's where? I appreciate the fact that you're beeping. Why you're beeping? Well, that's gonna irritate me but I'll try to ignore it. Okay, so you might say like, we've been talking about operating system privilege, but what does this actually mean, right? What does it mean for the operating system to be the privileged program on the system or a privileged program, right? And what this comes down to, and this is why we start talking about this when we start talking about the CPU, privileged mode is a feature of the hardware itself. So privileged mode, privileged execution is something that is implemented by modern CPUs, right? And what we're gonna see, which is kind of a really fun and fascinating story is over time there's been a, you know, as you would expect, there's been a huge amount of co-evolution of the operating system kernels and the hardware that they ran on, right? So there's been this continuous conversation between these two communities over a long period of time that comes down to, you know, what are operating systems trying to do and how can hardware help them and what sort of features does hardware need to provide in order to allow operating systems to do what they need to do? This is one of them, right? So this is something that emerged, I don't know enough about hardware to know exactly when this feature got started, but the, you know, what CPUs allow is certain, you know, what's called a privileged mode, right? So when I'm in privileged, or we'll frequently talk about this as kernel mode, because the kernel is the program that runs in this mode, right? It means, there means a couple things. So first of all, typically there are special instructions on the CPU that I cannot access unless I am running a privileged mode, right? So certain registers on the CPU, certain, you know, things, certain parts of the CPU cannot be modified or cannot be changed in certain ways unless I'm running in kernel mode, okay? And if I'm not running in kernel mode and I try to do this, we'll get back to that in a few slides, right? So there are mechanisms for, CPUs have mechanisms for enforcing this, right? So you have to ask, what happens if an unprivileged process tries to execute one of these instructions? We'll get to this, right? The other thing about the CPU, the kernel is the kernel frequently when running in privileged mode has a different view of memory, right? A view of memory that is different than the processes that are running, right? And sometimes this is actually implemented using the same mechanisms the processes use, but sometimes not, right? So let's talk about some of the special instructions, right? So when I'm in, and we're gonna talk later about how I get in and out of kernel mode and user mode, right? Or unprivileged mode. So that's, you know, kernel is privileged mode, user is unprivileged mode, right? So when the kernel is in, when the CPU is in kernel mode, there's these special instructions and usually these are instructions that have something to do with resource sharing, right? So they're instructions that allow me to do the multiplexing that the kernel is in charge of doing, right? And I just talked through this slide without knowing it was here. So we'll just keep going. The goal of protection boundaries on my system is fairly simple, right? What I want is when I'm in kernel mode, I wanna be running trusted kernel code and when I'm running user code, untrusted code, like code that I don't know where it came from or isn't designed and tested to allocate these resources, that runs in unprivileged or user mode, right? And the CPU implements these mechanisms to transition between them, which we'll talk about today. So just to give you some picture of the fact that this is a little more complicated, most modern CPUs actually implement more than one privileged mode, right? So we're talking about this very black and white thing where it's a privileged mode where I can do anything and I have access to all these instructions and then unprivileged mode where I don't have access to any of them and I'm this unprivileged process. And usually, so x86 processors for a long time have had this notion of what are called protection rings, right? And there were, I think, four, right? We were from ring zero, which is fully privileged to reese three, which is completely unprivileged. And for many years, operating systems that ran on these systems just used the top and bottom mode, right? So ring zero was kernel mode, ring three was user mode. The interesting thing is that when people started to wanna start virtualizing hardware architectures, which we'll talk about at the end of class, these other rings started to become interesting and important and potentially useful, right? So some of these rings are actually used by technologies like VMware to implement virtualization, right? Yeah, what's that? What do you mean by safe mode? Yeah, that has nothing to do with the process, right? That has to do with not loading certain libraries and not enabling certain features and stuff like that. Yeah, that doesn't map on to this at all, okay? And again, just to make sure we're clear, when I talk about applications, we're talking about something running in unprivileged or user mode, when we talk about the kernel, we're running in privileged or kernel, right? So here's a question. I mean, what makes the kernel so special, right? And the answer is it's the application that's allowed to execute code in kernel mode. But here's a question for you, right? Why is the cooperating system allowed to run in kernel mode? Who chose, you know, this Windows binary is the leader of the system, right? I mean, who put Linux in charge, right? I mean, who put, you know, the Mach OS, you know, X kernel in charge? Who did that? Like, how did this happen? Yeah, okay, so we're assuming that we need an app and we need an operating system, right? And that's a good question, though. I mean, we talked a little bit about that beginning of the class. Well, again, I would argue it's pretty critical for moderate systems to have something that can divide up resources and create some of these abstractions that programs are used to. My question is more basic. It says, you know, you, how many people have a Windows computer? Okay, how many people have a Mac? How many people use Linux? Who, why is the operating system on that machine allowed to run in kernel mode? Who did that? Yeah. Can we be more specific? Yeah. Yeah, I'm not, I'm not talking, maybe it's, maybe this is more of a how question, right? Like, how did this come about? How did your machine become a Windows PC? So, but, but that's assuming that it is a Windows machine, right? But how did it become a Windows machine? So it's not a trick question. You installed Windows on the machine, right? You chose to make your machine a Windows machine. When you ran that installer, right? You exploited, you know, a feature of the system that allowed you to choose, right? It said, hey, by putting the CD in and following the prompts, you have elected this blob of code to be your operating system, right? That's what you chose when you installed it. Yeah, what's your name? Sharma. I asked this question because the question is, how did a machine, how did this particular binary get blessed with this special power, right? What's that? No, no, no, the question is why is, why is a particular operating system, right? Why is a, if the Mac OS is managing a machine, why was that operating system allowed to manage the machine as opposed to some other one, right? Where did the operating system come from? It doesn't come baked into the machine, right? It might, like, but somebody is, if you got a Windows machine and you bought it that way, someone installed Windows on it, right? And if you wanted to, you could install a different operating. But the installation process is what was where you take, you didn't know you were doing this maybe, but you took a particular program and you allowed it to install itself on your computer so it could run in this kernel or privilege mode, right? And if you want to change it, you can change it, right? And then people pointed out about boot, right? So, and this is how privilege is bootstrap, right? So when machines boot up, I don't want to, like, boot is, you know, we could talk about boot for a month, right? Bootstrapping systems is very complex and interesting and ugly, but I'd rather not talk about it for a month. So I'm just going to skip over it very quickly and say that when a machine boots up, the CPU is configured to jump to, at some point to jump to a particular memory address and start executing instructions. And when you install the system, you put code blessed by your operating system at that memory instruction, right? And that's how the operating system starts to run when the machine boots, right? So at some point during boot, and there's a lot that goes on during boot, the CPU jumps to a particular instruction and starts executing in privileged mode, right? So when the system boots up, it jumps and starts executing in privileged mode and your kernel is responsible for lowering the privilege level before running user code, right? So there's instructions that allow it to, to surrender privilege, right? To say I no longer want to run in kernel mode, but this is what happens on boot, right? And if you look at your, now I would encourage you to do this and I'm gonna, we're gonna get Aditya to do through this in recitation maybe next week, right? All the code for boot on your Sys161 system is there, right? You can find the first instruction that your kernel starts executing at when it boots up, right? And in fact, you can find the code that loads that stuff into that instruction, right? So the point is that there's, there's some process where the CPU starts, jumps to an instruction, starts executing in privileged mode. That code that is loaded there is the kernel, right? And that will start to do whatever else the kernel wants to do when it boots up, right? So, so there's this question of how do we transition between privileged mode? So I have user mode and I have kernel mode, right? I want, like I said before, I want my user code to run in user mode and I want my kernel code to run in kernel mode. The question is how do I get back and forth, right? And we call the process of transitioning from user mode into kernel mode as a trap, right? Or you call this trapping into the kernel, right? You can think of it, that's probably why it's called this. I've never thought of it this way, but it's like setting a trap, right? There are certain things on the machine that are traps and as soon as the process hits them, the kernel will start to run, right? So that's really how you can think of these things. You can think of them as traps that the CPU and the kernel have set for user programs, right? That will cause the system to enter kernel mode, right? So there's this interesting question about, if you're running code in user space that makes a system call, right? What will happen eventually, and we'll talk about this in the next few slides, is that that code will trap into the kernel and the kernel will start running and take care of whatever the application asks it to do. So let's say I call open, eventually I trap into the kernel, I tell the kernel I want to open a file and the kernel takes care of trying to open the file, right? So I frequently think about the thread that they executed the system call is now running in kernel mode, right? So I run a system call, I trap into the kernel, and now that thread is kind of running inside the kernel, right? That's not completely accurate. It's accurate in terms of, it's a stream of instructions that was initiated by the trap, right? But it's not accurate because the kernel thread has its own state and its own stack, and the first thing it does when you trap into the kernel is save all the state for the user process so that it looks like a function call. And the other reason this isn't accurate is that if you have system calls that don't wait for the kernel to complete, then what actually happens is I call open and I keep executing and the kernel starts doing something as well, right? But you can decide how to think about this and decide which way is more most effective for you. But I like to think about traps as having the thread transition in and out of the kernel, right? So there are three reasons that we typically transition into the kernel and start running in privileged mode, right? One of them has to do with hardware, right? So if a hardware device requests attention, right? And we call this a hardware interrupt. The second reason is that the code on the system requests attention. We call this a software interrupt or a system call. And then the final reason is that software needs attention, right, and this is called a software exception, right? So what's the difference do you think between requesting and needing attention in the framework here, right? System call on the software exception. What's, I mean, you guys, we've been talking about system call. So what's an example of a system call? Yeah, fork. Fork says I'd like a new copy of myself to be made, right? What's an example of a software exception? Yeah, that'd be one of them, right? Like dividing by zero, right? So needing attention indicates that the software has done something that indicates that it cannot go on executing. The kernel has to do something about this. The software has messed up. It has done something it shouldn't do and the kernel needs to take care of it, right? It is something wrong and the kernel has to take care of it, usually frequently by killing the process that caused the software exception. Jeremy, you had a question? No, we just didn't see it. Okay. So hardware interrupts, so again, we have interrupts that are generated by software. We have interrupts that are generated by hardware and then we had these exceptions. All three of these will cause a very similar process to take place, right? So hardware interrupts are generated by hardware to indicate to the operating system that something happened that the operating system probably wants to know about, right? And so it's like, you know, the network card received a network packet over a particular interface, right? Or the disk has finished reading the bytes that the operating system requested. There's also timers on a system that fire at regular rates that simply tell the kernel, hey, this much time has gone by since the last time I fired, right? And maybe you wanna know about that, maybe you don't, right? So at the sort of micro-architectural level, there are like a gazillion typos on the slide. At the micro-architectural level, processors implement these ideas of interrupt lines, right? And interrupt lines is a good way to think about what's happening. It's essentially a little string that's connected from the kernel to some piece of hardware. And when that hardware needs attention, it yanks on that, right? And the kernel feels like a tug, you know, from the disk hardware interrupt line. It's like, oh, the disk needs something, right? Or, you know, the timer sits there going like this, you know, the whole time the system is running. You know, yep, yep, another tick, another tick, another tick, right? So, and obviously these aren't little pieces of string inside your computer, although that'd be cool. They're actually logical lines, right? Hopefully I didn't have to say that to anybody. Little thread, the thread on my disk broke. It's not receiving interrupts anymore. That's how one happens. So, when an interrupt is triggered, right? Here's what happens, right? And this is something that we're gonna go over and over again, but it's important to get right. So, and these things, these things you can essentially think of as kind of happening at the same time, right? There's not necessarily an ordering here, but this is the group of things that happens, yeah. Yeah, so the CPU implements these by having these, you know, these ports on it, right? When there's a logic level transition, the next thing I'm about to tell you will happen, right? Okay, so the first, you know, the things that happen are I enter privileged mode, right? So, if I was running in unprivileged mode, I'm now running in privileged or kernel mode. There's frequently some state about the interrupt that's recorded on the CPU, right? A register might be set to indicate which interrupt line caused the interrupt, right? Because when the kernel starts executing and wants to know, like, did the disk wake me up or the network wake me up or the timer or whatever, right? And then, this is the most important thing. Well, the top, the privilege mode and this one are the most important thing. I jump to a predetermined location in memory and I start executing instructions. So, when, you know, and this is just how the processor is built, right? So, when there's a logic translation on this line, I will enter privileged mode and I will start executing instructions loaded at some address, right? And that address is usually fixed in the hardware, right? So, like, a particular type of processor will jump to a particular location and start executing instructions when an interrupt is triggered, right? So, what we call the instructions that the processor executes when the interrupt prior is, we frequently refer to this as an interrupt service routine, right? This is a piece of code that's executed to service or, you know, deal with this interrupt, right? And let me just mention one more thing, right? So, who, like, the interrupt service routines are these little pieces of code. I mean, those are installed by who, right? Who, who, this is part of what piece of code, right? The interrupt service routine that I'm gonna run when an interrupt occurs. What's gonna happen? What code am I gonna start to run? That's it? So, I'm jumping to a fixed memory address, but who put the code there that's gonna start to run? The kernel did. Yeah, the kernel did, right? And again, you guys can find, on your own system, when the kernel boots one of the first things it does, in fact, maybe even the first thing, is that it copies the interrupt service routine into the location in memory where it knows that the processor wants to find it, right? Remember, memory is wiped out every time the machine turns off. So, when I boot, my interrupt service routine isn't loaded yet. So, the first thing the kernel does is load that, right? Because until it loads the interrupt service routine, it can't take any interrupts. That's like kind of the most important thing that the kernel does during boot, yeah, one way. Yeah, yeah, yeah. So, this is a good point. Interrupts and traps jump to this address and start executing that code. And yes, basically every system call will start executing the same code the minute after the system call takes place, right? That code is responsible for kind of figuring out what went on, right? You know, you could have a system where, and there probably are systems that have different service routines for different interrupt lines, that would be another way of doing it. You say, if the disk interrupted me, I start here. If the network interrupted me, I start here. But usually, you know, that's easier to do by just setting a register somewhere so that I know what happened when I start to execute. The interrupt service routines are usually like a tiny, tiny, tiny piece of code that just jumps into some other part of the kernel and starts executing, right? On the MIPS, I think they're like 80 instructions is the limit for the interrupt service routine. So, it's just the beginning of something and then it's gonna jump to some other part of it. All right, so I think this is a good place to stop for today on Wednesday, or this Wednesday. On Friday, we'll finish talking about hardware interrupts and then we'll talk about ways that software gets the attention of the operating system.