 Alright, welcome back to operating systems. So today, basic IPC. We get to experiment with it, but anyone remember what IPC stands for? Inter-process communication, right? Because processes are independent and we have to ask the kernel to nicely communicate between them. So if we don't remember IPC, basically you can think of it as transferring bytes between two or more processes. In fact, you've probably done it before because reading and writing files is an example, technically, of IPC. So you have a process, opens a file, reads it, maybe modifies it, writes it out. Later comes along, reads the same file again. Well, technically, two processes just communicated with each other. They didn't, they don't necessarily have to be running at the same time, but they just, just bytes have to transfer between them. That's it. So the read and write system call, that's how you do some basic IPC. They allow you to read and write any number of bytes you would like. So let's just make a simple process that just reads, yeah, reads things from standard in and writes things directly from standard out without having any idea what they are. So let us get into the example. And here is our example. So in the example, we will declare a buffer of an array of characters of size 4096. This number will become apparent to you as soon as we get into virtual memory. For now, it's just a number that kernel seems to really like for all you care about for right now. Then we'll declare a variable called bytes read. And then in a while loop, we will continually call the system call read on file descriptor zero. We will give it this buffer, tell it the size of the buffer, and that is the maximum number of bytes the kernel will write to that buffer. And we'll check that is greater than zero. So remember, the read system call returns how many bytes you can read from that buffer now that the kernel is done writing to it. So I will just call read over and over again. While there's some data left while I'm reading some greater than zero amount of bytes. And then in here, I write to file descriptor one, that same buffer, I'm going to write the number of bytes I read in. So anything that comes in goes immediately out. So here I record the return value from right because I should be a good programmer. Be sure to check that I have any error codes or anything like that. So if you read the documentation, it's like every other C wrapper for a system call. If it returns negative one, means there's an error. I can set, I can save error no print or use p error to print a message. So I don't have to decode it myself. And then I'll just return from main so I can just exit the process. Otherwise, here I will write an assert statement. So you should probably write these in this course because you'll make a lot of assumptions. And they might not necessarily be true. Here, I'm assuming that the kernel always reads the amount of, or yeah, I can write the amount of bytes I always get. I always read the kernel may not always write all the information I request of it. But here I'm just going to, I wrote the rest of the program assuming it does because it gets kind of ugly. If I have to, you know, there's a partial write and then I just have to do some pointer arithmetic, which is everyone's favorite thing. I am sure. And then, you know, read the rest or big, write out the rest of the file. So at the end of this statement, right here, if I exit out of this while loop, it means that bytes read is zero or less than zero. So I'll check if it is negative one, which means there's an error. So I'll just print off what that is again. And then here I will assert bytes read equals to zero. So you might have been told like when you read a file, there's an end of file character or something like that. While on Linux, that's a complete lie that doesn't exist. So the way you know you're done with a file on Linux is if you do a read system call on it and it says zero bytes written to it so you can read zero bytes from it. That means it is done with data. You'll get no new data out of it. That file descriptor is done. Nothing else is coming out of it. So you know if you're done, if read return zero. So here I'll just assert that, you know, read return zero in which case I'll exit successfully. So let's go ahead and run this. So it seems like a really dumb application that doesn't do anything, right? So how would I make this application do anything? Any guesses if it's just reading from standard in and writing to standard out? Yeah. Yeah, I'll type something. Hello. Enter. Bam. So anything I type into it? Well, what I type into my terminal by default, that will be standard input. And when I hit enter, well then this process was able to do a system read call. It read hello and a new line. So it wrote out hello with a new line to standard out. Yep. Oh yeah, so there's a good question. So after my while loop, do I have to close these? And in general, the standard file descriptor is like zero one and two. You just keep them open until the process exits and then it cleans up the whole thing. So sure, you can close them here if you want, like I'm done with them here, but the process is going to end. So like that's the same argument why I don't free memory if I'm going to use it the whole time anyways, it'll just clean up at the end. Yeah, so I can write stuff here. Hello. You know, hi, class, whatever, it gets repeated every time. So if I want to do end of file and signify end of file, the default thing is if I press control D, that says I'm not going to type anymore on my terminal and then the program is done. It terminates and we're all good. So any questions about that? Kind of cool or maybe useless depending on who you are? Yep. Oh yeah, so the question, what happens if that condition is not true for the assert statement? So if that is not true, your program will blow up and it, well here, let's just see. Why explain it when I can show it? So I'll write something, press control D, then it'll say that the assertion is no longer true and it'll say which assertion got triggered and then what line it happened in. So I know I screwed up here. So it's a good way if you have these all over your code and something breaks, it'll tell you that, hey, that's not true anymore and you should fix it instead of like doing what Java developers do and just like propagating an error and then you can never debug it ever. Be proactive in your debugging. Yep. So the file doesn't end with a zero, read, return zero bytes read. So yeah, the file can be anything. It can end in a zero, it can end in a new line, it can be whatever. So return zero bytes that you're allowed to read from it instead of some number of bytes or something like that. It just means you can read zero bytes from it, this is done, it's empty. So has anyone ever used the application cat before or the program cat before? We've used that before, right? What's cat do? If I just press cat and enter, guess what? We just wrote cat and it's a utility you've always used and it doesn't actually do that much but it's fairly useful. If you've used Linux, you've probably used it a lot. So let's see what we can do with that. So we can do some fun stuff. Oh yeah, and just so you have it, so read, again just read data from a file descriptor. No end of file character, read just return zero bytes read if you can no longer read any data and if that represents a file, well then that's the end of the file but as we saw file descriptors can represent other things. So if it's a terminal it means that I closed it and I don't want to type anything else in my terminal so it behaves the same way read just return zero. And again remember check for errors I will always tell you to check for errors and you know make sure you handle them or blow up early so you can actually see oops is this something my mistake or is there something kind of intended? Right, same thing returns the number of bytes written again you can't assume it's always successful although people do so we know printf calls write and who here has ever checked the return value of printf probably no one but technically you probably should because printf actually returns the same things it returns the number of bytes actually written to but no one checks it. I'll tell you to check it I guess you don't really have to check that one but you should probably still check things and also a thing to note here is that the read when I'm reading something to that file descriptor well it's necessary that something else wrote to that file descriptor so when that program is reading from standard input which is reading from my terminal well something else had to write to that file descriptor so that's what my terminal emulator did so my terminal emulator took my keyboard presses turned it into ASCII and then wrote it to that file descriptor that's supposed to represent that terminal and then my application was able to read from it and then whenever it wrote to standard output well my terminal emulator which is again a different process would have read that and then taken those characters that information then actually you know drawn something on the screen that I can see so both things like there's two sides of communication here even with that your terminal is interacting with your application so these are deceptively powerful so what we can do there's a few rules with file descriptors and the rule in Linux is if you open a new file descriptor create a new file descriptor it will always choose the lowest number that is available so if I close the standard if I close standard input so I just say close zero well then suddenly in my process file descriptor zero will be free so if I do an open system call for a file well the file descriptor that's going to return unless it's an error is going to be file descriptor zero because that number was free so let's see what we can do so I'll have the exact same code in the body but I'm going to have this function do something with the arguments so here I will check that if there's greater than two arguments my program's not going to work I'm not going to support multiple things but I could if I wanted to and then I'll have a condition here that if I have two arguments so the name of the program and then I'm expecting a file name if that happens I will close file descriptor zero and that will free it up in my process so right at the time of the close file descriptor zero now doesn't refer to anything then I will open the second argument which should be a file as read only and this should get file descriptor zero and then my code is otherwise exactly the same I don't want to find anything so with just doing that just playing with file descriptor zero well suddenly cool things happen so if I just run open example without an additional argument it's the same thing as before but now I can be meta and I can you know give it a file name argument so what should happen if I do something like this yeah it should just print the whole file so just print the whole file why to do that well because the code is reading from file descriptor zero at all times and writing to the file descriptor one so before it started I replaced file descriptor zero with an actual file so whenever the read system call happens it will now instead read from the file instead of from my terminal so with this I see the file that I gave it and it just goes out to my terminal which is kind of cool yeah yeah so before the question is before closing file descriptor zero what does it contain so file descriptor zero in this case represents my terminal and it's something you can actually mess around and one of the things in proc is a list of file descriptors and you can see what each file descriptor refers to it'll refer to some pseudo terminal device which is like it's just something you can read and write bytes to yeah sorry yeah the convention is file descriptor zero is always standard input file descriptor one output to standard error yeah so the only reason this works is because well by convention zero was already open I closed it and then I opened it zero was available so the Linux algorithm will always choose the lowest open file or lowest free file descriptor this case it's zero so I just replaced zero in this case and now instead of representing my terminal it represents a file kind of cool so yep sorry so when I just type cat before like it did the same thing as I was doing like it just with my terminal right but like you could do cat so cat has the same thing you can give it that which is exactly what I had right yeah yeah this is like the this is the implementation of cat right cats not actually that complicated but it's super useful because everyone's kind of use it yep yeah so close just closes a file descriptor in this case I'm closing a specific one because I know the convention in general you pretty much just close whatever you open kind of like memory but in this case I know the convention yep yeah so that's a good question so here if I don't close zero what happens yeah so if I don't close zero well by convention zero one and two are going to be open they'll refer to something in this case they're on my terminal so if I call open I get a new file descriptor zero one and two are already in use so this would get file descriptor three so it just open it and then I monkey around with file descriptor zero and one and then that's it so that file would never get read or anything we just opened it for fun yeah so then if I do this and then yeah if I read in there if I read fd3 that would be the same thing I'm reading from the file yep no so your file descriptors are independent to your process so you can think of file descriptors kind of like as pointers so what they're point to will not change but you know what they point to might not exist at some point so like you could have read fail or something like that if someone if you like shut down your terminal or something yeah like if I just close zero and do something yeah it'll just be closed so if you want to screw up your program right printf prints to file descriptor one we know well at the beginning of your program if you want to cause someone your friend maybe maybe not your friend a big headache just put close one at the beginning of their program and then when they try and printf debug nothing will ever happen because the file descriptor doesn't it's closed printf would that internally have an error they likely ignore it and then they'll never figure it out and it'll be fun so yeah if you want to screw someone close one at the beginning they will never ever ever debug it that's job security people all right so with this there are some fun things we can do so in fact your shell has little shortcuts for you so that you can have even if your program just uses standard in standard out you can interact with files without having to write anything so in fact I could go back to my read write example and then there is a fun little shortcut something like this what this will do so this is part of your shell and what your shell will do is if there is this arrow it will open this file for you and then this arrow means replace standard input so remember this version my program didn't have any open didn't interact with files at all but now if I use this my shell will open this file and use it to replace file descriptor zero whatever it creates this process whoops what I write did I oh right I didn't recompile it so just with that without having an open well now this can interact with files and in fact if I write the other end well and create a new just give it a new file name your shell will create that file open it and then replace standard out with that so if I do this well now I don't get a output because standard in got replaced by open example dot see and standard out got replaced by copy dot txt and guess what what's in copy dot txt the same thing guess what that did that copied a file so right that's all it was doing so it read this file and then wrote out this file guess what that's what copying is so if you use cat and you know how to use your shell you don't need a CP command you could just use cat and go in and out right CP is just a minor convenience and other fun things we can do so yeah so then you could that's why at the beginning when I s trace things to I directed it to a file so that's why that worked and that I could cat it I could see its contents and you can play around with it in fact there is a really fun one called a pipe so that's what this character is and what this will do is take standard output from the process on the left and that standard output will be standard input for the one on the right so it essentially glues them together so now they're communicating directly with each other so if I run something like this whoops whoops wrong file build well I get the same thing and what happened well cat will open this file and then it will write it to its standard output and this will glue it so that when this process reads it will be reading from the previous processes standard out and then in this case it's the content of the file this program does the same thing as cat so it's just going to have a roundabout way to output the file again so it's going through two processes this time and in fact if I really wanted to you know you could just make a big list and same thing is going to happen right I'm just creating more processes it's going through more processes I'm wasting more memory I'm doing more system calls but it's going to look the same any questions on that one yep so here so in this case what this operator does is essentially glue this process of standard output to this processes standard input so anything that this process writes to file descriptor one well when this calls read on its file descriptor zero it's going to read whatever that one wrote sorry yeah so one's writing information so whatever this one's writing out this one is reading in because of this little operator it's called a pipe so it just glues two processes together so in fact you know I could do that and that's why you can make useful utilities so this little utility just reads from standard input and then counts how many lines there are if I give it the argument dash L so I can figure out how many lines are in this file so I just pipe the contents of the file to that and then it just will read it read the contents of the file and tell me how many lines it has so kind of fun yep yeah so I could pipe yeah so question is well instead of that you know in fact I could just do this for this example so instead of doing that I could have it go to a file and then take that file and go here here I'll just cut out the middleman and I could just give that process replace its standard input with the file so I could just do something like that I'll get the same answer so too long didn't read you can just write utilities that just deal with text deal with standard input and standard output and then through messing with the shell they can magically handle files and do whatever you want with them because they don't care right they just read and write bytes they don't care what they actually represent your terminal a file if you wanted to you could replace it with like a network thing so just send some bytes over the network you could do this across computers lots of fun stuff all right so now to go into signals so signals are another form of IPC which is now new to us basically they are interrupts we all remember our good friend interrupts so how many of you have run a program you don't know how to stop it so if I didn't tell you about control D and I ran something like this how would I stop it yep control C right control C then magically stopped do you know how control C works guessing interrupts yeah good guess but so far control C has been like some magic that's just saved us right yep yeah it's a signal so control C is actually a signal so whatever you press control C you are sending a the kernel is going to send a signal to that process and your terminal is going to actually request that and then the kernel because it's IPC the kernel is going to go through with that request so when you your process receives a signal the kernel interrupts it it will run a default handler so there is an interrupt handler that you didn't write there is a default one each signal the handler just takes a number each signal gets a number so each number is supposed to represent something different control C sends a signal called sig int which is just supposed to stand for a signal interrupt in Python you might see it as a keyboard interrupt or something like that it's the same signal they just tried to make it a friendlier name and by default the default signal handler will exit your process with an exit code of 128 plus the signal number so if we go look back in our code this lower right number here that represents the exit status of the last process I just ran so I just told you that the default signal handler it's 128 plus the signal number so if I got 130 what is the number of the signal oh the question what's the significance of 128 and they're just by convention you like exit with 1 2 3 and use that so it is picked 128 it's like yeah no one will use things by this point so we'll just use it so yeah what is the so if that's it yeah what's the number of sig int well two right hopefully 128 plus two equals 130 so these aren't hex or anything this is just good old decimal so we can figure out that the sig int of interrupt from keyboard signal handler two so we'll see an example of how to register your own signal handler this will also it's a good way to mess with your friends that haven't taken this course yet so like your interrupt your interrupt handlers for other courses they don't return a value or anything and the signal handler on Linux just takes an integer and then returns nothing the integer will represent whatever signal number you're actually supposed to handle so these are a few of the signal numbers two nines a good one sig kill it's terminate immediately so we'll get into that fun bit and then there's one everyone's probably encountered so this is your seg fault you know dereferencing a null pointer that is actually just a signal and by default it's going to do just do the default handler and that's why if you seg fault your program you'll probably see the exit code is 139 and this is why and then in fact if you want to you could you know catch when a sig fault happens and then you could print like I'm gonna die now or something like that so you can actually have a signal handler for whenever you dereference a null pointer if you really want and then there is this one 15 seg term so that is by convention the nice way of saying hey process please stop and so this is please stop and this is stop so we'll see some example of that so let's go into the example so in the signal example I am going to use the same program we've been using so this is that open example that just reads from zero and writes to one so the only difference here is I'm going to call register signal so I'm going to register two signals sig int and then sig term which is supposed to be you know pressing control C and then asking nicely so I'll register both of them oops in the register function well there's a struct sig action that you have to fill in things will you might see depending on how you tackle one of the labs some other fields but the main one is just registering what function you want to run whatever signal happens so this is a function that just zeros that struck for you and then there is a field called handler that takes a function to run whenever the kernel sends a signal to your process then sig action is the system call that actually registers it with the kernel so sig action takes a signal number you want to install this handler into and then the action you want to be done so whatever function you want to call and then some optional arguments that we set as null here again we check for errors otherwise if it's not an error it means the kernel has accepted your request and now whatever you get that signal your process is going to run your function instead of the default one and what is my default one I'll just ignore the signal be like yeah I don't no thanks don't need you don't care so let's run that so now all of a sudden I have installed some signal handlers one was sig int so now if I press control C what should happen it should just print off I'm ignoring you I'm not gonna stop in this case it's a good thing we checked for errors because we got an error from read where's read ah so our error was here weird we got an error and it says interrupted system call so this is why you have to you should be a very proactive programmer because we didn't know that was possible that's actually not a bad thing all interrupted system call means like a signal happened and you should just restart your system call so that's actually not an error and the condition that we should probably handle if you go look at the documentation you'll go see that the error no gets set to E in there yeah the curl developers are not the best at names that just represents that interrupted system call so here in my loop I check if there's an error if there's an error that I can check error no and then if I see I got an interrupted system call I'll just continue and it'll call read again and then carry on its merry way so now when I run this and I press control C just ignores me and it ignores me and it ignores me and it ignores me and it ignores me how do I stop it huh control D okay without pressing control D control D's cheating so pretend this is like your friend trying to exit Vim yeah use another interrupt which one yeah nine's probably a good one so let's see so I'll have to open my terminal to recover from this so if I want to send the signal to a process I need the processes process ID I didn't print it out but luckily there's this fun little program called pit of that you could right now if you're done lab one so pit of just takes a program name signal example one then it goes through the proc directory and tries to see any matches and it'll print any matches so if I have more than one of the same process name running I'll get multiple numbers however many process I have running in this case I just have the one so the system call which is also a program to send a signal there's a system call to send a signal you would think it would be called something called like send signal would make sense in fact it is called kill so kill is the system call to send a signal to a process by default if you just give it a name it will ask that process nicely to exit so here I just tried to kill that process and asking it nicely to exit sends it signal 15 which I promptly ignored so I just ignored it so if I want to really end that process I have to force the issue so I can force the issue if you give it a dash and a number it will send that number of signal to the process so remember nine that was sick kill and that was exit not nicely so you are not allowed to ignore that one that one the kernel handles for the process and then if you do that it is dead so that is the not nice way of killing a process because otherwise you're free to ignore it any questions about that yep question is is there any other signals you cannot ignore this I believe I'd have to double check this is the only one you definitely cannot ignore because the kernel handles it for you if you try and install a signal handler for it you'll get an error saying you can't do that yep yeah we'll get to that in a second do you quit you're good okay so through that yeah so kill dash nine was the not nice way of asking a process to end and this has a caveat well if that process is in in that uninterruptible sleep state well unfortunately kill dash nine doesn't kill that process still and it's a pain in the but you can just never kill it until the kernels done with it all right so this goes back to the last lecture for wait so most operations you probably want non-blocking so you don't want to wait for anything to happen you just want to ask we I told you about before but now we'll see an example for it so a non-blocking call returns immediately and just checks if something happens so to turn weight into a non-blocking call we use weight PID and then one of the options we give it is W no hang and that just means just ask if I have a terminated child and return immediately so returns and then it'll return zero if you don't have one if you do have one it will return the process ID so this kind of goes back to your more architecture course remember pulling and interrupting well now if I have something like this if I want to make some poor decisions well I can just pull it over and over again so here is going back to last lecture we'll just do a fork create a child that sleeps for two seconds and then exits and then in the parent we will declare weight PID will initialize a zero declare some space to a W status and I'm going to create a variable that's going to call how many times I actually call weight so I'm going to have a while loop that says while weight PID equals zero so that means I don't have a child terminated yet I'm just going to pull over and over again and ask do I have a terminated child do I have a terminated child do I have a terminated child and then only break out of this loop whenever weight PID returns an actual process ID that represents the child process then I'll do the same thing I'll check for errors and then print its exit status so if I do something like this whoops it's called weight pull example so it's pretty wasteful so system calls are slow and I made 300 and almost 60,000 of them so that's probably wasting a lot of time on my CPU that's 360,000 kernel system calls probably not good so if I want to still use pulling and reduce the number of attempts I have what should I do to reduce that the number of attempts I have to make yep yeah pull less frequently how do I pull less frequently yeah I should probably just you know probably just give it a break right something like that so if I do that attempt one, attempt two oh I got it yay so that time I got lucky some other times I might get to attempt three there we go so in this scenario well I've a reduced number of attempts but it's also not that great because I'm essentially trading response time to lower that number so because I'm waiting a second well I might get super unlucky and then I call weight and then I start sleeping for a second then my child immediately dies whenever I first call sleep in which case I'm not going to respond to that for a second which is going to be pretty slow and average case hopefully it wouldn't be that slow but worst case you'll have to wait for over a second and the problem is whenever I lower that number well the number of attempts I have to make is going to get greater but there's always that trade-off of response time and then number of attempts you have to make so pulling's not that great but luckily remember last lecture I said the kernel pokes you if your child dies or terminates well the pokes you means the kernel sends you a signal so the kernel actually sends you a signal called we register it called sig child so it's a special number and it means that you your one of your child processes has now terminated and you should probably do something about it so the default signal handler for this signal is to just ignore it so the kernel just ignores it for you which is why we haven't encountered it before but it actually does send a signal to your process whenever a child process dies so in this case I can register a signal for a child and then in my main code in my parent just to show that I'm not I'm doing something else I'll just print off I'm going to sleep and then I'll go to sleep for a very long time this is to represent the parent process actually doing something useful instead of sleeping so it's actually doing something useful and instead of just doing a wait trying to pull or just blocking it's just going to depend on a signal so in the signal handler I'll check what signal number I got if it's not a child I don't really care I'm just going to ignore it otherwise I know that I'm being poked because one of my child processes has terminated so I can do wait PID and you might notice here I'm not waiting on a specific process ID so remember the wait system call waits on the first child process that terminates well if I use wait PID which I have to in this case because I have to use this options for the non-blocking one if you give negative one as an argument for PID it behaves the same as wait so if I give it PID negative one to wait on it will wait on the first child process so in this case I just call the non-blocking version in this case because the kernel is actually telling me that I have a terminated child process which is remember our terms currently a zombie then I whoops did I copy that then I just go ahead and see its exit status and print it off like nothing happened or like I did before so if I run that the parent goes to sleep and then it gets a signal and then that signal handler runs I clean up my child instantly it's now no longer a zombie I acknowledged it we're all good so yep sorry for the wait PID yeah if there was no child yeah so if wait PID doesn't have a child or wait doesn't have a child it just returns negative one and sets narrow no that says you have no children yeah yeah so the question is how's the kernel knows that the process is terminated well the kernel is the one managing the processes so it would have called exit so then the kernel knows it calls exit it has to handle it as part of handling it it can send a signal directly to the parent right so it doesn't have to the kernel doesn't have to pull or anything because you have to tell it you're exiting and then it knows about it yep okay cool so this is going to be a word of warning because this will prep us for having threads and things like this so you might be tempted to in your signal handler do some cleanup or something like that so I will have a signal handler that in this case closes a file so in this case I'll declare a global variable and then in my main I'll open a file and then in my signal handler I'll be like okay I'm gonna close that file all right got four minutes all right so I'm gonna close that file so I'll check if it's negative one if it's negative one oops I'll assume that I don't have an open file so I don't have to close anything otherwise I will just close that file check for errors and then print I close it and then I'll artificially wait a bit and then I will finally exit so if I run this program well I have to give it a file so I printed out its process ID just to make my life easier and then I said what I said I opened a file descriptor so in here I can go ahead and ask it nicely to close in this case I registered that signal handler with the same interrupt and sig term so I can ask it to close and it goes tries to close and I can hit control C and I interrupt the interrupt so if I interrupt the interrupt that's bad because it will try and close the file again and now it gets bad file descriptor because it has already closed that file descriptor you can't close a file descriptor two times just like you can't free memory from malloc twice so what happened well the first interrupt happened I printed I closed I printed this and then I slept and then while it was sleeping I generated another interrupt before it happened and it started executing this function again did this print line closed and then it encountered an error because I already closed it so this is a thing you have to deal with with doing signal handlers so signal handlers are a pain in the butt you have to be really careful with them and we'll learn more about this once we get to threads but just so you know in this case signal handlers the term for this is they have to be something called re-entrant which means in the middle of executing them I should be allowed to execute the same function again and nothing bad will happen so there's a few rules for that like it shouldn't use any global variables it should only use local variables things like that but we will get into that later but that's just a taste of things to come we don't have to worry about that people really hate signals because they have you know signals can happen within signals which is kind of a pain in the butt and we'll work around some of those issues as the course goes on then another thing so on a risk 5 CPU there's three terms for interrupts based off where they come from an interrupt interrupt is basically from hardware an exception is from like executing a legal code and a trap can be you know that requested trap whenever we do a system call or it could be you know it could also be caused by an exception if you want and just an example this is just terminology you don't really have to know it that well system call would be an example of a requested trap so that's the end of this lecture and you know we learned that hey we can kill kill some processes now so you might wonder what happens if I do something we learned that a knit was pretty important and then you can't ignore a sick kill so you might ask yourself what happens when I do that yep yeah ah ha nothing happens so one is actually protected so it is the only exception to that rule so there are no default handlers for process one so if you want to kill one while process ID one would have had to register sick kill and then closed itself and actually registered for it so you don't accidentally do something stupid like this so that's it just remember pulling for you we're on this together