斯坦福-CS110-计算机系统原理笔记-九-

88 阅读1小时+

斯坦福 CS110 计算机系统原理笔记(九)

P5:Lecture 5 Execvp Introduction - main - BV1ED4y1R7RJ

Okay, welcome back。 There are fewer of you here than before。

I kind of figured that might happen over the course of the quarter。 Thank you for joining us today。

So today we're going to continue with talking about fork and we're going to talk about this new thing called exec CVP。

which is generally why we use fork as it turns out。

And I will also talk about the assignment that's going out that just went out today。

Speaking of fork。 So, I, let's see, how can I do this? I took, so I was。

I was finishing some things up today and then I took a little break and I went to Reddit。com。

Our programming, because I look at the programming ones, right? And let's see。

the first thing that's there is this thing called a fork in the road。

And I went, oh, cool。 We're talking about fork in class today。

Let me click on it and see what it says。 What do they must be talking about?

Some cool things on, on fork。 And then let's see, is the actual paper here。 I believe it is。

There it is。 Okay。 So I go great。 This thing called fork and order。

The received wisdom suggests that Unix is unusual combination of fork and exec。

which is what we're going to talk about today, for process creation was an inspired design in this paper。

We argue with that fork was a clever hack for machines and programs in the 1970s that has long outlived its usefulness and is now a liability。

And I went, oh no。 Here we go。 And it talks about。

and so I read this paper and it talks about how fork is actually not the best thing in the world to teach。

Like as the first thing, which is exactly what we do in this class, is teach the first。 It says。

as designers and implementers of operating systems。

we should acknowledge that forks continued existence as a first class operating system primitive holds back systems research and deprecated。

As educators, that's me, as educators we should teach for as a historical artifact and not the first process creation mechanism students encounter。

Sorry。 Sorry。 So, by the way, this is written by Microsoft who, for what it's worth。

does not produce a Unix operating system。 They have their own operating system。

But it actually has some interesting interesting things to say about fork and about how there are some things that we will actually talk about where it does produce not the best like answer to kind of your questions。

For instance, for happens to be what we call not threads, say。

and we will talk about threading a little later in the course。

And it means that you can't mix and match forks and threads in certain ways。

And that's kind of not a great thing。 So, as an educator。

I'm presenting this to you as a historical, what did they say? A historical artifact。

So take that for what it is。 You still have to know it for the mid-term exam。 And who knows? Maybe。

maybe we will retool the way these things go as we do this a few more times in the course。

Anyway, I thought that was interesting and a little sad that I thought it was going to be this great article。

There you go。 Alright, so the other day in class, somebody asked a great question。 I figured it is。

I apologize。

Someone asked a question because we were putting up all these diagrams about file systems。

And the question was, "Hey, these various process control blocks that the operating system keeps track of that actually have file descriptor pointers basically in them can point to file descriptors in the open file table。

And there is one open file table。 And those descriptors in the open file table lists。

has some information about the various files that are open, including the mode。

whether it's read or write, read only, etc。 It also has the cursor position in there。

And the question was, "Wait a minute。 If there is only one copy and two files are allowed to simultaneously read a particular file and be at different places in the file。

how does that work if there is only one copy in the open file table?" And I said, "Well。

it's probably true。 There is more than one copy。" And I wasn't sure。 So I went and looked it up。

Indeed, the open file table, every time you call open, creates a new entry in the open file table。

which has a cursor for it。 So if two programs call open on a particular file or two processes even。

call open on a particular file。 You do get duplicates here。 And I said, "Well。

the reason it's only one is because you don't like all these duplicates。

" The difference is that there are, when we do fork, for instance。

we know that you copy all of the memory and therefore they will indeed point to the same place in the open file table。

So it is true that processes can point to the same open file table record。

which will have one cursor where in the file you are。 But if two programs open it up。

you do get duplicates in there。 So that's how you get two different cursors。

It's clear things up a little for the people who are curious about that。

Not the biggest deal in the world, but that's how this ends up working。

And I thought I'd at least give you the information I looked up on that。 Okay。

So hopefully first assignment is finishing up due tonight, midnight no extensions remember。

If you do have trouble finishing up there, I believe there are still offsours tonight。

so you guys should be able to get to there if you need to。

The second assignment, which is the next assignment。

in fact the first assignment where we're really testing things that you probably haven't seen before。

namely the file system stuff。 And it is on the file system we described in class。

specifically the Unix version six file system from the late 1970s。

Okay, why do we care about that file system? It happens to be a decent file system。

It happens to be relatively, relatively straightforward to understand for what it's worth。

And it is just a good one to practice manipulating data structures needed for file systems。 Okay。

the test data that you're going to read from the actual test disks that we have are literally built bit for bit representations of a Unix version six disk。

So somebody had a universe version six system up and running somehow。

probably in a virtual machine or something, and they copied the disks off and then put it in this assignment。

And that's where they came from。 And so you're going to have to figure out how to read those disks specifically。

Okay, we've given you some scaffolding for that obviously, but you're going to have to do that。

The lectures from lecture three, very important to understand。

because I think we went over the specifics there。 And then this section 2。

5 of one of the textbooks that we have, you can have a link straight to it。

is where you get even more specific information about the file system。

So if you have questions about specifics, go there first, then ask the question on Piazza。

come to Office Hours, etc。 So the assignment is you've got this disk image and you have to be able to read and write files and find files on those that system。

Okay, and then you should be able to read the files and read any black in that file and so forth。

You'll primarily be writing in four different files, so you have code in four different places。

We do, we suggest that you do them in this order。 First you do the functions in iNode。c。

and you can imagine what that is that's manipulating or like using the information in an iNode to get out information about the file blocks。

And then file。c which kind of puts another abstraction layer on it and says here's a file that you should be searching for。

Okay, and then directory。c which is also kind of for manipulating the actual directors。

I guess file。c is for like reading a specific file。 Directory is going and searching for the file。

Remember you have to search for root and you have to search all the files in there and then one after the other until you find the path you're looking for the file at the path you're looking for。

And then finally path name is actually doing the searching as well。 Anyway。

we say go through and note that order and after, I guess after the first two you'll have some functionality that you can test with either sanity check or by running the program。

And then as each one, as you finish each one you get a little bit more functionality。 Okay。

so that's the basic idea。 This assignment is in C, not C++。

You don't have access to maps or sets or vectors or anything like that。

You've got to deal with low level arrays and you have to deal with structs and so forth。

So no C++ stuff in this assignment。 Not the end of the world but it just means that if I think I might be able to do that with a map。

you can't。 No maps allowed in this assignment。

Okay, the basic ideas I said you'll be able to locate and read files in this file system。 All right。

you'll be using functions that we've written for you and you're doing your own。

The one that I mentioned here that you'll use a bunch of times is called disk image read sector and it takes in a few parameters。

It takes in a file descriptor basically。 It takes in the sector number or I guess it's a file system。

It's kind of what it is。 It takes in a sector number and then it takes in a buffer and that will read a particular sector off of this disk image and we've written that for you。

Okay, you don't need to worry about that part of it。

Finding the right block to read is the important part。

A couple things to notice or to know about it。 The function will always read disk image underscore sector underscore size number of bytes。

And you ask yourself, well, what if the file is only 12 bytes long?

Is it always going to read this many and this many happens to be 512 as we've discussed? Yes。

it reads that many because the non first 12 bytes are all garbage but the disk has to read 512 at a time。

So you're responsible for saying, well, which of those bytes are actually relevant and there's a little bit of arithmetic involved with that。

Okay, so you're going to have to think about those sorts of things。

It's critical that you read through the header files for this。

The header files are actually not that big for this assignment but there's lots of constants in there you're going to need。

For instance, root I number。 It's a constant in there。 Now we talked about that。

It happens to be one and that's the I number for the root。 But you need to use the root I number。

You could use one but in this case it's defined for you。

There's a directory entry struct and so forth which has a file name in it and all that。

And these are the things you learn from the header files。 Okay, so make sure you read through that。

And the assignment is up right now already by the way。 You can go take a look at it。

It's due next Thursday for what it's worth。

One function I did want to talk about。 This seems to be the one that gives people kind of the most trouble。

And maybe mainly because they don't really understand what it's asking before they start trying to code it。

Not a good thing。 You should try to understand what you're trying to code before you do it。

But here's what this function is。 It's called I node index lookup。

And you actually get passed in a Unix file system。

We have already created and initialized that for you。 You don't need to worry about that。

By the time this function gets called you will actually have an I node pointer itself。 Okay。

which is populated with the details of the I node。

Remember it's got all the blocks that have block numbers in them and it's got the information about how big the file is and if it's a large file and so forth。

Okay, and then it's also got a block number that you're searching for。

This is the block number of the file itself for however many blocks that file takes up。

So if it's a 512 byte long file it takes one block and so there would only be a block number zero。

Right? Block index zero。 If it's a two, like a 1024 byte file it takes up two blocks。 Okay。

the I node does and the I node takes up it's just regular, what did I say 32 bytes or whatever。

But the actual blocks in the file are what this function is going to return the index into the actual file that you're looking for。

Okay, so that's the。 Yeah。 If I say okay so I have a file that's I have a file that's I have a file that's 1024 long。

Right? Okay。 The block number is going to be let's say one, which is the block into that file。 Okay。

and you need to find the index of that block in the file system that you're looking for。 Okay。

In other words, the, I'll show you and you'll see how it works in a second。 It is a bit confusing。

Yeah。 Are you coming from zero or from one? Are you coming from zero or from one? Good question。

You can do either but you have to make sure you know which one when you're actually coding it up。

Yeah。 Okay。 In this case, it would probably be zero。

But here's the example I just wanted to show you。 Okay。 Here's the example I wanted to show you。

Let's say that the I node indicates that the file is 180,000 bytes long。 Okay。 180,000 bytes。

And let's say the block number is 302。 Here's what that means。 If you go 512 bytes at a time。

the 300 second one is the one you want to return。 Okay。 See。

it's the block that you will then look up in your file system for that one。 Okay。 Remember。

a file can have a block here that's the first 512 bytes and then it went over here。

That's the next 512。 And then one over here is the next 512 or whatever。

We're looking for the 300 and second one of those。 The block number for that。 Does that make sense?

What we're trying to look for? Yeah, hustle? How do you get the other ones? Well。

you call it once for each time。 So, I mean, if you want to get。

let's say the file had 302 blocks in it。 You have to call this function 302 times to get each individual index for each one。

You see how that works? That's the only way you can do it。

There's no linked list here that goes between them all, right? You've got the seven first。

or you've got the eight file number or the block numbers inside the I node。

And then you've also got the indirect ones as well and the double indirect one as well。 Yeah。 So。

just to be clear, this isn't necessarily the three hundred and second amount of data in the file。

No, it exactly is。 It's the three hundred and second amount of 512 blocks data。 That's what it is。

Okay, so it's not that。 So, it could theoretically be somewhere before that we're going to first block。

It could be anywhere in the file system outside the I-nodes。

but it's going to be anywhere in the I-file system。 But the, it's the。

if you're walking through the file, you first need to go find a block for the first 512。

then you need the next 512。 Then you need the next 512。

We're trying to find the three hundred and second 512 block, right? And to find that。

you have to know a lot about the file system to figure out how to follow all the little ways around to do it。

That makes sense? Yeah, so that's why this function is confusing because people don't quite get。

but hopefully we're clearing that up now。 Okay, all right。

let's just look at how we might actually do that。 Okay, let's say, as I said, the 180,000 bytes。

we're looking for block number 302。 Okay, we're looking for the third。

three hundred and second block of 512 bytes of data。 Okay, and remember。

and this actually isn't going to matter so much for what we're doing, but blocks are 512 bytes long。

Okay, and how do you find the sector index? That's the actual。

what sector do I need to tell the disk to read to get that third。

three hundred and second block of information? That's what we have to do with this function。

Okay, well, what do you have to do? Well, first things first。 Remember how I know, it's right。

there's those, those eight different blocks that have been there, right? One, two, three, four。

five, six, seven, eight。

And each one of those, if it's a small file, that's going to have a block number that's going to be the first 512 bytes。

That's going to be the next 512, the next 512, next 512, et cetera。 All the way up to 4096。

because that would be the only amount you can keep in eight blocks of, "Hey。

here's the different blocks。", So, the first thing your function better do is figure out if this larger small file。

because it's going to change what these numbers actually refer to, if it means。

if it's a large file versus a small file。 It's a small file, easy, right?

You know that it's going to be what that block, it couldn't be 302, by the way。

because 302 would be two, it could only be between zero and seven, if it was a small file。

But anyway, all you would do is go to that i-node, read the number off there。

and that's the number that you send back to the return。 In this case, it is a large file。

so you know you have indirect addressing, which changes things。 Now。

each one of these block numbers here, they refer to a block in the file system。

a segment in the file system。 But those, if when you go there, they have 256 numbers in them。

each one of which refer to a block where data is。 Okay, so, what does that mean? Well。

the three hundred and second block of your data is going to fall into the second indirect block。

Not this one, but this one。 Do you understand why that's the case? There's 256。

this one is going to refer to, because this points to a block which has 256 numbers in it。

and those are the first 256。 And then the next block has the next 256。

and 302 happens to be in that one。 Ready with me there?

So you see how you're going to have to start thinking about this? Okay, so what does that mean?

Well, you are going to use disk image read sector to read the sector that is in, and I erased it。

one, two, three, four, five, six, seven, eight。 You're going to go read the number here and read the disk image from that number。

which is somewhere else in the file system。 That is going to give you a block that's going to get 512 bytes。

which is going to have individual block numbers in them。 They are all Uint_T, they are shorts。

unsigned shorts。 Okay, you are going to then find the 302nd mod 256 short in that list。

and you are going to return the number there, which refers to the index in the block that you are looking for。

If the block number that you are looking for happen to be in this eighth one here, well。

you have another level of indirection, so your function better account for that as well。 Okay。

so you see how that works? Great。 I'm sure you'll have questions about that as you start doing the assignments。

but at least at this point, hopefully you're at least going, "Oh。

I see what this function is trying to get me to do。

Now I kind of have to go through all the calculations to do it。" Good。

What questions you have on that?

Okay, good。 So I'll just delete all the Piauts questions that come up about it。 So you also。

of course, for this assignment have to search through the directories to locate a particular file。

You don't have to follow symbolic links。 There's no worries about what's going on with a weird symbolic link。

You don't need to worry about that。 You can ignore those。

You do need to consider directories that are more than 32 files。

because they could be as many files as we want, basically。 Do not think that that's a special case。

Don't say, "Oh, no, it's more than 32。 I have to handle it in some special way。

" You have already written, presumably, you will have written at that point。

functions that are able to read an entire file one block at a time。 Okay。

that's kind of the function we just talked about。 Like, "Go find the next block in this file。

" You do not need to make this a special case。 Too many people try that sometimes。

It's not necessary。 Just deal with it like any other file and say, "I want to read the next one。

read the next one, read the next one。" And you'll be fine。 Okay, they all fit in 512 bytes。

It's not like a file entry in that can be, like, across two blocks。

It's not going to happen because of the way they're written。 Okay? By the way。

you will probably need to lay some structs over this data that you get back from these things。

Right? There's going to be structs of different things like directory entries and so forth。

Take those and kind of apply them in a CS107 sense to the data。

and you'll be able to read them off directly。 Okay?

So that's the kinds of things I could think about。 All right? Don't forget, and this is one thing。

file names are 14 characters maximum。 And if they happen to be exactly 14 characters, well。

there's no trailing zero at the end。 Why? Because they wanted to save one byte。

For all those 14 length files, they wanted to save one byte and said, "Oh, if it's 14。

I don't need to worry about it。", So what that means is if you're trying to compare two file names。

like one that you're looking for and one that's in your data, you better not use Stercomp。

because Stercomp looks for the n zero on the end。 You might want to use Stercomp or do some other method to say。

"Hey, is there a zero at the end there?", Okay? You probably shouldn't do Sturlenn either。

because Sturlenn, all those string functions presume that it's a properly formatted string。

If you have a 14 character name, not properly formatted string, but still in the file system。 Okay?

Little nuances like that。 That happens in programming。 Like we try to save some space here and say。

"You better document it if you're going to build something like that。" And of course they did。 Good?

All right。 That is assignment one。 It's a relatively advanced assignment。 I mean。

it's the first assignment where you're doing new stuff that you've seen only in this class and haven't done before。

Please start early。 You hear that in every class。 Come to Office Hours, Ask Piauts questions。

obviously。 The CAs are not going to look at your code for this。

So if you have questions about your code formulated in some sort of conceptual way and say。

"Here's what I'm trying to do。 What am I doing wrong or what kinds of things do I need to think about because I'm trying to do this and draw some diagrams and so forth that the CAs can help you with。

They're not going to like--they will look at error messages and they'll try to figure that out。

But they're not going to dig into your code and try to figure out what you're trying to do。

They're going to ask you, "What are you trying to do? Here's the conceptual framework you find it。

" Okay? All right。 Other questions about that? The assignment? It's kind of a fun one。 I mean。

it's definitely a pretty media assignment。 As far as it goes。

Okay。 All right。 Let us go back to multi-processing。 Okay。 So here's where we left off on Monday。

On Monday we said, "Hey, there's this new function called fork。 It's actually a system call。

It's called fork。 It takes no parameters。 And it just returns to you a number integer which is either the PID of the child process that for creates if you are the parent that created that called fork or it returns zero which is the value saying basically you are the child。

It is not the PID of the child for the child。 Okay?

Because you want to be able to differentiate between these two。 Okay?

And then we started looking at some of the various programs about this。

And we looked at this one called fork puzzle。 Right?

And we found out that it turns out that with fork puzzle you can end up with data and going in--or the output in some order that you can't predict。

Right? If I do it again it'll probably be a slightly different order。

And in fact this time it went and we found that the parent finished before one of the children。

And so the command of the crop came back and then they got this weird D at the end。

And it was kind of ugly and kind of things that you don't necessarily want。 Okay?

We want to make this a little bit more predictable if we can help it。

And specifically we want the parent to be able to wait for the child。 Okay?

We want the parent to be able to say I'm going to wait for any children I create to finish。 Okay?

So how do we do that? Well, we use this system called weight PID。 Okay? Weight PID。

Weight PID has a few arguments of course and it has an argument that's a PID。

Most of the time as it turns out we won't actually put a PID in there。 You'll see why later。

But it stands for the weight set which can be one PID。

Like I am looking for a particular child or it can be another number like negative or like yeah negative one which means wait for any child。

And then whenever any child ends this will continue。 Okay?

And it does stop the parent until a child ends。 Okay?

The parent just sits there and waits and doesn't do anything。

And by the way it doesn't use up lots of processor time either。 The kernel。

the operating system says, "Oh, you're waiting for the child。" Okay。

just go to sleep for a while and it doesn't do any processing which is a good thing。

We like that in our programs。 Okay? So that's that。 It also takes an address of a。

get to your second one second。 It takes an address of a status。

So you pass in the address of an integer and it will populate that integer with your。

with the result of the weight PID call。 Which can be an error or it can encompass the return value and so forth。

And then finally we have the options which are basically a bitwise or set of options。

For now we're going to make it zero because we're going to get to those later。 Okay?

There are no options we're going to worry about right now。 Question。 Good question。

If the child makes a child, does the weight PID wait for that? No。

The child can only wait for its own child in that case。

The parent doesn't necessarily know that its own child。

that its grandchildren or it can wait for those。 No。 It does need to wait for only its children。

Good question。 All right。 Anything else on that? Let's see an exact, yeah。 That's a good question。

If the, okay, you're getting into the weeds here。 If the parent has a child and the child has a child。

the grandchild does not have any relation to the grandparent to the extent that it will wait。

When the parent's child ends, even if the parent's grandchild keeps going。

the parent will get notified。 So, no。 It doesn't sit there and wait for any of grandchildren to finish。

If you use weight-purity for both, then the child can't finish until its child finishes。

which means the parent will not finish until the child finishes。 Yeah。 So。

you can string them along like that, but you have to be a little careful with that。

But don't think that somehow the grandchildren all get encompassed into the parent。 Yeah。

Good question。 Okay。 Yeah。 Yes。 Good question。 You're saying you're passing in the child that you want to finish。

In this case, if we pass in a PID, not negative one, for instance, then it will wait for that child。

Correct。 And you can't pass in one for your grandchildren。 It doesn't know about that。

You only pass in ones that you might know。 They are direct children and yours。 You'll see。

Let's do an example and then, and then we'll see。 Okay。 By the way。

the return value is the PID of the child or negative one if there was no。

if there were no children available still running, because that could be the case too。

If no children happen to be still running and you say wait, PID for a child。

it will return negative one。 And that means, oh, there weren't need to wait or that one in particular had already ended。

Yeah。 Before you're done using the way PID that one of that child could terminate before it actually。

Good question。 Doesn't mean that your to the child could terminate before you even call it a PD。

Absolutely。 Yep。 And that's okay as it turns out。 Yeah。 By the way, PID also does some cleanup。

It makes it so the child process does get cleaned up。 So we actually should do a way PID in general。

We didn't in the program before because we hadn't learned about it yet。

but you should do it in general。 Yeah。 Again, we'll see some examples in this。 Yeah。 Sorry。

What's the question again? If you call wait, PID on the root or well, let's just put it this way。

If you the if you call wait, PID on, you should call it only on your children or use negative one and we'll get to what that is in a bit。

If you only call it on the children, if your children already ended。

the return value will be negative one, which is fine。 You'll be able to catch that。

Or it will give you back the PID saying, hey, yeah, that child just ended。

All you really need to know about that。 And we'll see how it manifests itself in a minute。 Yeah。

What happens if you add a property that owns PID and you feel waits at? If you add your own process。

it is not a child of you, so it'll kind of ignore that, I believe。

So you're not going to wait on your own PID。 So what would it play?

I don't think anything will happen, actually。 Like。

I don't think it'll crash or wait forever or whatever。 But no, I don't think we can try it。

but I don't think that's what's going to happen。 I don't think anything will happen as it turns out。

It'll probably return negative one saying, no, that one's not even in your child's set at all。

It's probably what will happen。 Okay, let's look at an example。 Okay。

What we're going to do is we're going to do an example called separate。

I don't need to do that right now。

We're going to do an example called separate, which is going to use some of this information。

this wait PID, so that we can actually do something where we know what's going to happen and the ordering is going to happen in it。

Okay, so here's what we're going to do。 We're going to say print F before。

So this is the first thing that's going to happen is going to, it's going to print before。 Okay。

and then we're going to say PID T, PID equals fork。

and that's going to do this process separation business。 Okay。

and then we're going to say print F after。 How many of these should we get to, right?

Because now we have one extra process and both are going to do that。 Okay, if PID equals zero。

what does that mean? Child, or the child。 Okay, if the PID equals zero。 Okay。

you're going to want to say something like print F, I am the child。 Okay。

and the parent will wait up for me。 Okay, it's kind of a nice analogy in that sense。 If you want。

Okay, and then we're going to immediately return if we are the child。

and let's just return 110 for CS 110 or whatever。 It doesn't matter。

We're just going to show that it's not zero。

Just the return value。 You can actually get the return value from the。

you can get the return value from the return when the child ends。

which is kind of nice because maybe you want to know what happened with your child that it ended appropriately or not。

or maybe you'll pass some information that way if you want to。 Okay, all right。 At this point。

we can put an else。 This else is not strictly necessary because of the way we've done our program。

right? It will always, the child will always return at this point。 Okay。

so the else is not really necessary in that case。 But anyway。

the point is that it's now we're saying we are the parent in here。 Okay。

and the parent is going to have a status。 Okay, and the parent is going to do wait PID。 For now。

we're just going to wait for the actual PID。 We're going to pass in a pointer to the status。

and we're going to pass in zero for the options。 We'll get to what the options mean later。

Okay, all right。 After this, this will wait until the child finishes。 Okay, and then it will, yeah。

question。 The status doesn't need to be initialized。 It's going to pass in a pointer。

and it's going to be changed。 It's not used。 The function is not using the value of status。

It's populating the value of status for you。 Okay。

it's another way to get kind of two return values, if you will。 The status and the actual PID。

It's kind of the C way of doing that。 Okay, so we're going to do that。 Now。

we're going to check some things about this。 Okay, we're going to use a macro。

which basically is an, you can think of it as an inline function。

We've seen a little bit of those before, but it's called WIF-exited。 Okay。

now I know that looks like wife exited。 And I always say that when I think it because I read it and I go。

oh, that means, but it's not。 It means WIF-exited。 And what it does is it checks the status。 Okay。

and if the status is, if that macro returns true, it means, okay, it was fine。

It exited with a regular old return value。 I'll show you what that means when it doesn't happen in a few minutes too。

Okay, and what you can do is you can print F and you can say child exited with status percent D。

Okay, and this is going to be the actual, it probably shouldn't be status should be something like the return value or whatever。

And then you get another macro, which does exited status, sorry, W exit status。

And that's going to give you the status。 It's just reading off these bits because the。

the actual macro or the status variable holds a whole bunch of information in it。

Part of it is the return value。

Part of it is the other information about how it exited and so forth。 Okay。

so that's what we're going to do there。 All right。 And then we want to do what?

We want to do an else on this case。

So let's say that we had bad news。 The child didn't exit correctly。 We can print F child terminated。

Ab normally。 Okay, something like that。 Okay。 And then we've got that。 And then after this。

we can just return zero。 Okay。 So that's what's going to happen。 Okay。 Anybody see any type of。

We'll find out。 Make separate。 Okay, looks good。 Okay。 So if we run it, right, it says before。

after, after, and then it says, I am the child and the parent will wait up for me。

Child exited with status 110。 And it will always be in that order。

no matter how many times I run it。

Okay。 Because the parent is forced to wait until the child exits at which point that we are all set。

Okay。 All right。 You might be asking yourself, well, how could it exit? Ab normally? Well。

how might we make the child exit in a bad way?

In other words, crash it。 Can I get the job to crash? We can do a bunch of things。

We could divide by zero if we wanted to。 How about this? This is one that I was like。

int star a equals no。 Asterisk a equals five。 That would be bad news, right?

Try to dereference in place of value at no, which is going to be bad, bad news, right?

And if the compiler shouldn't actually care about it。

Make separate。 There we go。 Okay。 Separate。 And before, after, after, I'm the child。

the parent will wait up for a child terminated normally。 Right? We killed the child。

That got the exit status to be to tell you that。

Oh, bad news。 Something happened。 Okay。 Question。 Is it possible for the child to print like the child?

Is it possible for the child to print? I am the child before the child parent prints after。

It is possible。 It's probably not going to happen in this case, but that's perfect。 Yes。

That's possible。 So I guess in this case, it could happen that that's the case。

It's probably not just because that's a good point。 Yeah。 So that is a。

By the way, this is a good term to learn。 That's called a race condition。

And a race condition means two things are going down some paths at the same time。

One of them is going to win, but you don't know which one is going to win。

We try to avoid race conditions in this class。 Sometimes you absolutely have race conditions and therefore you have to do some logic to make sure you know what will happen。

Okay。 When we get to threads, you'll see this a lot with, well, actually。

you'll see it in processes a lot too。 But in particular。

if you're trying to modify shared data structures。

you have race conditions where they're both two things might be trying to do that。

you want to avoid that。 So you, you handle in ways that we'll talk about。 Question。 Yeah。

In the L statement where it calls way PID down here, yes?

[inaudible]。

Isn't that supposed to be a child's PID? It is the child's PID because remember what is the return value for fork?

The parent gets the child's PID as its return value。 So yes, that's exactly。

And that's why that happens so you can use it。 It turns out a good question。

Any other questions on this? Okay。 Let's keep going。 So this function。

the output is same every time barring that one thing where the child could print the。

could print the after and then it's line before the parent even gets to after。

The parent, the whole reason this works in the order we expect is because the parent waits for the child to end。

And then you use these macros to actually get more information about this。

And you might be able to say, wait a minute, how can you overlap the return value and other information?

Right? Wasn't the return value an int? Not really。

Return values are supposed to be between 0 and 255。

So you have lots of extra bits in that int to store other information。

That's the way return values should be。 They are ints but you should limit them between 0 and 255。

That's the way it goes。 Okay。 And then as I said, the wait PID does donate the resources back to the system。

In other words, it's kind of the cleanup that you like to do at the end of the program when something happens。

You should be doing this wait PID for all of your children as it turns out。 Okay。 Better for the。

in the end, no matter what the system will clean up for you but it's just like doing free after you do a malloc。

It's nice to do because it cleans things up。 All right。 What else? Okay。 So。

let's look at another example。 Okay。 In this other example。

we're going to see it's kind of a little bit of a brain teaser。

And we're going to, we're just going to kind of walk through it and say。

and it's going to demonstrate to you how deep this copy actually is。 Okay。

This one is going to be called the what? This one is going to be parent child。c。 Okay。

Here's what we're going to do here。 This is going to involve random numbers。 Okay。

We're going to have some randomness in here。 Okay。 And in this case。

what we're going to do is we're going to first say print F, I am unique。 And just get printed once。

Okay。 And we know why that's the case because there's no forking yet。 Okay。 And then we'll do PIDT。

PID equals fork like we kind of always do。 Then I'm going to just do a make figure out a little Boolean。

Boolean parent equals PID equals, let's see, PID is not equal to zero。 Okay。 What is that all about?

Basically it's saying if it's the parent, in other words, if the PID is not zero, it's the parent。

It's just going to set that。 Otherwise it's not going to be the parent。 Okay。

It's just kind of a fancy way of doing that。 Okay。 All right。 And then we're going to。

we're going to say, okay, if random, which is going to give you a random number。 Okay。

Mod two equals zero and that equals the parent。 In other words, this is C。

so one and zero are true and false basically。 Okay。 If that equals the parent。

then we're going to sleep for one second。 That's what this means。 Okay。

Sleep is another command that basically says turn your process off for that amount of time and then wake it up again after that amount of time。

Okay。 All right。 And then if we are the parent, we are going to wait PID for PID。 No。 Oops。 No。

And zero for basically because we're not doing anything there。 Okay。

Parent waits for child in that case。 Okay。 And then we're going to say print F, I get printed twice。

This one is from percent S, comma, if I'm the parent, I'm going to print out parent。 Otherwise。

I'm going to print out child。 Okay。 And then we're going to return zero。 Okay。

So what this is doing is this is going to set up so that only one of the two actually gets to sleep。

Right。

And if it's the parent, it's going to have to wait for the child。 Okay。 Make parent child。 Okay。

Parent child。 Now, you kind of have to, I'm going to put it up here。

You kind of have to watch this pretty closely because it's only one second。 Right。

But watch what happens。

Boom。 I mean, you can get printed once。 It waited for a little bit of time and then I get printed twice。

Okay。 If I run it again, it printed that I got printed twice immediately。

And then printed the next one。 I'll show you again。 Let's see which one happens here。 Okay。

So that means that the parent was the one that had to sleep。 Okay。 And then maybe they're again。

And the child is the one that had to sleep。 Okay。 Everybody see how that's working。

Only one of them actually allowed to do that。 What seems weird about this?

We've got random numbers in here。 What seems a little bit weird?

Why wouldn't it ever be the case that two of them could sleep?

It's a random number for each one。 Yeah。 Is the seed of the random number you're sharing to change your confidence?

That's a perfect answer。 The seed is actually shared between the two processes because everything is shared。

In other words, random numbers are not really random。 They're pseudo random numbers。

Which means that there's an algorithm which decides which number comes next。 Okay。

And it should be seemingly random。 Okay。 And what's going on here is we're saying, oh, okay。

We are both calling the random function in two different processes。

but it's the exact same random generator。 So the next random number will be the same for both。

meaning only one of them will end up sleeping。 Question?

So that's kind of the x random random number。 Yeah。

So the x random is just seeding the random number with the current time as it turns out。

Don't worry about random numbers too much, but a pseudo random number generator says。

I'm going to start somewhere in my list of random numbers。

and I'm going to start giving you what look like random numbers after that。

And the seed says start here, basically the way it works。 Okay。 So it's not really random。

it's pseudo random。 But it kind of demonstrates that, oh yeah, they both have the same memories。

It turns out。 Yeah。 What happens if you comment out the 19? If you comment out the 19。

nothing would happen to have any different except that it would, well, I get that's not true。

It would probably do the exact same thing every time because the random number generator starts in a particular。

value every time。 And we can see if that's the case, we can try it and see if that, in other words。

no matter when you start a program, it's going to。

so let's see if we run parent child looks like the parent slept, the parent slept, the parent slept。

so it's going to be the same one every time。 We could probably print out the random number and it's going to be the same one every time。

Why is the parent sleeping when it goes slow like that? Good question。 What's the code look like?

Right? The code says, I'm going to take this back out again or put that back in again。 Okay。

It says, if you are the parent, right, or if the random number equals what the parent is。

in other words, if the parent is one, then the random number is one, then sleep。

Right? Otherwise the other one has to be the one sleeping, which is the child。 And remember。

the parent is the one that is not going to even print。

I get printed twice until after the child ends, so if it's sleeping。

it's going to wait until the child prints it and then the parent does。 Make sense?

Let's look at it again。 Yeah, question。 The sleep has to just sleep the parent, right。

because only whatever process it's in。 Okay。 Let's look at it again here。 Parent child。 Okay。

I'm going to, I'm going to print it once。 And then the child got printed before you could see that it waited a little bit。

then the child got printed immediately。 Right。 And now it's going to be, well, in this case。

it's always going to be the child is going to print。

No, it's not necessarily。 It is。 Let's say, did I make it again? Hang on。 Hang on。

Did I make it again? I thought I did。 I did。

Parent child。 I mean, let's try to get there, guys。 Okay。

So the child got printed at that time immediately because it didn't sleep。 Right。

It's always going to be in this order because the parent has to wait until the child prints。

but whether or not the child prints immediately or waits a second is the difference。

Does that help? Yeah。 Let's look at it again。 I mean, just quickly, let's just look at it again。

If you, and this is the logic part that you definitely need to kind of understand。 Okay。

If the parent ends up having to sleep, that's your question in a minute。

the parent does have to sleep, then the parent will sleep and the child will immediately print it。

Does that make sense, everyone? Does that make sense? Yeah。 If the child is the one that sleeps。

both of them have to wait until the child prints it because the parent is definitely waiting for the child to sleep。

The parent has to wait anyway, but only because the child hasn't finished yet。

Not because it's sleeping。 Yeah。 [inaudible], Yeah。 Good question。 If you wanted to use the。

if you wanted to have a random number in both that was different。

you would see the random number general after the fork。 [inaudible], Oh, that's a good question。

Can you actually use time because it would probably be the same time? Yeah。

That's going to be a little bit trouble too。 You'd have to figure out some other way to get a different seed and time would not be the best one。

Yeah。 There are other ways to get seeds, but that's a good, good point。 Very good point。 Yeah。

Question over here。 Yeah。 Sorry。 Would you mind explaining and why only forces only the parent or the child would sleep?

Yeah。 So the question is, why does it only make the parent or the child? Okay。 You will agree that。

hopefully agree at this point that both share the same exact memory。

meaning that the random number generator in both is going to give the same number。 Okay。

One of them based on the PID value that gets back is either the parent wants the child。

This line right here says, if you're the, if the number mod two equals the parent, right?

Then sleep for one, meaning that if it's a parent and the mod number, then the number was one。

then it would sleep。 If it's a child, it would not。

but it can't be both because they're both going to have different Boolean values。

Parent and child are both going to be different。 There was a question up here。 Yeah。

So I have a question。 [ Inaudible ], Correct。 Only one of them is sleeping。 Yes。 [ Inaudible ]。

At least one of them is sleeping。 Yes。 Absolutely。

So one of them is absolutely sleeping because they're both different Boolean values。 Good。 Yes。

[ Inaudible ], What's the mod two doing? The random function call gives you a number between zero and two to the 32 minus one。

We're just trying to make it between zero and one。 So now our mod two is between zero and one。 Yes。

[ Inaudible ], If you sleep for longer, could the child wait? Yes。

The child actually almost always does when you're, when you're sleeping。 Yeah。 Let's do this。 Here。

one thing we can check is let's see if parent, let's see if parent, here's what we're going to do。

We're going to change this a little bit and we're going to say, in PID return equals wait PID。

And we're going to print it out and see what happens。 Print F, PID return。 It may actually。

you know, it may actually always give the value of the child。 I think it actually will。

It may give negative on。 We'll find out if it's already ended。 But I think it's going to。

I think this is not going to be what I thought it was going to be。 But anyway, PID return like that。

Yeah。 That should do it。 Let's see。 Make parent child parent child。 Okay。 Okay。 Return is that one。

And then now it's waiting。 No, it's always, it will always。

it looks like it'll always give back to parent that child's one。 Even if it's already ended。

In fact, it would return immediately if the child has already ended。

Alright。 Anything else on that one? One stuff, huh? I'm getting there in this stuff。 Okay。

So what happens when we want to have multiple children? Okay。 If we have multiple children。

then we can actually wait for all of them。 Okay。 And we can wait for all of them by using negative one as the PID as the first parameter in PID。

Wait PID, which means wait for any child。 Okay。 And if a child has ended。

return to me and give me the PID and so forth。 Okay。 Any, for its, its, its, absolute to any child。

So you're going to see we're going to do this in a while loop and it's going to say, okay。

we're going to wait for all the children。 You'll see as we, as we do this。

So reap as they exit。c。 Okay。 Here's what we're going to do。 Okay。 We are going to do the fun。

We're going to create eight children for size t i equals zero。 i is less than eight。 i plus plus。

Okay。 If for。 Okay。 Okay。 Equals equals zero。 What's that mean? The child。 Okay。

We're going to exit with a value of one, one ten plus i。

So we'll get a different return value for each one of those。 One ten, one eleven, one twelve。

et cetera。 Okay。 And then that should do it。 So we'll always exit there。

And we're going to do eight of it。 Okay。 Question。

What's your reason why you're using an exit instead of just going return one ten plus five?

You could do return one ten plus i。 We normally exit out of the children instead of return。

It doesn't really matter as it turns out。 But yeah, this, that's good question。 We normally do that。

Okay。 So we're going to do that。 And then now we're going to say, okay, fine。

We are going to do a while。 We're going to do a while true in this case。

I'm going to demonstrate something else from you。 We could search for it。

We could wait for all of them because we know there's going to be eight of them in this case。

But we're going to do this status。 P。I。D。T。 P。I。D。 equals weight。 P。I。D。 negative one。

And we're going to pass in the status like we did there。 And then zero。 Okay。

And then we're going to look。 And if we find out that if P。I。D。 equals negative one。 Well。

that means there were no more children to wait for so we can stop our while loop in this case。 Okay。

And what we actually want to do in this case, you don't always have to do this。

But it's a little bit of error checking。 We're going to assert in this case that the error number equals each child。

I'll tell you what that is in a second。 And then we're going to break。 Okay。

What we're doing here is we're saying, well, the return value is negative one。 Technically。

if the return value is negative one, yes, it meant that there were no more children。

But it also could mean, hey, there's an actual error here in your wait P。I。D。 call。

It's kind of overloaded。 So it's got a wait P。I。 It's got an error。 It definitely could be an error。

But what it does is it sets error no to each child。

which means the reason I gave you an error is because there were no more children left。

And if you check that, then that's actually okay。 In fact, works perfectly for our purposes。 Okay。

Yes, it's an error。 But oh, it's the child issue。 Fine。 Let's just go ahead and go on。 Okay。

So that's what we're doing with that one。 Okay。 And then we should just check our W if exited status here。

Okay。 And then let's print a child percent D。

exited status percent D。 Now let's do the P。I。D。 and the exit status。

So you can get the exit status from the status itself, which will be the return value for the child。

Okay。 And then it looks like I have something wrong here。 Hold on。 Let's see。

Print that child extra bad。 Child extra status that P。I。D。W。X。 status。

That's the one。 There we go。 Okay。 How's that? That was bright。

But I don't think that's the actual issue there。 Oh, there's a bracket。 Yeah。 Okay。

And then else and then print a child child percent D, exited ab normally。 And we'll go just P。I。D。

because no, there's nothing else there。 And that should do it。 And then our return zero。 Okay。

So let's see what happens when we do this。 Oops。 That I don't want to do。 Okay。

Anybody ever done that with the encrypt their file? It's bad。 I mean, try it。 Okay。 Make。

reap as they exit。 Okay。

Okay。 Creep as they exit。 And what should happen is it does 10, 11, 12, 13, 14, 15, 16, 17。

But let's do it again。 Let's see。 10, 12。

There we go。 Okay。 Finally, I got any wrong order。 10, 11, 12, 16, 13, 17, 14, 15。

This is not in enough particular order because we are saying wait for any child to exit any one of those children could exit before any other one。

Although it's likely that the one that came first exits first because it's just not in the case of the child。

Okay。 So we can get some non-determinist behavior here。 Okay。 Everyone see what's happening there。

Negative one for wait P。I。D。 does that for you。 Okay。 All right。 Let us say wait。

What if we wanted to actually wait for them in order? Like, could we wait for them in order? Okay。

We certainly, this is what it does do, by the way。

This is what does that。 And I said, talked about error number and all that。

What if we did want to wait for them in order? Let's take a look at another program。

This one's going to be reaped in fork order because we want to actually do it in the order they are in。

Okay。 So let's do same sort of thing。 Except now, if we want to wait for them in order。

we better know what the order is。

Okay。 So we probably want to do something like P。I。D。T。 children array of eight。 Right。

Because now we need to keep track of all the P。I。D。s if we're going to wait for them in order。

We're going to have a same size T。 I equals zero。 I is less than eight。 I plus plus。 And then。

and then we are going to do status in here。 P。I。D。T。 Sorry。 I'm going to put a formula in here。

In this case, we're just going to create them all here。 If children of I equals fork。

We're going to do a little bit of a formula。 We're going to do a little bit of a formula。

We're going to do a little bit of a formula。 Okay。 All right。

And then we're going to do now we're going to go through them all for another size T。I。 equals zero。

Not a while true loop is this case。 Although you could probably make it work if you really wanted to。

Okay。 And in this case, we are going to now do P。I。D。T。 P。I。D。 equals wait P。I。D。

for a particular child。 I status zero。 Okay。 Let's assert that we got the right child。

even though we almost always would。

P。I。D。 equals equals children I。 Okay。 And then, assert really could we're not waiting for anyone。

We're waiting for specific one right now。 And let's do。

We're going to just kind of check a couple things at once here。 W。X。

its status status equals 110 plus this is like lots of extra checking just in case here。 Okay。

And then we're going to print child with P。I。D。 percent D is accounted for。

And return status of percent D。 Period。 New line。 Children。 I。

And we'll give the actual status of it。

Which is the return value。 Okay。 And that's it。 Say again。

I do not need to initialize that。 Oh, you do declare it。 I do indeed need to clear it。

I don't initialize though。 Thank you。 Thank you。 Okay。 Anybody else issues? Let's try it。

Make reap in four quarter。 Okay。 Looks good。

Now, in this case, it has to wait for each one。 So we are going to always get them in order。

Because it's waiting for 110, 911, then 112, etc。 And all of them might be ending in different orders。

It's just going to eventually reap the process。 Another way P。I。D。

is going to where specifically going to ask for a specific one。 And it's going to return and say。

yes, you got it。 Okay。 What questions you have on that? Yeah。 That's a very good question。

And you have to be careful。 The question was, doesn't it populate the children array with the P。I。D。

Only for the parents。

It gives it the child's P。I。D。 Remember, what is the child's P。I。D。

or what's the result of fork for the child? Zero。 It is not the child's P。I。D。

It's only zero if you are the child。 So in this case, I kind of briefly mentioned this。

In this case, yeah, it's putting a zero in there for the child's version of that array。

But we're going to ignore it。 We don't care except to say, is it zero or not?

We've got to follow up。 Yeah。 In the forward, then it will wait for children I to follow。 Ah。

good question。 When you wait for children I down here, isn't always going to be zero。

There are two arrays every time you fork now。 There's a parent array。 You fork。

The child has its own version of the array。 The parent still has its same version as before。

It gets the parent gets the child P。I。D。 The child gets zero, but it's initially immediately exits。

Oh, it worked。 Yeah。 This is why I want to be really careful about that return value for fork is not a PID except for the parent。

It's only the child P。I。D。 for the parent。 It is always zero for the child。

If the child wants its P。I。D。 for some reason, it has to call get P。I。D。 Question? No。

The children never do the second for loop。 Exactly。 Good question。 Yes。 So, in this case。

most of the time we call get P。I。D。 for the parent。 It's already exited。

Some of the children have already exited。 Yes。 In fact。

most of them after the first couple loops have probably already exited。

You can wait for a child after it's exited and it still returns without it。

We tested that a little earlier。 Good question。 Yeah。

Could a child create a like I equals seven look back on the P。I。D。

values of the children for like I equals five or three? No。 Yes。 It could。 Sure。 Yeah。

This is a good question。 Could the 6th or 7th child see all the values of the P。I。D。

s for the previous children? Sure。 Before that, it's still the parents。 It's the same。 Yeah。

The parent keeps it and keeps populating。 So sure。

Now you're not trying to hide that from the children。 It wouldn't be in that case。 Good question。

Yeah。 Because I'm confused。 Why can you wait for a child after it's exited when she wants to like reuse that process or something else?

Why can you wait for a child after it's exited when she wants to reuse that child for a process or something else?

It hasn't been cleaned up yet until wait P。I。D。 is called。 So you're still waiting for。 In fact。

there is some extra overhead because it hasn't been cleaned up yet。 That's really the difference。

You've once it ends, it's waiting for somebody to do a wait P。I。D。 on it basically。

That's the clean up process。 Yeah。 Why doesn't the first four look like creative that tree of like that?

Yeah。 Why doesn't the first four look creative tree? You tell me。

What's happening in this first one right here? What happens at each child?

What does each child immediately do? It dies。 So it never gets to the next loop。 Because it exits。

Yeah。 All right。 Good。 This is like, you can see why this stuff is like challenging to get the first time。

The first couple of times you see it。 Okay。 This is what it is ever in theory。

You could have any of the children in any order。 But the loop is still going to be held up waiting for that first child to end。

then the second, then the third, then the fourth。 That's just the way these ones work。 Okay。

All right。 So that's the, that's fork。

Now we have showed some interesting examples of fork and how you've got your program going in two different directions。

and you've got your code in the child and your code in the parent and so forth。

It turns out that most times you use fork, it is not going to be to do your code in both the parent and the child。

It turns out that what normally happens is we are going to make another program run。

We're going to set some other program off to run such that it does its thing and we may or may not capture the output。

But we actually are running some other program。 That was kind of the original reason。

Fork a process, have the other process the child run some other program。

That's mainly what it's for。 You can do what we've been doing where we show these kind of interesting tree examples and interesting child。

But most of the time this is what we're going to be doing。 Okay。

We're going to run a completely separate program and we may or may not capture its standard out。

We may or may not send it information in standard in。 Okay。 This is what your shell does。 Okay。

When you type LS on your shell, right, well what happens is the shell is a program and when you type LS it takes that and says。

"Oh, okay。 There's a program called LS on the system。" And it runs that program。

waits for it to end and gives you a prompt back。 That's what's happening with the shell。

So the shell is doing exactly this。 It's doing what we call forking and then execvp which is another system call。

And this system call does the following。 It basically reboots the process with some other program。

Okay。 And this is one way to do it and in fact kind of the default way to do it。

And that paper I showed you earlier is probably complaining about this。 It's complaining about like。

"Why have we do it this way? There's other better ways of doing this and so forth。

" But this is what it does。 Execvp, it's basically a family of commands all called exec as in execute。

I guess。 And the vp happens to be a certain variety that we're going to use。

You can go look up all the other ones。 If you type man execvp, man execvp。

you can see that it actually shows a whole bunch of them。

execl, execlp and so forth。 The one we use and it's just so you can have different environment variables。

Not really that important。 The one we're going to use and focus on is execvp which basically takes a path。

and then takes an argument array just like argv and it runs the program at that path with the argument string or array passed into it。

Okay。 As it turns out just like all your other regular programs。

the path and argv zero are going to be the same。 Because the first argument in argv is always the name of the program。

So therefore we're going to have the name of the program as the path which is also the same as argv zero。

Okay。 So that's just the way that works。 And all it does is it runs it and it basically takes over that process。

runs that program in it and that's that。 Now what's interesting about this is if it doesn't work。

let's say you gave it a bad path and the path didn't work, it would not actually run anything。

It would return to you and give you an error message or an error return value of negative one and you can capture that and say oh there was an error。

Okay。 If it does succeed, like in other words if it runs that program it never returns to the calling function。

It just doesn't ever return。 Why? Because it's a whole new process。

Well it's actually cannibalized to the process, to the extent that it is now the same process that you just created with fork except it's it's all own。

got it's own, everything else。 Okay。 So that's what's happening with execv and Jerry put this in here。

That hashtag deep。 Right。 But it's like never returns if it succeeds。 That's the way it goes。 Okay。

So that's what execv。 If he does this is mainly the big reason we're going to use for is to do that and to call them。

Okay。 What example are we going to do? I've got seven minutes and I guess I can do the whole。

Yeah it's actually it's actually not that bad。 We're going to do a little system example。 Okay。

We're basically going to do a tiny little shell。 If you guys are in CS107E you know all about shells and you're going to build one for an assignment in this class as well。

But for right now we are going to do a tiny little one called my system。 Okay。

And what it's going to do is it's going to cheat a little bit。

It's going to use the shell a shell program to run a program we give it。 Why?

Because I don't want to have to parse out the different parts of the command line。

You'll see what happens when we do this。 And it's a good first example anyway。 Okay。

We're going to actually do the my system function itself。

It's going to be passed in a command that we want to run。 Okay。 And here's how it's going to work。

Okay。 PID equals fork。 So the first thing you do when you do this exact series you need another process。

You don't have to have another process but if you don't have one you never get back to your original program。

It gets cannibalized。 Okay。 If PID equals zero that means we are the child。

Then what we're going to do in this case is we're going to say all right our arguments equals and what we're going to do is we're going to say run this program called bin slash SH and that's the program that's going to get run。

Then slash SH if you want to run another program from within that you have to do a command parameter called dash C。

And then here's the command itself which you have to make it constant or you have to make it non constant as it turns out。

It'll just be a warning。 It doesn't really matter。 But it's passed in as a constant in that case。

Command and no。 Okay。 You have to end your it's this one。 Thank you。 Sorry for the program。

That's just creating an array by the way is really all that's doing。 Okay。

And you have to end the last argument with no。 It's just the way that we do these kind of arrays because they're all strings we can do that as it turns out。

Okay。 All right。 So that's what we're going to do。

And then at this point then we can say oh great exact CVP the arguments zero which is the。

bin slash S H and then arguments。 And then that will actually do the process right if we can say print if we get to this point。

It means we failed because why because it never returns if it doesn't get to this。

Doesn't if it works。 Okay。 Failed to invoke failed to invoke slash bin slash S H to execute the supply command。

Sad face。 Okay。 And I guess we should probably do。 That doesn't really matter。

We'll get a new line later。 Okay。 And then exit zero just to say well we got it。

We have our fork where we're done。 Okay。 And that will do that if we are the parents。

We're going to do in status。 All right。 Well, we're going to wait for it。 All right。

Wait PID。 P ID status and zero。 And oops。 Okay。 And then return。 W if exit status。 Okay。

If that's true。 We're going to say the exit status。 Otherwise we're going to return。

We're going to turn the negative of the termination status which just basically says well what happened。

Don't worry too much about that。 For that。 I think it's term。 Yeah, that should be it。

And then that's that。 So that's our function。 Okay。

Let's write a little kind of a driver for it here。 Okay。

And the driver is going to be just the main part that's going to call it。

So let's actually set it up。

Okay。 We'll make it like a little terminal。 We'll just keep going in some loop until you do it。

Let's see。 We want to do。 Let's do create a char command。 Let's make it K max。

Let's just say you can't type more than 2048 characters in your command。 Okay。 And then while true。

It's going to keep doing this forever。

While true we're going to print F a little prompt like that。

And then use F get S which basically is a nice way of getting something from a file descriptor。

Right。 And in this case we're going to or not it's a kind of a different kind of file descriptor。

It's a similar sort of thing。 K max lines in case you type too much。

And then we're going to get it from standard in。

Which is the way you can do this with this。 And if F E O F standard in which basically means if we have the if we've ended if we do control D this will end it。

We're going to break out of our loop。

And otherwise we're going to say come in。 And we're going to overwrite the last part of the command with a zero instead of a new line it gets you get the new line back。

Like that。 Okay。 We're going to overwrite the end there and we'll let you run there。

Probably forgot to print the thing here。 There we go。 Okay。

And then finally we're going to print out print F return code and percent D and my system。

And like that。 Okay。 And then we're going to print a new line。 And we're going to print our turn。

Okay。 That is going to hopefully if I did everything right make my system。

Oh no。 Let's see。 Expect a cost chart。 Let's see。 Hang on。

Passing argument makes a pointer from integer。 What did I do here。

Chart star arguments is that I don't want to keep you guys much longer。

I just want to run it for you。 Hang on。 Let's see。 Chart star。

Oh arguments think it needs to be a right, doesn't it。

Then my system needs to be an array。 Thank you。

That should do it maybe。 There we go。 Okay。 Now my system will give you a prompt。

So there's your little prompt。 And if you type LS it runs LS。 Right。 And there's our prompt。

And if we say we could actually run Python or something。 Right。 And whatever you want。 Right。

If you want to run it does the return code。 If you type some bogus something it'll say hey permission denied or something else it'll say return code 127 which is incorrect。

Okay。 That's the first example of exact CEP。 We will start with that on Monday。 See you then。

P6:Lecture 6 execvp, pipe, dup2, signals - main - BV1ED4y1R7RJ

Welcome, welcome back to Monday CS 110。 We are continuing with multi-processing, which is。

like I said, the first time you're probably seeing things happening in parallel of a program you've written。

So we're going to take the time we need to discuss that。 We're also going to talk about today。

we're going to talk about these things called pipes。

which are basically a way for two processes to send data back and forth using read and write。

So we'll get to that。 And then we'll talk a little bit more, depending on how much time we have。

about interprocess communication, which basically means that we'll talk about how two processes actually talk to each other。

And how you end up getting a message, a signal, which is another name for it。

how you get a signal when a child actually finishes。 So we'll get to that by the end of the class。

I think。 How's the assignment going? Biosystem going all right。 I've seen a couple of thumbs up。

It's definitely, you know, it's got a lot of little moving parts to it。

Somebody was just asking me in the hallway about, like, how do you get a signal? Like。

how do you know when you're reading data, what kind of data it holds?

You kind of don't unless you're in a function where you're specifically reading data from the disk。

where you already have information about, oh, I'm reading a file, or I'm reading a directory。

or I'm reading an I know block or something like that。 And you know that as you。

when you call these functions that read from the disk。

because the disk itself could care less what's on it。

it just matters to you as the actual operating system。

and the file system has been put in place with certain, certain things associated with each block。

So that's how you know that。 Anyway, hope it goes well。

Any questions on the assignment at this point? Piax has been going along。

Comments or questions about it? Yeah。 [ Inaudible ], Good question。 So the question was, hey。

you said that you're able, that you are allowed to change some of the functions。

You mean the ones we've already written for you? Things like that? No。

You should not change the return values for functions if you can help it。 I mean。

especially the ones, I mean, in general leave them the same。 If you need to, for some reason。

you should have a really good reason for it。 But no, you should。

the spec we've laid out shouldn't need to be changed as far as the return values。

If you have a specific question, come on up after and let me know what you're talking about and we'll chat about whether or not。

But most of the time, yeah。 Everybody else? Okay。 Office hours are this evening and rest of the week。

I have my office hours tomorrow morning。 There haven't been many people showing up。

So I may just add some office hours, kind of maybe in the afternoons。

I'm thinking maybe Wednesday after class。 Would more people be able to go to that? If I had。

I don't see, I see a couple people。 Okay。 I'll see about doing that in my own schedule。

I've got to look at it。 But we'll try to put some more office hours in there or Thursday around the same time。

Might also work。 So we'll do that。 All right。 So last week。

we actually finished with the example I'm going to go back over again。 Remember。

we've been talking about this interesting system call called E-X-E-C-V-P。

which is part of the E-X-E-C or exec functions system calls。 And what that does is it says。

I want to run another program and basically replace my current system。

And replace my current process with that program。 Now, if you want to do that。

most of the time you're not going to be done with your program。 I mean。

there might be some cases where that's going to be true。

But most of the time you want your program to kind of spawn off this other process that some other program and you use fork to do that。

So you fork, you get a child process and you save it a child process。

you will run this program and while doing that, the child process will become that program and that's it。

Now, there is some communications between that child program and the one you're the parent and we've got to talk about how that actually happens。

So we wrote a little, just a very basic program called MySystem。

We're going to expand on this a little bit more in a few minutes and then you're going to expand on it for assignment four actually。

You're going to make a much more robust MySystem kind of command。

which is basically a little shell is what we're talking about。

So here's what the program itself did。 So let's look through this a little bit。 Basically。

the first thing the MySystem function, this is the MySystem function does。

it takes in a command and that could be something like LS or cat file。txt。

It just takes a command in and it's going to run that command using the shell itself。

the actual shell。 We do that so that it's a little easier to parse it。

When you get to yours for assignment four, you will figure out how to do it without using the shell as kind of training wheels。

You'll see it, we'll see what that means in a little bit。

And then it takes that command and then it does this fork。 Does this fork and MyPen has died?

So it actually forks because we are about to call exec CVP。 So we don't want to make it the only。

we don't want the function to be done for our program。 Oh no, it's done。 Didn't work at all。

Hang on。 Maybe we won't use these again。 Hang on。 Let's see。 Nope。 That's not working。

Try this one more time and then we will not use this。 Not working。 Tabla。 Anyway。

what it's doing is, I'll even blow this up here。 So basically。

it's going to fork and then if it's the child, it's going to spawn this other process which is the program you want to run。

And it's going to do that by basically saying, okay。

actually run this program called /bin/sh which is the shell program。

Run it with the -c option which says, hey, run the following program。

So it's kind of like two levels of indirection here。

And then it's going to take your command and it's going to run that command that you have typed in。

And then this is an array that has to be null terminated。 So that's what's happening here。

It's basically creating an arguments array that is an array of string pointers。

charge star pointers。

And then you call exec CVP and it calls the - the first argument to exec CVP is the name of the program that is directly being run。

In this case, it's /bin/sh which also happens to be the first argument in the argument list just like it is in main。

And then the second one is the entire arguments list, second parameter。 If that fails。

then it returns to this function。 If it doesn't fail。

the entire rest of that child process is destroyed。

It's cannibalized so that this other program can run。 Remember, your parent is still running。

But the child process you just created is now destroyed by running the other program。

Okay, so that's what's going on there。 Now, I actually am trying something new。

One of our TAs actually created this little system here so you can actually in the slides run this。

although I think the font is possibly a little too small。 But you can actually run this in here。

And here's what it looks like when it runs。 There we go。

And it puts up a little cursor here like that。 I'll just do make this a little cursor like that。

And you can type ls。 And that's what the program is doing。 It's actually running ls and doing that。

And then let's see, catcode。c should also print out the code。

And so we've written kind of a little shell and it always comes back to the prompt for us at the end。

Okay。 The standard in for the child process is just regular old standard in。

We'll see how that might change in a little bit。 Okay。 And then to end this, by the way。

for us to end this function, we actually do control D。

which says we're done entering things from standard in。

It basically closes standard in for the process that's being run。 Okay。

So let's look at the main and how main actually works here。 Main basically says, oh, okay。

We're going to do a while loop because we have to keep putting a little prompt。

which is just this little arrow here。 And it gets a line using fgets from standard in。

And that's what you type ls or cat whatever。 And then if you type control D it would end。

That's what's going on there。 And then it just populates the new line。

changes the new line into a zero so that we've actually got just a regular old string here without the。

Without the new line in it。 And then it actually calls my system with the command gets the return value back from my system。

which is the return value from whatever the my system command does。 And then it continues to look。

Okay。 Do you see what's going on there?

All right。 So that's what we kind of covered the other day。 Let's move on。

And this is the test on it。 So I just showed you that。

Let's move on and we'll talk about a little bit more detail of the similar type of program in a few minutes。

I want to introduce a couple of other topics。 Okay。 We're going to introduce the notion of a pipe。

which I mentioned earlier。 And then doop two。 Now in lab you should have covered doop to some extent。

We'll go over the details of what that's what do to in particular is doing here。 Okay。

So now let's have a little more complicated shell。 All right。 In the actual, let's see。

I just want to make sure that we're getting to the, because there's no piping yet。

Let's actually go ahead and do this the more advanced shell here。

Okay。 This is going to be simple shell dot C and。 Oh, no, hang on。 It is。 Yeah, it is simple shell。

Hang on。 I think I already had it simple shell。 Sorry。 Oh, all right。 We'll just look at it。

I won't type it all right now。 Got plenty of things to type a little bit later。

Here's what we're going to。 Look at it。 Actually, I can do it in the thing over here。

Now I think about it。 Here's what the code is going to do in this case。 Okay。

We are going to create a shell that actually allows you to do a background command。

Now what is a background command? In the regular old shell。 Okay。 You can do the following。

You can actually type a command that runs in the background。

which means that you get your prompt back immediately and now it's all the other program is still running。

Why might you want to do that? Well, there might be various reasons why that you might want to have some other program running。

and you still want to use your shell。 Okay。 It's a little weird if you do something like LS dash AL。

but then you put it in the background。 And that's how you put it in the background, by the way。

You put an ampersand and what it does is it actually immediately says, let me go back up here。

Here we go。 It immediately says, "Hey, this is a process I just created。

" And then it gives you a prompt back, and the rest of it continues going。

I think I showed you an example of this the other day, that actually kind of, what was it?

It was like, "Wow。 Actually, I wouldn't think it was quite this one。", So, "Wow one。 Echo。

Are you annoyed yet?" Something like that。 "Semicolon sleep。 One。 Done。" Oops。 Let's see。 "Wow one。

Do。 Do。 Echo。 There we go。" Okay。 So that's that。 We can just run this and it's just going to run every second。

If I put that in the background, what it will do is it will, every second, just tell me that。

which I can do other things。 And every second it just says, "You know, you're new and then whatever。

", And I'm still able to run programs。 And this is kind of the same sort of thing I showed you today。

And it just basically does, it's running in the background。

So I've now done that by doing the ampersand on the end of the program, that says, "Hey。

run this program and then let me do other stuff。" And you might think of various reasons why you want to do that。

And this would be a bad thing to do unless you want to be annoyed lots。

And then actually you have to actually type F G and then control C and it will actually stop the program。

F G says, "Put it in the foreground。" Okay。 You can actually do this a different way too。

If I just run that same command without the ampersand and let it go。

if I want to put this one in the background, I just type control Z, not control C, control Z。

and it will put it in the background and stop it。 Which might be what I want。 Okay。

And then if we want to actually continue it, let's see。 Let's see。 If we want to continue it。

I think, let's see。 I think if we, nope, hang on。 Job。 Nope。 Yes。 Oh。

it might have killed it accidentally。 Hang on。 Might have killed it。 Let's see。 If I do control Z。

it stops it and then I can put it back in foreground。 Oh。

I think it only stops the final thing that my while loop isn't going to necessarily work。

I could do it this way though。 I could say like an annoying got shell。 Let's see。

We're going to check this。 There we go。 And then, an annoying got shell。 All right。 There we go。

Now。 An annoying got shell。 Annoying。 Okay。 There we go。 Now that's that。

And then hopefully this will keep, if I keep doing, there we go。 It'll keep, there we go。

Now it's in the background。 So what you basically do is you run a program and then if you do control Z。

it stops it, pauses it in the background。 And then if you type BG。

it will continue it running in the background。 Okay。 And then that's it。

And now I can still type LS, but every second it's going to say。

are you annoyed yet, et cetera。 And then if I wanted to go in the foreground, I can do。

I can take FG。

It will go in the foreground and then I can control C and get rid of the message final。 All right。

So what we are going to look at here is this program that basically will use the idea of a。

background to enable us to run background processes。

So it's a little bit more advanced than what we had before。 Okay。

So let's look at the actual code here。 Okay。 Another while loop because we want our little shell to。

actually keep going and giving us back a prompt。 Okay。 We are going to read a command。

which is another, function, which basically reads the command in from the line there。 Okay。

We are going to create an, arguments list, which is going to have the command in it that's going to parse the command。

If you, want to look at all the details of these functions, you can see them。

But basically we're going to type in a, command, have it parsed。

And that part is not really that important to what we're trying to do here。 Okay。

And then we are going to look for something called a built-in, which is this quit thing。

Now how before we had, a type control D and it would end。

Now we just type quit and it actually ends。 So this is actually just a little kind of。

an extra part here where we say, oh look, if the arguments that we typed in was quit。

then stop this while loop。 That's nice。 Okay。 And then if the last thing in the argument was the ampersand。

we want to put this in the, background。 In other words。

we want to go back to our prompt and let it keep running。 Okay。 Well, how are we going to do that?

We'll see。 Okay。 We are basically going to do the following。

We're going to see if we are in the background。 Okay。

Then we're going to get rid of the little ampersand so we can run the command correctly。

Then we're going to fork。 Okay。 And if we are the, child。

remember if the return value from fork is zero, we are the child。

we are going to call exec CDP on the arguments with the, argument zero in the arguments。

And that of course is going to not return because it's the child。

We should do error checking on this。 Like if I type a command that doesn't work。

it shouldn't do this。 If it's the background, we are just going to print the actual child。

process PID and then the command we did and then we're going to continue going back to another prompt。

Okay。 And then we are going, if not, if it's not in the, but we have to do a weight PID。

And the weight PID does what? It tells the parent, don't do anything, until the child process ends。

Okay, so that's the one here。

And again, you can go look up some of the details there。 I think I actually have another, yeah。

I can run it here, as well。

Okay? Run it here。 All right, this is simple, the prompt happens to be called。

Simple SH。 Okay, you will be writing one called STSH, for Stanford Shell。 Okay, if I type LS。

there we go。 If I type, let's see, if I type L, oh, you know what?

I'm going to run this any other one, so I can show you the one that we just did。 Simple, maybe not。

make Simple SH。

There we go, Simple SH。 Okay, so if I type LS, then it will give me that。 I can type catmysystem。c。

and it will give me that。

If I type the catmysystem。c with the ampersand on it, then it actually runs in the background。

and my prompt is going to be up here somewhere。 It's going to be way up here, there。

It actually printed out the process number, and then the name of the file, the function。

or the program that's in the background, and then it prints the prompt right there。 Okay。

so we should be able to run our annoying program as well。 And, oh no, I forgot, I forgot to。

We should be able to write a "knowing" I've put in the background。 Annoying。sh in the background。

there we go。

And then now we're on our Simple SH。

Like that。 Now there happens to be no way to kill this。 Well, that's not true。

I could probably type Jaa。 I probably could type PS, and it will tell me that。

and if I do a command called kill, it may work, although I don't see annoying there, but yeah。

so maybe not。 Anyway, how are we going to do it? We're going to type Control C。 Or quick。

And that's that。

But anyway, the point is that now we've actually, got a little bit more functionality to our Simple。

SH。 Which is to say that we can now run a command in the background。

or what we call in the foreground, which actually, pauses the parent。 Yeah。 [INAUDIBLE], Yeah。

good question。 So the question was, hey, why are we。

waiting on the PID in the parent? OK, remember what the parent is doing。

The parent is giving you back a prompt, and waiting for you to type something。

If I run a program normally without being in the background, in what we call the foreground, we。

don't want the parent to put the prompt until it's, waiting for the child to end。

Does that make sense? So that's what's happening here。 It's waiting for the child to end when。

it's in the foreground, because the child, processes taking over the input output。

If it's in the background, we don't want to wait PID, because we want to go, oh, OK。

it's off running on its own。 Let's give you back a prompt so you could do more other things。

Sounds good。 Yeah。 [INAUDIBLE], Does the child not finish until after the argument--。

or are they commanding pass a DEX, DEX, DEX, and so on? That's a good question。 The question was。

wait, does the child not, finish until the exact CBP ends? Yes。

The exact CBP is the child at that point。 Remember。

the entire process gets taken over by that program, and it is the child process。

So even though you run that other program, that's a chapter。 When that program ends。

then your wait PID will stop。 Good question。 Yeah。 Will the child ever get to the line that you're。

putting on right now? The child will never get here if the command was a legitimate, command。 Why?

Because exact CBP never returns if it, cannibalizes the process and works correctly。 Good question。

Any other questions on that? OK。 Let us-- and you can go-- like I said, you can either run it。

in here, or you can run it certainly on your own computer。

using the MIF machines。 All right。 Let's talk about another system call called pipe。

The pipe system call enables you to have two file descriptors, one of which you read from。

the other you write to, and whenever you write something to that other one。

you can read that data from the first one。 Basically sets up two file descriptors。

that communicate with each other。 Well, let's see how that actually works。

It takes in a little array, in fact, a very small array, of two integers of FDS 0 and 1。

So it takes a two integer array。 And those integers, when you call pipe。

it populates that array with two file descriptors。

One of the file descriptors you're going to read from, the other one you're going to write to。

And what it does is it actually sets it up, so that parents and children can talk to each other。

Because remember, when you do fork, everything is copied。

And the pointers into the open file table are also copied。

So you get the same data so that you can write to one, and read from the other。 And they're both in。

both the parent and the child。 We're going to see an example of this in a few minutes。

and you can do that。 And you can see why it actually becomes important。 But that's what pipe does。

You call pipe, you pass in this tiny array。 You do not have to initialize the array。

It doesn't matter。 You just pass it in。 And it populates that array with two file descriptors, one。

of which writes, and the other one reads from the one, that you just read。

That's how it works。 All right, let's look at a program。 In fact。

I'll write this one out so we can do it one line, at a time。

This one is going to be called pipeexperiment。c。 So in here, what we're going to do。

is we are going to do this in FDS2。 That's our file descriptor that the pipe is going to populate。

Then we are going to say pipe FDS。 We're going to ignore the return value for now。

Don't worry about the return value。 It actually is not-- I think it may give us negative one。

if it doesn't work。 Just ignore it for now。 And then we are going to do a fork, the IDT。

PID equals fork。 All right, if PID equals 0, which means we're a child, what are we going to do?

We are going to-- well, here's what this program is going to do。 I just want to tell you that first。

The program is going to create a pipe, and the parent is going to write some data。

that the child will read。 So the parent is going to write, the child is going to read。 All right。

by the way, this file descriptor-- everybody, always gets confused。 Which one's the read。

which one's the write? The FDS0 is the reader。 And I always remember that because you。

learned to read before you learned to write。 So it's the first one is the reader。

And so the second one, FDS1 is the writer。 That's the way I remember it。

And what you can do is you can say, OK, because the child will read from the pipe, what we can do。

is we can immediately close the FDS1。 No need to write。 In other words。

we're going to close the pipe。 When you create a pipe, it creates two file descriptors。

And when you fork, now you've got a total of four file descriptors, because everything's copied。

And by the way, it updates the reference counts too。 So now you've got a situation where。

you've got four file descriptors floating around out there。 You only really want to use two of them。

The reader will be in the child, and the writer, will be in the parent。 You could use both。

But if both the parent and the child, tried to write to the writer, they might get garbled up。

because there's no real easy way of determining who's going, to be writing at one particular time。

You could probably figure that out, but that's not what we're trying to do here。

We're simply trying to get the child to read from this pipe, when the parent writes to it。

So we're going to close the FDS1 because the child does not, need to write。 OK。

we're going to do a buffer that happens to have six characters。

We're going to only pass six characters in here。 We're going to, in the child, read from FDS0。

And we're going to read into the buffer。 And we're going to read six characters, which。

is basically the size of the buffer。 And then we're going to print red from pipe。

bridging the processes。 OK, percent S, whatever we read。 And we're going to write the buffer。

So that's that。 Now we are done reading in our child。

So we need to close FDS0 because we just finished it。 I'll just put Don reading。

And then we're going to return 0, or we could exit as well。 So that's what the child is doing。

The child is-- got these two pipe parts。 It's going to close one of them because it's not going。

to bother writing to it。 And then it's going to read from the other one。 And then when it's done。

it's going to close that。 You should always close your file descriptors, when you're done with them。

except for standard in, standard out and standard error。 You don't need to close those。 OK。

all right, so that's what the child is going to do。 The parent will write to FDS1。

So what's the one that we're going to actually close? FDS0。 No reading necessary。

That's what we're going to do there。 And we know we're in the parent because it's。

though not the PID of 0, but we got that。 OK, what we are going to do here is。

we're going to write FDS1 because that's the writer, part of the pipe。

And we're going to write just hello。 OK, and we're going to write six bytes。

Why is it six bytes and not five? There's a little null on the end there。

Got to write that in there。 OK, and we are going to then just wait for the child。 PID, PID, no。

we could do this with the return values。 Now that we're not going to worry about it。

And then we're done with writing。 Done writing。 So we close that pipe and then we return 0。 Oops。

got an extra one in there。 All right, questions on that? Yeah。

Do you have any guarantee that the parent is going to write, before the child is--。

You do not have any guarantee。 That's a good question。 The good question is do you ever guarantee。

the parent will write before the child reads? No。 But remember read blocks until it gets the number of bytes。

that you request。 So unless the parent closes, that will wait until all six, bytes get in there。

Generally, you actually--, yeah, well, it will wait if there are six available。

If there were four in it closed, then it would only read four。 But in this case。

we know we're sending six。 But no, that's a very good question。

Good point。 OK。 All right, let's try to run this。 Make pipe experiment。

All right, pipe experiment。 This is going to be kind of anticlimactic。

but read from pipe bridging process hello。 OK。 So that's all it did。

What questions do you have about this at this point? What other questions? Yeah。 [INAUDIBLE]。

If you made the buffer 12 long, and then--, But you only wrote six。

Let's try it and see what happens。 Let's just try and see what happens。 OK。

If we made the buffer 12 and said, hey, let's read 12。 Let's see。

That was that size of buffer is OK。 And there we go。 Let's see。 Make pipe experiment。

Pipe experiment。 Same thing。 So what happened was it tried to read 12 bytes。 It read only six。

And then the file ended。 In other words, the input file ended。 And so it was able to continue。 Now。

what if we did this though? What if we said, oh, I'm going to forget to close my writer?

Let's see if this actually changes。 May or may not。

It may close it when it actually ends。 Let's just see。 Pipe experiment。 And let's see。 Yeah, OK。

So it did actually, in that case, close it。 But I bet if we ran it in Valgrine-- oh, I don't know。

how to the actual file。 In Valgrine, you can check to see if the files are still open。

And it will tell you。 Not a good idea to leave them open just for waste。 Yeah。 All right。

What other questions? Yeah。 Can you only read and write two times?

Is that a time when you write multiple times? Or is it OK to read?

Can you only read and write to one at a time? And if you had multiple children。

would you be able to only use this one? You can use this pipe for whatever you want。

As long as your logic is OK, you, could have all your children reading from it。

But you'd need to know what order things were happening, back in the parent and so forth。

But generally, if you're trying to write to multiple children。

you will probably create a different pipe for each child, and just keep them in an array。

And each child has its own reader pipe, that it's trying to read from。 And by the way。

we could have swapped this and had the child, write to the pipe and the parent read it as well。

But that would work too。 [INAUDIBLE], Yeah。 [INAUDIBLE], Oh, good question。

Why is it implemented as an array of two file descriptors?

This is the way they wrote it。 I mean, it has to be-- they just said, no。

you've got to have an array。 And it does have to be an array。 It can't be like two。

It's not two parameters。 One parameter, which they point her to an int array。

And it can't be more than two。 A pipe is only working on two。 Creates a reader and a writer。

That's it。 Yeah。 Could you explain why we wait for the child process before closing。

the write file descriptor? Yeah, good question。 So why do we wait for the child process before closing。

the file descriptor? It could be that if you close-- it probably could be--。

and I'm not 100% sure on this。 But it probably could be that if you close the file descriptor。

before the reader has done reading, it might actually say, oh, both the write was done。

I'm not going to actually produce any more information。 So you kind of want to wait。

You definitely don't want to close it until you're sure。

that the other process has done all the reading。 I think that's it。 Any other questions on this one?

Yeah。 [INAUDIBLE], At four-- I didn't say four processes。 Or if I did, I apologize。

We have four file descriptors now, because we have two file descriptors for FDS in the parent。

and two file descriptors for FDS in the child。 Only two processes。 Whenever you fork。

you get one more process。 Two processes, but you've got a copy of FDS。

So the parent has FDS 0 and 1, and the child is FDS 0 and 1。

Which is why we had four closes in here。 We closed FDS 1, and the child immediately。

because we're not writing to it。 We closed it after we're done reading。

because we're done with it completely。 And then the exact opposite in the parent。 Yeah。

So the questions-- I thought since they're copies, they don't update in each other's frame。

Good question。 The question is, wait, wait。 Are they not-- if their copies。

they don't update in each frame。 When you fork, they both end up pointing, to the open file table。

which is one file is open, for the actual file descriptors。 And it does update the reference counts。

for who's pointing to them。 So it does-- does it-- does it not make new-- does no more。

opens in that case。 Yeah。 So how does the child's copy do the open one do the writing。

in the finish? Good question。 The question was, how does the child process know。

when the writing is finished? It reads as many bytes as are available。 Is really what it is。

So I asked it to read six bytes。 So it will read six bytes if they're available。

That's how it knows。 It waits until they're all ready。 It's all available。 [INAUDIBLE], No, no, no。

It's possible for the child to try to read before the write is, done。

But it waits until those six are available。 So they're not available。

They might not be available when that read command is generated。 Yeah, good question。 Yeah。

So by waiting, do you mean like they're, just signal in the read function and walks in the child。

process? Then it waits until it receives that signal。

and then execute the rest of that child process after read。

Like once it actually passes the six bytes to read raw。

So what you're asking about-- the question was basically, wait, how does this waiting work here?

Read is a system command or system call。 And the kernel says, oh, OK, you want。

to read six bytes from this other file or from this file, descriptor。 Are they available yet? Nope。

no data has been read yet。 Oh, OK。 I'll just put you to sleep until they're available。

And then when they're available, I'll give you them back。 You will read them all。

And then you will continue along with your program。 Yeah。 That's actually very-- that's actually。

a very good question。 Because later on, we'll see a function where we go。 Wait a minute。

How does-- we're going to use a sort function。 How does the sort function know to wait for the data?

It just says, hey, give me all the data until the file ends。

And if there's no data-- if the file has an end, it'll just wait until data comes in。

So we'll get to it a little bit more of that later。

Yeah。 So if we change the whole load's high, then, we'll never have six characters, so we'll。

just have to have a read。 Yeah。 Let's try this。 It may actually-- oh, yeah。

If we don't close the thing, that's the good question。 The question was, wait。

what if we changed it to we only row, high and we only row three bytes instead of six? Let's see。

Make, pipe experiment, pipe experiment。 Let's see。 In that case, it looks like it--, let's see。

did we change anything else? We still-- oh, we didn't-- let's see。 Yeah。 In that case。

it may have been because we actually--, the parent ended and the file closed on its own。

That's probably what happened。 So in other words, the file-- so if we were。

to do something like this-- let's do this。 Let's do this。 While on semicolon。

Just keep going forever。 The parent won't end。 And I won't be able to tell if the child ends。 Ah。

yes we will。 Because they're making me do all this crazy stuff。 How about this?

It probably won't actually-- it shouldn't actually, turn anything。 But let's just do the print。

Child is ending。 Like that。 Make, pipe experiment。 Pipe experiment there。 OK。

so it did seem to read the bytes probably possibly, because the zero was on the end of the high。

That might be what it is。 I don't know。 We can check again。 There's some nuances in here that--。

let's see。 That one is still。 The parent is still three。 What if we made it only two? Hang on。

If we made it six, it would read past the end of the buffer。 That's probably not what we want to do。

But I mean, that would just be like--, let's see。 There we go。 Now we've only written two。

So I think what happens is these file descriptors, are what we call somewhat buffered。

If it gets a zero, it will pass it on to the other program, probably possibly。

There's probably what's going on there。 Where's what's that? Where's the-- oh, hi, Att。

Because I didn't pass the actual zero on there。 So oh, you know what? That's the other thing。 Yeah。

so it did get a zero eventually。 So it tried to read。 It must have read only two or whatever。

and then there is no zero on the end。 That's actually another thing。 So yeah。

it's a little more nuanced than that。 So what this tells you is that you。

do have to make sure your logic is correct so that you know。

how much data is being read and written。 It's not as important as you think。

because normally we will actually close files when, we're done writing to them。

So you should just remember to do that。 It may or may not work depending on what you're trying to do。

Yeah? Yeah? I'm sorry。 What was this experiment? This didn't include anything as it turns out。

Good question。 Yeah, I thought it was going to maybe pause and say, no, I didn't get enough data。

But it only wrote two, and then it closed anyway, and it didn't actually wait for more data as it turns out。

Yeah, I'm trying to think if there's a way to do that。 Let's see。 I don't-- yeah。

I'm not sure exactly how we would, get it to pause in the child。 I mean。

maybe when it gets to this wait PID, it says it does something with it。 But I'll have to look it up。

I'll try to find out this actually if we can fool it。

into pausing in the child to wait for more data。 There may be some time out as well in the read。

Yeah? What if you never wrote any data and it was just waiting to read? There was no data。 Yeah。

that's a good question。 We can try it and see if there's no data。

We're actually writing it all。 Make pipe experiment。 Oops。 Okay, pipe experiment。 Yeah。

If we write no data, it goes, I'm waiting for some data。

And there's probably either a time out in there, or when the read/write function finishes。

it's right。 It tells the read, go ahead and read anything, and so forth。 So yeah。

So this one told us something。 Said if we don't write anything, we'll get a--。

we'll stop in the child and wait for more data。 Okay。 Lots of nuance, I suppose。 All right。

But anyway, that's how pipe works。 Pipe creates two file descriptors。 One you can write to。

one you can read from, and whatever you wrote to the one you can write to。

gets read from the other one。 Okay? This actually makes things very interesting as we go along。

Okay? In fact, why don't I show you something first, before we get to the next little part here。

There is a program built into units called sort。 And what sort does is it will sort input that you type in。

line by line。 It will sort it--, do it。 So if I type bat and cat and apple and dinosaur and--。

let's do some other things in here。 Bling and cool and whatever。 And when I end, right。

if I do control D, it will actually, rewrite all the output in sorted order。 That's what sort does。

Okay? If I had a file, let's say unsorted。txt, and I put these words in the unsorted file。

I could do the following。 I could say sort unsorted。txt。 I could also do cat unsorted。

txt and then pipe it into sort。 Okay? And what that does is sort member takes input。

as you're typing it, waits until it's all in, sorts all the lines。

and then spits them all out again。 Okay? But this pipe idea, this little vertical bar。

is says take the output from cat, which is the words。 And send it into the input of sort。 Okay?

So you have to think about what's going on there。 There's actually the output of one file。

becomes the input of another file。 And we do that with a pipe。 Okay?

So that's actually what we're about to take a look at now。

Okay? All right。 So let's see how we're going to--。

this is the one we could run that again。 We already did that。 Okay?

We've already talked about all this, all this things about how the actual pipe works。 Okay?

I think that's a duplicate。 Okay? Here's an example that we're going to do。

And you will redo this function and make it more, robust for your next assignment, assignment three。

which is going to come out on Thursday。 And it is a function we're going to create called subprocess。

Okay? And subprocess uses all of these kind of--, these pipe and fork and dupe。

And I think called dupe 2, which we'll get to what, that means in a minute。

And it uses exec CVP and so forth to produce--, to basically say。

I want to send my output of my program, to some other file that's running。 Okay?

So it's kind of neat。 The thing we need to first look at。

is this struct that we are going to create。

It is called subprocess_t。 And here's what it has in it。 It has a PID。

which is the process of the--, I guess it's the program you're running。

process of the program you're running。 And it has a supply file descriptor。 In other words。

a file descriptor that if you write to it, it becomes the input to the file that you--。

the program you're running。 Okay? Yeah, I'll say that again。 It gives you the process identifier。

for the program you're running。 And it also gives you a file descriptor, that when you write to it。

it becomes, the input for the file you're running。

So let's think about how we're going to use this for sort。

And that's actually the example we are going to use exactly。 Once that goes on。

What we're going to do is we are going, to create a subprocess of the sort program。

And then we're going to pass into it, a bunch of strings。

And the sort program is going to sort them, and print the output to the screen。

Our program is going to pass in a bunch of strings to sort。

And we're going to let sort do the sorting。 That's what's going on。

You'll see how it works in a minute。 I'm getting some faces like, I don't get it。

You'll see when it-- when it happens。 How's it make a question? Is it the PID of the new program?

And you've got to have the input here。 And the PID of the subdominant。

It's the PID of the child process, which, is the one of-- in this case, the sort, for instance。

And you'll see why we need it, why we need to do that, because we need to wait for it。 OK。

we're going to create the subprocess and then wait for it, to end after we send it all the data。

You'll see how it works in a minute。 OK? All right。 So we are going to, again, pass in a command。

which, is going to, in our case, be sort, or whatever program we, want to run。

And it's going to create that process, start that process, running。

and it's going to basically tell the process, hey。

I'm going to give you a bunch of standard in as if you, were typing it。

And then your job is to do whatever you want with it。 In this case, it's going to sort。 In our case。

it's going to sort it。 OK? That's what's going on。 So let's actually look at it。

So here's what I'm going to do。 I'm going to type in--, I'm going to start with main, actually。

Subprocess。c。 I'm going to start with main here。 OK, here's the type。

Here's the struct that we're going to have。 And in main, here's what we're going to do。

We're going to make this program sort a bunch of words for us。

that we're going to pass to the sorting program。 So we're going to start out。

and we're going to say, OK, we haven't written this function yet, we will。 Subprocess。

TSP equals subprocess。 And the sort program lives at slash user slash bin slash sort。 OK?

Our subprocess is going to take that sort function。

It's going to create this subprocess T struct for us。

It's going to create all the little pipe that's necessary, and it's going to set it all up。

We'll get to that。 OK? But that's what it's going to do。 It's going to start the sort program going。

waiting for our data。 All right。 So then we are going to create--, let's create some words。

charge star words。 And this, in this case, is going to--, I don't know。 We'll use some words like--。

let's use--, Jerry calls them SAT words, like phagicity, umbridge。 Doesn't matter。

whatever words you want。 Susser, equation。 Hopefully I'm spelling these right。 Halcyon, et cetera。

OK, we're just basically creating a bunch of words, that we're going to pass into this sort。

because we want to do that。 Pulperitude, let's see, evolution, some, not, some。

Let's find a right to cross out。 OK, and then how about indivotigable? OK。

it doesn't matter if I didn't spell them right。 That's right。 OK, anyway。

we're going to create a bunch of words。 They are not sorted right now。 OK?

They're in this kind of random order。 OK? And then we are going to take those words。 I equals 0。

I is less than size of the words divided by size of words 0。

That should be familiar from 107 of what we're doing there。 And then I plus plus。 OK?

And then we're going to use a new function, called called dprintf。 OK, dprintf says a。

Print to a particular file descriptor。 OK, well, we have a file descriptor。

because we created the subprocess up here, sp。 We're going to go right to the supply fd。

And we're going to write out each one of these strings, separated by the new line。 OK。

so what we're going to do is we're going to say, hey。

I'm going to take this file descriptor I got back, from subprocess。

And I'm going to throw a bunch of words at it。 It's followed by new lines just as if I。

were typing those words in to the sort function。 All right? And then after we're done with that。

we're done with the closing of--, or we're done writing the word so we should close our thing。

We'll test this and see if we don't close that if it stops, the sort of sort actually stops。

I doubt it will, but it may。 I don't know。 And then we could actually-- actually in this case, yeah。

well, we might as well--, PIDT, PID equals weight, PID4, the PID。

that we were given in the subprocess。 Status and 0。 And then we are going to, let's see。

return PID equal--, we're going to basically check and see if we got an actual good, return value。

If we got the correct PID back from weight PID, then we have to check W if exited。

And if that is-- if we exited properly, we better return the exit status from the file we created。

Or let's just return negative 127, which basically means not good。

We didn't return anything reasonable。

All right。 Let's see。 So this should actually almost work。

except we didn't create the subprocess function yet。 Make subprocess-- OK, good。

So at work, we have no-- not good。 That's everybody get what's going on here。 Creating a subprocess。

It's about to take in all these words, we're going to send into it。

We're going to de-print F to that file descriptor, that we know that subprocess is listening on。

And then we are going to wait for that process, to do all the sorting。 When it does。

we're going to end our program。 That's what Maine's doing。 OK。

All right。 So the subprocess function itself。 OK。 Well, we're going to use our pipe in this case。

FDS2, we're going to create that。 And then we're going to create a pipe。 FDS, like that。

And then we are going to create a little subprocess T, process-- we'll call it process in this case。

This is a struct remember。 So we can use the little curly braces to do this。

This seems a little weird。 But I'm basically going to call fork。 And then I'm going to pass it--。

I'm going to do make the FDS1, which is the writer, which。

we're going to send back to the calling function。 This is what's going to get returned from this function。

OK。 We've got the PID。 And then we're going to also populate it。

with the writing part so that we can write, from our original calling function。 All right。

So it's going to be there。 If process。pid, which we just created, equals 0, well。

we're in the child。 So what we need to do is we need to actually do some setup。

so that we can read from the file descriptor as standard in。 What does that mean? OK。 Well。

no writing necessary from the child, right? So let's do that closed business before FDS1。

No writing necessary。 And then we're going to use this function called dupe 2。

Dupe 2-- and I'll write the whole thing out, what we're going to do here。

We're going to take that reader that we get back from the pipe。

And we're basically going to copy it, such that standard in--。

whenever we read from standard in in the child, it is actually going to read from that pipe。

That's what this is doing。 It's creating a copy of this FDS0 descriptor。

and setting it such that FDS--, or the standard in file number, which is basically-- which。

is generally 0--, is now going to point to our new pipe。 So it's basically saying, no more typing。

We're going to get our input from this file descriptor。 That's what the dupe 2 is doing。 OK?

Once we do that, because we've duplicated it, we can actually close FDS0 because we're done with it。

Already duplicated。 OK, and basically, this is-- again, this is now-- we've already, said, hey。

standard in is going to come from this pipe。 So we can close the original because we've。

done made the copy that we need。 And then we're just going to do a little bit of setup。

to actually call our command here, which is another, like。

slash bin slash sh with the dash c argument, which, says, run the program about to pass you in。

And then it is going to-- we can do a little casting, because it's constant。 Come in。

And then we need to make it no on the end there like that。

And that's basically creating the command line, that we're about to use exact cvpn or exact cvpn。

And then we're going to cvpx cvpn, rv0, rv。 And that's that。 If-- and that's it for the child。

The child is going to never return。 Child will never get to here。 Sad face。 All right。

And then-- I'm ready to have you face。 So that's actually what we want。 In the parent。

we are not going to use, not used in parent。 We've also got a closed FDS0 there。

because there's no reading that's happening in the parent。

We're just letting sort print out to the terminal。 And then we have to return the process。

So that's what's happening with this function。 It's basically doing what? Creating a pipe。

So we've got two file scriptors。 Setting up this subprocess T struct with the P ID of the child。

and then FDS1 for the parent。 This is for the one the only one we care about is the parent。

And the child, if it's the child, we have to do some more setting up。

We close FDS1 because we're not actually writing to it。

We are going to duplicate FDS0 so that we're able into。

standard in so that we can read from standard in from that file, as standard in。

Then we're going to close that because we duplicated it。

Then we're going to set up the command to actually use, execcvp and then we're going to do execcvp。

And if we're in parent, we are closing the reader, and returning their parent。 OK。 Question。

How would you close the one that you duplicated in?

How would you close the one you duplicated in D2? Well。

we closed the one there that we actually did there。 You mean the other one?

That will get closed by the program we call when it just, closes all that's open。

Or it will just-- when a program in, standard in gets closed automatically。

That's what would happen there。

All right, let's actually try it。 Make subprocess。 We'll see if I have any。 OK, so subprocess。

we passed in all those words。 And subprocess should sort them。 And it did。

So subprocess used sort to actually do the sorting for the, words that we passed in。

It was exactly as if I had had these words--, oh, they're going to be sorted now。 Hang on。

Then unsorted。txt。 Let's do this。 There's probably a command in then to actually scramble up。

your words。 But OK, so there we go。 Those are not sorted。

It would be exactly the same as doing this。 Cat unsorted, pipe into sort, and there it does the。

actual sorting for us。 All right, so now that you've seen it work and you've, actually done that。

What other questions do you have about it? Yeah。 Is that what he does at the start of the subprocess?

Ah, that's such a good question。 The question was, hey, does the exact CVP discard the old。

file descriptors? No。 File descriptors are actually passed on to the child or。

passed on to the program exactly as they are。 And that's a good question about why they chose to do that。

Probably for this exact reason is why they do that。 But anything that's process related。

you kind of want to, keep it with the process。 You don't want to destroy file descriptors and things。

because the other new program might use it。 All the old memory--, right, I couldn't--。

in the other program, I wouldn't have no access to, FDS0 and FDS。 Well。

they don't exist in that program。 But the file descriptors are still open in that program。 Yeah。

good。 Very good question。 What other questions you have on this one?

Everybody gets how it's working? All right, and you get why we might want to do this and how。

it's actually doing the piping。 OK。

All right。 OK, so we've got this。 We've got the SAT words。 Here's an important part。

The close call--, and we'll have to check that, because I promise we'd check, that。

When you do the close in the actual parent, that's what, tells sort to start sorting。

because it doesn't get me more, data。 It's a step I control, control D, in the words that I typed。

So let's actually try it at May, depending on how--。

it's written in May, actually does。 But what if we forgot to close this after we did it?

Make subprocess, and then subprocess。

There we go。 It's waiting。 So sort is over there going, hey, I haven't seen the。

end of the file yet。 I'm just going to wait for you to end the--, for the file to end。

So in this case, we've crashed while we've paused sort。 We've frozen it。

because it's still waiting for our data。 And since we can't wait for the data anymore。

or since there's, no more data coming, it will just--, it will not actually end。

So that's another good reason for remembering to close, your file descriptors。 Yeah?

Why is it that close that one back close? Good question。 Why is it this close that prevents the--。

and this is a good question。

What is this close actually doing? Well, remember, we're now in the parent。

We've set the subprocess, the sort program going。 What's the sort program doing?

Waiting for us to type in--, it's waiting for us to type things in, right?

So at this point down here, this is as if we are typing in, a bunch of words。

They go to our or the supply FD that we've created with the, pipe earlier。

They are being read by standard in in sort, which now has been。

duped so that its standard in is the output part--, or the reader for the supply or the pipe。

And it's waiting。 Sort waits until you the file ends before it does the sort。 And it has to, right?

I mean, sort can't-- you can't sort。 I mean, you can start to sort as you go, but you can't。

officially print anything out until you get the last word。

Because what if it was the first word in the list? You couldn't have already printed something。

So it needs to wait until no more words are incoming。 Well, in this case。

the only way it knows no more words, are incoming is when that file ends。 And it says, OK。

no more words are incoming。 Then it can continue。 That's why it's that close。 Very good question。

Right, where-- yeah? OK, so let me reiterate。 So dprintf, normal in print, says the terminal。

It says we've typed it to the sub-process。 It will actually go into our sort。

Dprintf prints to a file descriptor。 Whatever file descriptor we give it。

printf just normally prints the terminal。 Dprintf is new。 And it goes, oh, look。

there's a parameter right here, which is our file descriptor。

That file descriptor is what dprint and prints do。 So in this case。

we're going to print to the supply file, descriptor, which is the writer end of the pipe。

The reader end has now been turned into standard in for sort。 Write to this end, read from this end。

into sort。 That makes sense? Now, did you have to follow up more questions? No。 OK。 Yeah。

that's exactly what your exactly where it was。 All right。 Anybody else on this one?

More questions on this? OK, good。

All right。 You will get lots of practice to this for the next assignment。 All right。

Oh, we've already done this。 Dada-da-da, sub-process。 OK。 So we have about 15 minutes to go。

Let me introduce you to another topic。 We may not actually write any code for this right now。

When you have two processes that you want to communicate between the two。

One way to do it is by reading and writing actual data。

But that's not necessarily really what you want to do。

You just want to basically tell another process, hey, I'm dumb with something or something。

has happened in my process that you might be waiting for。 Or, hey。

go do this thing because it's time。 I'm dumb with my stuff。

It's your turn to go take that and run with it。 OK。 We do this with the idea of signals。

And a signal is just a message。 In fact, you can't even really pass data along with it as it turns out。

It basically just says, hey, this following thing that you can pass in one number that。

says what kind of a signal it is basically。 It's a small message that allows you to say some event has occurred。

Signals get sent by the kernel all the time。 Every time you hit Control C in your program。

you have sent a signal to the program that, basically kills it。 That's what that's a signal。

There's a signal called SIGKILL。 All right, which kills it。

There's lots of other signals we'll talk about as we go along。 How do these signals get handled?

In other words, what function does the processing once a signal comes in? Well。

you have a special function called a signal handler。

And a signal handler is a function that actually gets called only when some event happens。

So the kernel is waiting around for some event to happen。

It realizes there's a signal that needs to get sent。

And it tells that function in your program to do this。 If you took CS107E。

you've dealt with signal handlers a lot when you did interrupts and, so forth。

You created a function that when you typed a keyboard press, let's say that function got, called。

It's called event handling。 And now we're able to do this in C。 There's a SIG SEGV。

which happens when you get a SEG fault。 Whatever you do。

you get a signal sent to your program that says, you're SEG fault, and it。

generally kills your program。 You could capture that and deal with it and not have your program killed。

Some programs do that, but that's one thing we can do。

And this is what signal handlers allow you to do。 They allow you to take those signals and do something with them。

There are some signals you can't actually capture。 One is SIG kill。 You're not allowed to say, hey。

you can't kill my process。 And the other one is a stop。

which we'll talk about later as we go along to。 So basically what we're trying to do is we're trying to say。

here's a process。 When something happens, some event happens, call this other function。

No matter where it is in our code, by the way, some other external thing calls our function。

Our program stops everything, goes to that function, and starts dealing with the signal。

that came in, which is a little weird to deal with when you get into it。

We'll do lots of details on this。 One of the kinds of details, our signals are there。

There's SIG floating point exception, which is basically when you do things like divide, by zero。

you get this weird signal that says, whoa, you did something math wrong, and that's, bad。

I can't deal with that, so I'm going to send a signal。 I told you we got SIGINT。

which is the interrupt。 I think I call it SIGINT, it's called SIGINT。

And that's the Terminator program。 There is SIGSTOP。 Whenever I did control Z earlier。

that actually stops your program and just pauses it for a。

while and brings you back to the terminal if you're in the terminal。 And then you can have a SIGINT。

which continues the process running。 We will see some really neat examples of when those two signals are used。

You can actually send signals to yourself, by the way。 You can tell your own program, stop。

just wait。 You can tell your program to do。 It basically says。

wait for somebody else to continue you。 And we'll see that in some interesting examples of that。

When pipes end, you get a SIGPITE。 When children end, you get a SIGCHLD for a child ending。

And that's an interesting one that we'll use, as you can imagine。

This is the one we're talking about right now。 SIGCHILD is what happens whenever the child changes state。

In other words, your child process is going along doing something。 If it stops a SIGCHILD message。

you set the parent saying, hey, your child just stopped。 Do you care about that? Maybe you do。

maybe you don't。 A SIGCHILD happens when the child ends。 The signal handler gets called。

And it also gets called when it continues as well。

The parent process should actually use wait PID to handle when a child state changes。

We've done that already。 We've kind of ignored it in a couple cases, by the way。

You go back and look at the simple essays。 You'll see that when you have a background process。

we actually ignore the wait PID and, we never do it。 It's actually not the best way to do it。

You should have handled it at some point, even though we didn't。 But we'll get to this。

You can get a SIGCHILD sent to a particular function in your program。

That's when you end up calling wait PID。 You don't actually normally do it in the main loop in the program。

Sometimes you do, but normally you don't。 You actually wait until the child handler function is called。

Most of the time we ignore all the signals。 Most of them we ignore。

We can't ignore SIGINT and we can't ignore stop, but we can ignore all the other ones。

and normally you do。 So far in C and C++ you've never handled a signal before unless you're in 107E。

But in CS107 or CS106B, you just ignored them。 These signals were happening and whatever and you're just ignoring them or they were。

killing your program。 So that's what's happening in there。

We will write some functions that actually do this。

The purpose of a SIGCHILD handler is almost always do wait PID and maybe handle some other, things。

All right。 So here's some code。 We'll just take a real quick look at this program。

This program is a little Disneyland example actually。 Jerry wrote it。

And what it's doing is it is basically setting up a bunch of forks and it's modeling。 Okay。

the parent takes a bunch of his children to Disneyland and sends them off and they, all go play。

And then when the dad goes to sleep while the children are out playing and then one by。

one they come back, dad wakes up and he says, "Oh, I'm glad you're back。"。

When all the children are back, they leave the park。 That's what this is modeling。

And here's what it actually looks like。 It's pretty simple。 You've got a print F that says。

"Let my five children play while I take a nap。", And then it sets up a signal to a function that we're about to see。

Function is not here yet。 It's going to set up a SIGCHILD signal to that function called REAP Child as it turns out。

That's the name of the function we'll write。 Then it's going to do a for loop for the number of kids。

five kids in this case。 And then if it's the child process。

it is going to make the child go to sleep basically for。

three seconds based on the number of the child。 Okay。

so first child is going to come back immediately。 The second child is going to sleep for three seconds。

And so actually, no, that's not true。 The first child is going to sleep for three seconds because it's one that it's going to。

The first child is going to sleep for three seconds。 The next child is going to sleep for six。

et cetera。 And then it's going to when the child wakes up, pretend they're off playing, it's going。

to close that child, finish that child。 At that point, this signal handler will get called。

And then that is about it。 Okay, actually there's a little bit more to it in terms of what happens in the parent。

In the parent, you actually have to wait for them。

While the number of children done is less than the total number of children you have, which。

is five in this case。 Okay, printf, at least one child is still playing so dad sleeps, nods off。

And the dad is going to sleep for five seconds。 Okay, and then after the dad, when the dad wakes up。

when the child handler happens。

the dad will actually wake up。 Sleep is an interesting command。 When you get any signal。

sleep actually stops。 So you think it's doing it for five seconds。

but it's actually going to do it until some, signal happens。

Okay, and down here we have the tiny little function called reap child, which basically。

waits for the child to just end it using negative one, because we don't know which one ended。

We just know that some child just ended。 And then it increments the number of done。 Numb done。

because it's in a different function than main, we actually have to use a global, variable。

It's kind of too bad because we don't necessarily like global variables, but this is a case。

where you kind of have to use a global variable because there's no other real way to pass information。

between two functions。 Okay, one other thing。 The reap child function is in the parent。

So the parent's process gets called when the child has a change of state。

Okay。 So let me actually, let me actually type this in and run it。

See this one's called five children。 Actually, let's do this。 Copy five children to five children。

See, okay, make five children。 Five children。 Here's what it does。

At least one child is playing so dad nods off。 The first child comes back after three seconds。

returns to dad。 The first child is after six seconds, returns to dad。

Third child's after nine seconds, returns to dad。 The fourth child returns to dad after the next second。

the fifth child returns to dad, after the last time。 So actually, I guess in this case。

the sleep didn't actually stop。 So it did the parent one didn't。 The parent looked that up。

I thought it happened。 I guess it's only when it's off the look that's up。 I thought it stopped。

but it didn't in this case。 Basically, that's what happens in the thing。

After a child comes back after three seconds, after three seconds, after three seconds。

the reaped child function gets called when the child finishes。 Let's look at the code again。

If the last two minutes or four minutes, we will talk about whatever questions you have。

about it。 What questions you have on this code at this point? What's happening here?

Maybe that you just haven't understood。 Why is there a parameter for reaped child? Oh。

good question。 That is, I believe, the signal that was triggered。 In other words。

it's going to be "sig child," as it turns out。 That's good question。 The question was。

"What was this parameter here?", We're not using it here because we don't care。

We know there's only one signal that could get into this function。 But yeah。

if we had multiple signals that we sent to the same function, you might want, to know that。

Good question。 The only reason we're having orders is because we're waiting for such a long amount of time。

that it's likely that number one will finish before number two and number two will finish。

before。 Yeah, it's a good question。 The question was, "Wait, why is it happening in order?"。

Remember how we set this up。 We said that each child waits for three seconds times the number it is。

The first child waits for three, then six, then nine。

That's the only reason it's happening in that order。 If they have on Wednesday。

we'll see an example where we're trying to do them all at the same。

time and we'll see if this actually breaks, which is not really what we want。 How's it going?

The output。 The output。 Yes? I was trying to prove why when the dad makes up the fire。

the question was, "What is the, question?", Yeah, the good question。 Why is the dad wake up here?

The dad is sleeping for five seconds。 The first child comes back at for three and the dad still got two more seconds of sleeping。

Right? That goes back to sleep for five, but in the meantime, six happens and second nine happens。

That's two in a row before dad wakes up again。 I was thinking the dad would wake up every time。

but it's not true。 Yeah。 Yeah? It's a good question。 The question is。

"Why are we doing a weight PID in the REAP child?"。

We do want to clean up after children。 When a child ends。

we should call weight PID to do the clean up。 That's the function that does the clean。

tells the colonel, "Go clean up after this child。"。

So we might as well do it in REAP child because we got to clean up for it sometime, might。

as well do it right then。 Good question。 If you didn't wait with the REAP memory leak。

If you didn't wait, good question。 If you didn't wait with the REAP memory leak。

it wouldn't necessarily be a memory leak, but, it's kind of like a memory leak。 I mean。

I guess it's a memory leak in the sense that when your program ends, all the, children get handled。

In the meantime, we're done with them, why are they still available? That's kind of。 In some sense。

it's a little bit of a memory leak。 There's extra resources being used that。 Why use them? Yeah。

Any other questions on this? Yeah? Sorry, weight PID like cleans up the memory from the child as well?

Yes。 Good question。 Weight PID does clean up the memory from the child and it returns any resources that。

the child might be using。 Okay。 Yeah。 [inaudible], So, the REAP child could have done anything。

I mean, you could just find that signal to any program process。 REAP child, yeah。

that's a good question。 REAP child could have done anything。

There are any signal and there's less than 30, but a bunch of different signals。

Any signal we could have this function handle。 And remember。

this function gets called no matter what。 Dad happens to be sleeping。

This function gets called and starts running while when that happens。 So。

there's two parts of your program now in the same process, which could be doing two。

things at the same time now。 So, it's another parallelism that we have to kind of deal with。

All right。 Last question, then we're going to let you guys go。 Yeah? [inaudible]。

You could have given, yes。 Good question。 When you say signal, signal, child, root child。

whatever you type here, that's what you end。

up getting。 That's the signal that REAP child is going to look for or get called triggered on。

We're passing SIG child because we know we're creating a bunch of children that we want, to handle。

That's why we're having SIG child。 The SIG child signal gets no matter what。 In this case。

we're letting it, we're making it call our function。 That's that。 Okay。 If you have other questions。

come on up。 I will see you guys Wednesday。