 All right, welcome back to operating systems. So this is the first lecture where you're actually responsible for learning everything today. So we're going to probably spend half the class on one simple, well, one example. Not going to be simple, depending on whether or not you've taken this course before. So let's get into, we know a bit about processes. Let's get into how to create them. So recall way back in lecture one already. Wow, week back. So recall what a process is. It is an instance of a running program. There are some things we know that are in a process. So like virtual registers because processes do not interfere with each other, right? They look completely independent. Remember virtualization solves a lot of our problems. And they also think they have access to all the memory. So they have virtual memory. And that is where their stack and their heap is. So we explored a bit more. And we also argued that, hey, global variables are just memory two. They're also in virtual memory. And all the open file descriptors are also part of the process. Remember our good three friends, file descriptors 0, 1, and 2, standard in, standard out, standard error. There are good friends. And so what's actually in a process? Well, so the generic term for it is called a process control block. And that is the structure that contains all the information about a process. It is called a PCB for you hardware folks. That is not a printed circuit board. That is a process control block because we're in the software world now. So specifically in Linux, this is called a taskstruck. And you can click that link and browse it on GitHub to see everything that's in the struct. It is absolutely enormous. But that's the beauty of open source in Linux is, hey, you can pry into these things and see what actually makes up a process control block. Save you a bit of reading. It contains some things like the process state that we'll see next slide. The CPU registers because it has to save them. So that's how it does the virtual registers. Any scheduling information, so whether or not this process should run, how much time it's taken up, is it a hog, whatever. Memory management information to handle all of that management of virtual memory. I owe status information. So using those file descriptors, if it's writing to a file or reading to a file, something like that. Lots of different other types of accounting information. The only thing we really need to know about it is that each process gets a unique process ID called a PID or a PID. And that keeps track of a process. So it's unique to a process. It's just a number. And that exists as long as that process exists, it will never change until it eventually finishes executing and terminates. So this is the process state diagram. And this is what we'll be doing today. So how to create a process from nothing. So the first thing your kernel is going to do is create a process. You can imagine it will have to do something with virtual memory. It will probably have to load that program in the memory. Might have to do some other things, mucking around, initialize some state. And then eventually it will go to this waiting state. This waiting state could also be called ready if you want. Means the same thing. Means that the process can actually execute on the CPU. Everything is set up. It's ready to execute, but it's not executing yet. Eventually your kernel is going to decide to transition it from waiting or ready to running, in which case it will actually be running on a CPU core and hopefully doing something useful. Then eventually if it takes too long, your kernel is going to be like, okay, you've had enough time on the CPU and it would transition you back to this waiting state. So it would have to do some things, save the registers and essentially stop you from executing. So we'll get into the details of those mechanics later. For now, just know that you run and then you don't run. And then the kernel gets to decide whether or not you run because it is the judge of everything. Eventually when your process is running at some point, it will terminate and take this transition and then it will be terminated. It will be deleted. All the memory associated will be cleaned up. The process will no longer exist anymore. Any trace of it will be wiped off the face of your computer. So we saw a system call already to how to terminate a process. Anyone remember what system call this might be that does this transition? Kill, very close. Exit, yeah, it's exit. So exit or exit group is the only way to terminate a process. So this running to terminate it happens because that process called exit. So you might be wondering about this other state block. So when you're running, you might do a system call like writing some bytes to a file. Writing bytes to a file is very, very slow. So when you make that request to the kernel, it would put you in this block state because your process can no longer run. It's actually waiting for something. So it's a bit unfortunate that's not called waiting. So blocked means it's waiting for something and it can't continue until that operation is done. So that's the extra part of waiting, which is why it's called blocked instead. So for instance, it could be waiting to write to a file. It would go in this block state, which means the kernel can't execute it anymore. And eventually whenever it writes those bytes to the file after a very long time, the kernel will transition it back to waiting or ready, running or waiting or ready, in which case it can go ahead and run again. And then you can see the result of reading from a file or you know that you've written to a file. So that's the state diagram that we'll get into today. Again, we're just kind of only concerned about the created thing, but knowing that the kernel gets to decide whether or not a process is running is going to be slightly important for us. So another fun thing, which is basically this is what you'll be using for lab one. So lab one, everyone's probably used like task manager activity monitor where you can see all the processes running on your machine. Those are processes. So lab one is you're writing your own process monitor. So you'll just shoot out all the processes that are currently running on the machine and it will be cool. So on Linux, and this is specific to Linux because every kernel is going to have a different way to access information about it. On Linux, instead of having specific, like a billion different system calls for each type of information, it creates some fake files that you can read. So there is a standard slash proc directory and it looks like a directory that actually would exist on your disk, but it's not real. The kernel is just kind of faking it and presenting information for you in that way. So there's a slash proc directory on Linux and it lets you read all sorts of things about the kernel. So there's like proc CPU info, you can read what CPU the kernel is currently running on. You can do that on any machine, even like the ECF machines or whatever and see what processor they're running. So one very important thing in this directory is that there are within proc, there are directories that look like numbers. And if that directory is a number, that number represents a process ID. And within that directory, which is a number, there's all sorts of information about that process. You can read its name, you can read all sorts of things about it. Specifically in lab one, there's a file there called status that holds its name and that's what you'll be using in lab one. So you might think we can just create processes from scratch that would probably make the most sense to you, like there is some system call where I just say, hey, create a process from scratch, run this program and just create the process control block for me, initialize it, do all that stuff and just start running it. Well, that's what Windows does. There's a system called create process and that does not exist on Unix, which is Mac OS or Linux. Unix decomposes that into some more flexible instructions which will start off being very weird, but you'll find out that that is actually quite a useful thing to do. So how do you create processes on Unix? Well, instead of creating a new one from scratch, the only way to create a new process is to actually clone it, which seems a bit weird. How that works, which as the example we'll be doing pretty much all lecture is it pauses the currently running process, copies its process control block and makes a complete copy of it that is otherwise independent. So at the time of the clone, the two processes are going to be completely indistinguishable from each other. They're going to have the same virtual memory that may or may not map to the same physical memory. They're going to look the same and after that point, they're going to be completely independent. So the only way to tell which process got cloned and which process is the clone E, if that's a word, is Unix keeps track of processes with a parent-child relationship and why they call it that will become a parent. Oh yeah, that's pretty good. Yeah, so one way to think about it is that, hey, if you have a child, it's kind of like a clone of yourself. You might think, well, siblings are kind of like clones too or identical twins. Why is it called parent-child? Well, we'll figure out in the next lecture that the parent is technically responsible for the child. So it has to take care of it in a way and that is why we call it parent-child. This is also where your Google searches start becoming weird. So one thing you could Google, which is a valid thing in operating systems is how parent to kill child or something like that. And then suddenly you will go on some watch lists. Probably not, but if you have roommates or something like that that aren't in computing, don't let them see your search history because it's going to get weird because of this course. Yep, yeah. Cloning is the only way to start a new process on Unix. And we'll see why that is a thing later on in the course, but for now, that's just a thing. So the point of that is it's a complete copy and then the new process could reload a new program and set up a new PCB, process control block if it wants to. It turns out that cloning is really, really fast and essentially free and it gives some useful benefits. Yep. Yeah, so that's a good question. How do I start the very first process then and we'll get into that next lecture because we're going to have to take care of some children tomorrow and it's going to be great. It's much funnier than actual parenthood, trust me. Actually, don't trust me. Anyways, so this is also where your Google searches because clone isn't called clone, it's called fork. So you're going to be forking new children, which also sounds really weird. So again, be careful with your Google searches. Make sure Safe Search is on for this course. So it has this following API. So fork takes no arguments. It takes no arguments, you just call fork and it returns an int or process ID and this is a process ID of the newly created child if it is returning from the parent. So remember that fork, it may fail in which case it doesn't create a new process, in which case since we don't create a new process, your life pretty much stays the same as it ever has and it returns negative one to say I couldn't create a new process. Otherwise, if it succeeds, your life gets weird. So if it succeeds, now there are two processes that look exactly the same at the time of the clone and now the only difference is going to be what fork returns in each process. So in the parent process, it will get a integer that is greater than zero and that will represent the process ID of the child that was just created and then in the child, it will always get zero as the return value to indicate that it is the child. Yep, sorry? Yeah, so just can the child have children? Yep, so everything has children. Yeah, we'll see, yeah. Yeah, so that's a good question, what would fork return in that case? In that case, so only the process that called fork gets cloned. So you'd make two children, they would each call fork and then they would both get cloned and we'll see how that can get out of control. You'll probably break my machine, I'm almost sure of it. Yep, they are independent after the, so let's just get into an example. So if it returns not negative one, there's now two processes running, they can access the same variables, all the rules still applied, just there are now two of them. And the operating system, the reason why I can do this really, really fast is something called copy on write, which we will get into way later, so you can come back to this slide when you're reviewing the course and be like, hey, I understand what that last line means now. So, also for documentation stuff on POSIX systems, you can find some documentation just on the virtual machine itself using man, which is short for manual. So you can do man fork and read about it there too. Also sounds kind of weird. You can also do man exec VE, you can read up some documentation or I mean, it's 2023, you can Google it, there's online man pages too, people that write documentation, C++ documentation will probably have these functions as well, because you'll still have to call them because hey, it's the only way to create a process. Okay, so here we go, here's fork example. So this is going to get real weird real fast. So initially it starts off in main. Well, it starts executing in main, we know that's not really true, it's going to load the standard C library and all that stuff, but thankfully that was just for your information. We can just pretend it starts executing at main even though we know that's not true. And now the first thing it does is fork. So it forks and now we have two processes and they're an exact copy at the time of the fork. Thankfully it didn't really do anything before this point so it's not really copying much aside from the instructions and stuff like that. And then one of the processes will return from fork with a value greater than zero if it succeeds, which would be the process ID of the child. And then it would probably go not into this branch because it would be PID, or return PID, sorry there's a typo there, would be greater than zero. These are all in the if statements supposed to be return PID. So it wouldn't go in this branch either because it's greater than zero so it would go in else. And then it would print off the return PID. There's also a system called to get the process ID of the current running process called get PID, very aptly name. And then there's also this get P PID. So that means get the parent's PID. So if you get the parent's PID, so it'll tell you what the parent's parent is. And that kind goes in the question of like do children, children, children. Yeah, so parents can have parents can have parents. So like they'll have like grandparents and stuff like that if you wanna call it that way. But it's just a big sequence of parent-child relationships. Then the other one, this fork will return zero. So return PID will be equal to zero. It'll come in here, do these print lines and then exit and then return and then that process is done. So let us just execute that because this will probably take some time to probably take some time to make some what sense. Oh, please don't say I still broke it. Uh-oh. Oh, the last class broke my machine. Okay, one sec, I can save this material. Okay, let's try that again. Saved. All right, so this will be a bit weirder because I'm running it on macOS instead of Linux. But hey, it works on macOS because guess what, it has fork. So in this case when I run it, I know that these lines are from the parent and these lines are from the child. So your life still makes sense. A program cannot be in two if branches at the same time. One is from one process, one is from the other process. And the numbers kind of make sense, right? So the parent, it's return PID is 20, 20, oh, 20, 23, six. And that should be the process ID of the child, which if we look at it, hey, guess what? It's the process ID of the child, that makes sense. And it's the child's return PID is zero just like I told you. And you can see that the child's parent process ID is, I'll say the last two numbers is 35. And guess what, that is the parent's process ID, 35. Yep. So the question is how does it satisfy two else at once? The answer is there's two processes. One goes into one branch and the other goes into the other branch. Yeah, that's kind of cool, right? So this is where your world becomes weird because that's kind of weird. It kind of looks like if you don't know what's going on that it's going in both branches at the same time. But in actuality, there are two processes. Any questions, yep. So yeah, questions, where did we create the two processes? So one already started executing main, something created it for us and then we forked, so we created one. Yep. Yeah, so because it's a complete copy at the time of the fork, that's like a complete copy of everything, including program counter and everything. So it would look like they both continue right at the fork. They both keep going, yep. Yeah, so that's a good question. Does the parent always just execute after and then the child? Well, let's see if we can answer that. So this, so it kind of looks that way, right? I'm executing this a bunch of times. The parent's always going first. So this demo will work. If you do this on your VM, it will probably not always be in this order. It was like, macOS does some weird things. So, no, so the sleep is after, so it doesn't matter. So they'll both try and do this, the first print line, so that sleep doesn't actually matter. Yeah, but so the generic answer is in this case it kind of looks like the parent always runs first, but the generic case is you don't know. So after it creates two processes, remember that whole waiting and ready and going back and forth. Well, you're not guaranteed that you continue executing. Your process could be put on waiting and the other, the one you just created can run. So you can see child before parent and we can go over that quick. So how does that work? So just to spell it out hopefully a bit clearer. So whatever you start executing this program, it will be executing in a process and let's just call this process one. So process one, we'll start executing main. So let's just assume it's there, right? So one process, one main, your life is the same as it always has been. Now it throws you a wrench and immediately calls fork. So at the time of the fork creates a new process, process two, and we're assuming it's gonna succeed. So we create process two, process two, we can go ahead and do an arrow to represent that. Process two is a child of process one. So here we can just say parent child. So having these lines might be useful, but I'm going to delete them for now because I'm gonna show you the execution. So at the time of the fork, both processes haven't returned from the fork yet. So they're in the operating system, the kernels copying stuff, it's doing the magic of actually cloning them and making them the exact same. So eventually it's gonna finish cloning everything and then one of them is going to return from fork. You don't know which one returns first. In the case of my laptop, well this one returned first, the blue one, process one. So its fork would have returned, what number would it return? Anyone remember already? Two, right? So it returns, the parent returns the process ID of the child. In this case we created process two. So its fork would return two and then in this process it would create the local variable return PID after the fork because that's the order C does things in and just for note, this PID T looks a bit weird. It is just a type def. Whenever you see this, PID type is basically just an in. So if you wanna replace it, works same thing. Generally you try and be explicit with your types that this is supposed to represent the PID, but nothing bad will happen if you say it's an in. So then at this point, process one would assign returned PID equals to two. No, so it's assigning the fork, right? And fork return two because it's the process ID of the child. So at this point in order to assign that variable, process one would be at this line in execution, right? Somewhere after that statement because now return PID exists and assume I didn't have a typo here. Is return PID equal to negative one? No, so it would go here. Then it would check this if branch is PID while return PID again, sorry for the typo is return PID equal to zero. In this case, no, we see that it is two. So it would go into the else and then it would print this. So parent return PID, it would print two and then get PID of itself would be one and then this would be a bit of a mystery. Now at this point, let's just say that process is done and now process two starts executing. What is fork going to return? Zero because this is the child, right? Process two was the child process. So it would return zero and then it splits off and it would create its own return PID unique to this process and it would be equal to zero. So now it would continue execution like everyone knows and loves. It would check this if statement. It's not negative one. So it would go here. It would go into this if, oh, is return PID equal to zero? Yes, it is. So it would print child return PID zero. It would get child PID, what would this be? Two and then child parent PID, what would this be? One, right? So then it would finish, go this return and spoiler alert, returning from main eventually just calls exit. So that's how both of them and then eventually both of them would be done. But at the time of the fork, what could happen is, well, we could have executed, the kernel could have decided to execute P2 first and we could have seen everything from the child instead of seeing it from the parent. If you go ahead and run this example, well, you might actually see them in different orders but the order within a process will make sense to you. It's just the order between processes. You have no control over and yep. Oh, sorry. Oh, like why is the process ID two? I just made up a number. Yeah, so in the parent, I just made up a process ID number for both of them. I just said the initial one is just process ID one that starts executing main and the child I could make up two. So I just made up the numbers. I could have like, they just would be consistent. I could have just called this process 100 if I wanted to. But whatever you assign a process ID, it will exist as long as that process exists. So there won't be another process 100. Yeah, sorry. Sorry, can someone repeat that's like halfway in bit? Oh, okay, yeah. So can the parent only have one child? So, well, let's see. So could we do something? Let's go here. So is anything preventing me from doing this? Whoops. Well, let's try. Okay, I like trying. And finally, this class did something new than the other one did. So that's what happens. And that is all of the output here. Let me hide this. So that's all the output. So why did that happen? And spoiler alert. These are gonna be the questions you have to answer. Yeah, kind of. So was there another comment somewhere else? Yeah. So here, we think there's two parents and two children. Well, it's not really a parent, another pun. So how many processes are actually here by just looking at how many lines get printed? Three, right? Hopefully, let's go look at this again. So in each branch, there are three print statements for things. So probably each process prints three of them. And when I scroll up here, I see nine of them. And funnily enough, the parent ones look exactly the same. Yeah. Yeah, yeah. So let's go over this a bit slow if you didn't, weren't able to hear that. So let's go back up with, I'll keep my same two processes for the first fork. And where'd we put the other fork? We put it like essentially right here, right? So everyone's still on the same page with what happens for the first fork for PID one and two? I guess so. Like the confidence? Anyone else more confident? Yeah, okay. All right, tough crowd. So the child. Let's assume the child executes first because that is the easiest thing to reason about. So same thing happens, it would have got zero from that first fork. Created return PID equal to zero. This is not true. Goes here. This is true. Print, print, print. Goes here. Returns. Process two is now dead. Technical term. So that accounts for the prints from the child. Well, what happens in the parent? So parent returns two in this case, creates return PID, which is equal to two. And then checks it and this branch, it's not equal to negative one. So it go here, return PID does not equal zero. So it goes here and then it forks. So fork again, like I said, creates an exact copy at the time of the fork. So it would create a new process and it's a copy of whatever called fork, not some random process. So we'd create a new process, P3, something like that. And I'll color this orange. And in P3, what would that fork return? Zero. Zero, right? That is a new child. And if I wanted to draw parent-child relationships, it would probably look like P1. It had a child called P2. And now it has a second child, right? It has a second child called P3. So that answers your question about having multiple children. In this, I have multiple children. I have two children now, right? So here in P3, fork would return zero. What would that orange fork return for P1? Three. What do we do with that three? We run away. Absolutely nothing. And also, at the time of the fork, in process one, did return PID, did that variable exist? Yes. What was its value? Two. So since that existed before the fork, does that value exist in P3? Yes, it's an exact copy. So that variable exists in P3. And what is the value of returned? So it exists because in process one, return PID exists, right? When I call it fork. So if I call fork and I create a new child and it's a complete clone of me, that variable exists in the clone. And what's its value? Two. Two. So I don't really change anything. Both of them would print the same thing. So both of them would print return PID equal to the same thing, which is, let's see, go back. So here, return PID here we can see is the same thing because I just took the return value of fork and it's like fork it, I don't need it. Who cares? But you'll see that the numbers are actually different. So the processes are indeed independent. So this parent has process ID, it ends in 53 and this one ends in 55. But oddly enough, they, oh yeah, and you can see that, well, this, oh wait, that's not returned. Oh, yeah, see the order doesn't exactly match up because it's, oh wait, yes it does. Sorry, I'm done, ignore me. So in one of them, the parent's parent ID was this 110 and the other parent ID was this 30 or sorry, 53, which corresponds to this one, right? So in that diagram that corresponds to this one, right? It has a parent and its parent was the original one, yep. So fork just returns either zero if you're the child or the new process if it's successful, that's it. And it's a complete clone. So like, what do I mean by complete clone? So let's go. So complete clone means like complete clone. So let's go here, say before the fork in x equals 42. So if I run that, what should I expect to see? I should see x equals 42, hopefully. How many times? Twice, hopefully one from each process, right? So x will exist, it'll fork and then they'll both continue going along and they would have been clones at the time of the fork. x existed, it was 42 and both of them should go ahead and print x equals 42. Magic. So to make sure they are still independent, we could do something like this. We can have a print x equals something at the end and let's just say, so the parent seems to always go first. So let's just change x equals to two only in the parent. So likely this will get executed first. So what should I expect to see for my value of x? 42 and two, right? Because they're supposed to look exactly the same but after the fork they're independent of each other even though they look exactly the same. So we should only update x equals to two in the parent and it shouldn't affect the child and in fact, there we go. So this also lets you know that virtual memory exists. Why? Well, let's go ahead and take this even further. So the address of x, we can print out. So when I say exact copy, should the address of x also be the same? Yeah, when I said exactly the same, I meant exactly the same. So here, 16f, 60, 30c8, same thing for there. So they're using the same virtual address but my kernel, they're both in virtual memory, each process has its own virtual memory. So even though at the time of the clone, they're exactly the same after they execute independently. So changing x to one in one doesn't affect the other even though it's the same virtual address. So it gives us some confidence that, hey, that is not a physical address, otherwise I would have seen two the same time, right? So I would have updated that variable to two in memory and it would have been two for both of them. So this is virtual memory. Yep, in the terminal. Yeah, just a warning. Oh, more. Oh yeah, you'll notice that. Why does it say child parent PID equals one? We'll figure that out next lecture. So usually it's a different number. I'm on my MacBook, so this example won't work. So there's this number that looks kind of mysterious, right? So if I was on Linux, how could I figure out what process that is? Okay, how could I figure out with what I said earlier? Yeah, in the PROC file system there's gonna be PROC and then this number is going to exist in there and it's gonna have a status file and at the top of that is the name. Okay, so since I'm running on Mac, there's no PROC but spoiler alert, anyone wanna guess what this process is? Terminal very close. So that process is ZSH for me. So it's your shell. So what does that mean ZSH did to launch my process? Yeah, because I got a new process ID, right? So what must it have done? It must have forked, right? So in order to launch your program, your ZSH called fork, made a copy of itself and then started running your program instead. Kind of cool, how to do that? Or do we wanna screw with this? So I think we answered a good question. You should be able also answer what happens if I do that. Let us, yeah, so we'll start this off for you. So it starts off the same way. Let's get rid of this fork. So we'll start off the same way, process one. Again, I could name this anything for fun. I don't know, process 42, whatever. I just have to be consistent with the numbers. So 42 calls fork. Okay, well, let's create a new process called 43. And it is the child of process 42. So in here, I'll just call it return PID equals 43. And in this process, return PID equals zero. So now what happens? Well, eventually they're going to both be right before this fork and they're both going to call fork. I have to argue, I can, you don't know which one goes first. So just pick one. So which one do you wanna do first? Who called fork first? 42 or 43? 42 again. 42, all right. So 42 calls fork. So it would create a new child. It would get some process ID, probably 44 or something like that. And since return PID existed at the time of the fork, it will be a copy, an exact copy of whoever called fork. Whoever called fork was 42. So return PID is going to be 43, right? I don't update it. Again, I throw away the result of fork. Now I can argue about process 43 that would call fork. So it would call fork and then it would create a child. So it would create a child, say process ID 45 or something. Well, did return PID exist when it called fork? Yeah, that's what value is it? Zero. And now these are all, these are the four processes that get created. And you can see that, yeah, there's kind of a grandparent. So this parent's this, whose parent is that. So you could say that process 45 is a grandchild of 42 if you really want to, if you'd want to go with the whole family relation thing. So anyone want to explain what happens there? A nuclear explosion. A nuclear explosion. So how many processes do I create there? Okay, how many processes do I create after the first time through the loop for every process? Well, just one, just one process starts, okay, let's assume this line doesn't exist. Actually, just move it up. So first time through the loop, one process would create one other one, and it would split, so now there's two. Now both of them, assuming, let's just assume they run at the same time for whatever reason, they would go both to the beginning of the loop, and then both of those would call fork. So how many processes would get created that time? Two, right? Two processes called fork, they each create a new children. So they create two children. How many processes do we have now? Four, two plus two. All right, so now those four will go through the loop again. Then each of those calls fork. How many processes do we create? Four, how many do we have in total now? Eight, next time, how many do we have? 16, 32, 64, 128, 256, 512. This gets bad real quick, right? Yeah, what happens when I actually run that? The answer is the lecture ends, probably. So before in the other classes, they got me to run this, but my virtual machine was working and it died, which is why it died at the beginning of this, but right now this is my Mac. So I will run it, I will not be happy about it, but it will definitely end the lecture. The other one, it was a virtual machine, so it was fine. This is not okay. All right, so first, before we break my computer, let us quickly introduce another thing and then we can go over examples again before. So you might be asking, how do I create a new process that runs a new program? So there is a system call called execve, and that replaces the process with another program, resets the whole process control block and starts executing that program instead. It doesn't create a new process. It repurposes the already running process. So it will be the same process ID, but if this is successful, it will start running something else. So it has the following API. It takes a path, which is the program to load and execute. Again, on Linux, this will be an elf file. And then it takes argv, which is a ray of string. So this is basically what you get from main. So you can tell that, hey, ZSH, if it's executing my program and I get arguments from main, it's using this and setting the arguments through this system call. And then there's another argument for environment variables that we can ignore for now. So if this thing fails, then it means probably the file doesn't exist or something bad happens or whatever. The process is still executing, continues on with an error code. But if this is successful, this function call looks like it never returns because this process gets replaced by another program and starts executing that instead. So whatever called it got replaced doesn't exist anymore. So if we, okay, so let's just, so code. So if we go here, actually we'll do it next lecture. All right, let's just end it. So this begins with while one fork. So this will definitely end the lecture. All right, so let's compile it. Huh? Can I what? Oh, new line, of course. Oh God, 10 seconds, all right. It's still going. So it's actually going a lot slower because it has to print, print takes a while. So you guys actually saved me, thanks. So let's see. I guess you can't see this, but I can see my activity monitor. I am loading it up. It is not loading. Oh, there it goes. CPU. So it looks like it actually stopped. So it says I have 2,900 processes and the number is not climbing. So you can defend against this. So what macOS is doing is it's killing some of these and not letting it go on infinitely. So it limited it some way. So macOS is smart. Linux will also do this. So if you run this on the ECF machines, again, I'm not telling you to do that. But if you do that, they have defenses and they will know you're doing this because it will probably limit the number of processes you can create and you will probably get a very angry message from the admins. So, and if you remember my name, don't remember it when you have to talk to them. Yeah, sorry. So the VM, it's not set up to really defend against it. So it lets it grow really large and it killed it. Yeah. It looks like it's killing them and making new ones and killing and making new ones because it's still printing lines, right? Yeah, but it would continue to. Oh yeah, good point. Yeah, so it might just not be, fork might just be failing over and over again. I should have just print the return value of fork. It's probably just failing. Good point. Sir, quick question. So back to the original fork program. Say we have P1 and P2, what would the six lines print? I'm assuming that far that...