[Autogenerated] let's now do something useful with the jobs API Let's
use the CPU rate limit feature for windows eight or later to limit the
CPU consumption of a process over choosing.
We'll put that single process into a job and then that
limit of course it wouldn't be much more troubled to add
more processes to the same job.
And so that the limit is applied to all the processes in the job as a whole.
Let's create a simple console application.
Let's call that CPU limit.
Let's call the solution jobs because it is part of the
jobs module and use create here.
So it's usually I'm going to add and include here for
Windows dot edge and for S T D I.
O.
To have some simple printed output that we can use and we can start coding.
So the first thing I want to do is to accept enough parameters from
the command line so we can select the process in question and the
percentage we would like to use for the limit.
So I'm going to accept these parameters.
And if we have too few parameters let's just display simple usage
message for the color and we need to use CPU limit here.
And what we want really is the process ID that we
want to limit its process usage, processor usage and the percentage.
So let's just write percent here.
That will be good enough and return if we have three parameters
so we have the executable name and the P I.
D.
And the percentage we can grab them.
So let's just do that for convenience.
Let's grab the process I.
D.
Here.
And then also the percentage from the second parameter in the command line.
Of course, disregarding the executable name.
So I'm using the simple eight Y C.
API function to convert this esky characters into an integer we can use.
So the first thing we need to do is to create a job object.
Let's do that by calling the create job object API And as
you can see it accept two parameters.
One of them is the job attributes which is ready to security
mostly which we don't care about here too much.
Let's just provide now we'll get some different security which is good
enough and then a name for the job which is completely optional but
lets you use it anyway because it would be easier to identify in tools
Once we test our application, let's just call that CPU limit job for convenience.
So if this fails for whatever reason there's really nothing we
can do and simply return some kind of error.
So let's just write error.
Creating job.
And what I'm going to do is create a simple error function.
Its only purpose would be to accept some kind of text and display the
text with the company value return from get last error.
So we do something like this.
We're going to display the string whatever that string is and
whatever get last error happens to return.
And essentially we're done.
There's no point in moving any further.
So if this fails, it should actually succeed.
There's no good reason for that to fail unless we have really um
exhausted systems memory or maybe there's a different kernel object
that happens to have exactly that name.
So this is unlikely, but still a good idea to make sure error handling is robust.
So now that we have the object really want to do is call the
function assign process to job object.
So let me try to call that.
You can see that the first parameter is the handle to the job.
So that's not a problem.
We have that, but the second parameter is a handle to the process.
And so this is something we don't yet have.
We only have the process idea,
but you don't have a handle to the prom process which has to be
powerful enough for the assignment to work.
So if we fail to do that, for whatever reason,
I'm going to just use my simple error function here.
Error in assigning process and let the whatever get Lester returns be displayed.
So first we have to find the handle to the process.
And for that we're going to use the function we met
already in the previous course, which is open process.
Well, in fact, I think in this process in this course as well.
So with open process, we need to ask to open a particular process idea.
This one we have that's the last parameter here.
This flag is for handle inheritance something we'll
discuss in the future course and it's completely
irrelevant to what we're trying to do here.
And the most important piece besides the process
idea of course is the access mask.
We need to ask and so we need to ask for process
terminate and process set quota.
Where did I come up with that?
Well this is documented in the normal documentation for the
assigned process to job object function.
If I just press F one here to open my online documentation here,
you can see in the documentation that it says the handle to the
process must have a certain access mask and you can see here the
process set quota and process terminate.
Are the minimum access mask beats you have to have set in order for
the assignment to have a chance of succeeding.
This is exactly why I use this particular flag.
So it's always important to check the documentation to see if we
can figure out what the correct parameters are and that could save
us lots of frustration time later on.
So if this fails, for whatever reason, of course,
there's no way to to continue here.
And as a counter example,
we've seen protected processes in this course and we know that protected
processes cannot be accessed in an invasive way specifically.
No protected process can be opened with process at quarter,
which means that you cannot add a protected process into a job unless you
are in a protected process yourself and you have a higher protection level
than the process you're trying to access.
But for normal process they can't add a process which is
protected or protected light into a job directly.
Only the colonel can do these kinds of things.
Okay, so we are in open process.
Let's just do the the error handling here in a very simple sentence.
So once we have a handle to the process,
we can assign the process to the job object.
Once that succeeds, we can technically close the handle to the process.
There's no need to keep that open.
You might be wondering why that is.
And that's because the job adds a reference internally to the process object.
So even if you close your handle the process and the job object are not going
to go away and the job will keep a reference so that the process object will
be maintained as alive no matter what you do.
So that's just some clump.
We may want to do And finally we get to the last piece
of the puzzle which is the function.
Such information, job object.
This is where we need to specify the handle to the job and the kind of limit
would like to have and any associated data with that limit.
So let's just press F1 again and we should be able to get to
the documentation once again and you can see here there's the
list of all the supported values.
At least all the documented values.
Some are actually not really documented and don't appear here.
And like I said, the power of jobs has increased between Windows versions.
So you can see in some cases it says this particular op is not
supported for certain operating systems.
And exactly this is what we need.
Job object CPI rate control information.
That would be the enumeration.
Let me just paste it here.
And then as you can see there's an associated data structure
that is this one that we have to provide.
So let's just grab this data structure and then then just fill it as required.
So here's my data structure.
Let's just call that info in terms of parameters we have to
provide the address of that data structure and the size of the
data structure fairly easy to do.
And again, if this fails for whatever reason,
there's no point in continuing So we can again call our air
function saying failed to set a limit and we're done.
And so the last thing to do really I'm going to use sleep
here infinite in order to keep the process alive and explain
very soon why I want to do that.
But you know that it doesn't really have to be in this way.
So you don't really have to keep the process alive for the limit to be applied.
So the job handle will be closed if this particular process shuts down.
but the job itself will not die because the target process is still alive.
As long as that process is alive but yet we have to fill
the data structure here appropriately.
Otherwise this will not work.
So one thing we need to do is to fill out the control flags and
the control flags represent the features I would like to use
here for the separate information.
Again,
this is all fully documented and the first thing you want to
do is to have this flag for enable because without that the
option will actually make it this.
So essentially if you want to remove that limit later on,
you can make the same kind of call but don't supply this flag and the
limit will be disabled or removed from the job.
So obviously we need that enabled thing,
I'm going to add another thing which is called the hard cap.
So this flag of hard cap says that even if there is more CPU to go around
and don't want the job to get any more CPU time than what I specify the
default without the hard cap flag means that if there is more CPU to go
around the job can get more CPU uh percentage.
However,
if things start to saturate so the system is
completely saturated with CPU activity.
This job will be the first to suffer by reducing its CPU rate limit based on
the limit specify right now and this is done using this member called CPI
rate as you can see or perhaps kind of get a hint.
There is some other options for this uh for this kind of
control but we'll keep it simple and use just this option
to set a particular percentage.
So we obviously need to use the percent value accepted from the command line.
However, the way this parameter works, if you read the documentation,
this number you provide is actu Really based on an extension or
expanding of the value by a factor of 100.
So the basic idea is that the colonel doesn't
really like floating point numbers.
So in order to provide something like 5.3%,
you actually have to provide the number 530.
So 100 times larger than the actual percentage which would
allow you to have fractional values.
So in the cmdlet I'm not going to accept any fractional values.
We're accepting that as an integer but so to make it be interpreted in
the correct way we have to multiply that by 100.
So if I provide 10 the actual value to to provide to
represent 10% is actually 1000.
If I just provide 10 it would be interpreted as 100.1%
which is of course not what I want.
So once we get here maybe a little printer of saying success
let's build that if it builds successfully.
So now the next step is to test it, which we'll do in the next video.