Quickstart: Adaptive Partitioning Thread Scheduler
This example gives you a quick hands-on introduction to the thread scheduler. First you need to determine whether your target system includes the adaptive partitioning module. Next, we'll take a look at the programs you'll use, and then you can try adaptive partitioning for yourself.
Is adaptive partitioning running on your system?
To determine whether adaptive partitioning is running on your target system, type:
aps show
If you see an error message, you need to add adaptive partitioning to your OS image. On your development host, do the following:
- Go to the directory that contains the buildfile for your system's boot image.
- Create a copy of the buildfile.
For example:
cp my_buildfile.build apsdma.build - Edit the copy and search for the line that starts procnto.
The line might look like this:
PATH=/proc/boot:/bin:/usr/bin:/opt/bin \ LD_LIBRARY_PATH=/proc/boot:/lib:/usr/lib:/lib/dll:/opt/lib \ procnto-smp-instrNote:In a real buildfile, you can't use a backslash (\) to divide a long line into shorter segments; we've done that here to make the command easier to read. - Add
[module=aps]to the beginning of the line:[module=aps] PATH=/proc/boot:/bin:/usr/bin:/opt/bin \ LD_LIBRARY_PATH=/proc/boot:/lib:/usr/lib:/lib/dll:/opt/lib \ procnto-smp-instrYou can add commands to your buildfile to create partitions and start programs in them, but when you're experimenting with scheduler partitions, it's better to do it at runtime, so that you can easily make changes. For more information, see the Setting Up and Using the Adaptive Partitioning Thread Scheduler chapter.
- Save your changes to the buildfile.
- Generate a new boot image:
mkifs apsdma.build apsdma.ifs - Transfer the new image to your target system.
- Reboot your target system.
The code
You'll need some programs to run for this tutorial. If your system has a GUI, you can use a graphical program (e.g., animated gears) that runs continuously; otherwise, you'll need a program such as this, which we'll call looper.c:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <time.h>
int main( int argc,
const char *argv[] )
{
struct timespec t;
uint64_t nsec = 0, old_nsec = 0;
unsigned int delay_msec = 0;
delay_msec = atoi (argv[2]);
clock_gettime (CLOCK_REALTIME, &t);
old_nsec = timespec2nsec(&t);
while (1)
{
delay (delay_msec);
clock_gettime (CLOCK_REALTIME, &t);
nsec = timespec2nsec(&t);
printf ("%s %ld\n", argv[1], (long unsigned) (nsec - old_nsec));
old_nsec = nsec;
}
return EXIT_SUCCESS;
}
You'll run multiple instances of looper in this tutorial, so
argv[1] is a string that you can use to determine which instance you're looking at.
The next argument (argv[2]) is the number of milliseconds to wait between iterations;
you might need to experiment with this number so that the program uses a significant amount of
CPU time, without using it all.
You'll also need a program (which we'll call greedy.c) that simply loops forever, so as to consume as much CPU time as possible:
#include <stdlib.h>
int main( void )
{
while (1)
{}
return EXIT_SUCCESS;
}
Compile and link the programs, as appropriate for your target. For example:
qcc -V gcc_ntox86_64 -o greedy greedy.c
qcc -V gcc_ntox86_64 -o looper looper.c
Transfer the binaries from your development host to your target machine.
Let's try it!
For this tutorial, you can log in as any user.
You don't have to be root to manipulate the
partitions because the security options aren't initially set.
If you use the thread scheduler in a real
system, you should choose the appropriate level of security.
For more information, see the
Security for Scheduler Partitions
chapter in this guide, and the entry for
SchedCtl()
in the QNX Neutrino C Library Reference.
You might need to use multiple virtual consoles, in order to run several foreground programs simultaneously:
| To go to: | Press: |
|---|---|
| The next active console | CtrlAltEnter or CtrlAlt+ (plus) |
| The previous active console | CtrlAlt- (minus) |
| A specific console | CtrlAltn, where n is the console number |
Let's create some adaptive partitions and run some programs in them:
- The thread scheduler automatically creates one partition, called System.
Create some additional partitions:
- one called partitionA, with a budget of 10% of the CPU time
- another called partitionB, with a budget of 30%
by using the aps utility:
aps create -b10 partitionA aps create -b30 partitionBThe new partitions' budgets are subtracted from their parent partition's budget (the System partition in this case).
- Use the aps utility to list the partitions on your system:
$ aps show -l +-------- CPU Time ------+-- Critical Time -- Partition name id | Budget | Max | Used | Budget | Used --------------------+------------------------+------------------- System 0 | 60% | 100% | 0.21% | 100ms | 0.000ms partitionA 1 | 10% | 100% | 0.00% | 0ms | 0.000ms partitionB 2 | 30% | 100% | 0.00% | 0ms | 0.000ms --------------------+------------------------+------------------- Total | 100% | | 0.21% |Note:The -l option makes this command loop until you terminate it (e.g., by pressing CtrlC). For this example, leave it running, and switch to another console. - Use the on command to start a process in partitionA:
on -Xaps=partitionA ./looper A 5 - In another console, start a second instance of looper in PartitionB:
on -Xaps=partitionB ./looper B 5If you switch between consoles, you'll see that the programs are running at approximately the same speed.
- To determine which partitions your processes are running in, use the
pidin sched command.
For scheduler partitions, the ExtSched column displays the partition name.
For example:
pid tid name prio cpu ExtSched STATE 1 1 /procnto-smp-instr 0f 0 System READY 1 2 /procnto-smp-instr 255r 0 System RECEIVE 1 3 /procnto-smp-instr 255r 0 System RECEIVE 1 4 /procnto-smp-instr 10r 0 System RECEIVE . . . 110613 1 ./looper 10r 0 partitionA NANOSLEEP 118807 1 ./looper 10r 0 partitionB NANOSLEEP 127000 1 proc/boot/pidin 10r 0 System REPLY - Run the greedy program in partitionB, where it will
consume as much CPU as possible:
on -Xaps=partitionB ./greedy & - You might be surprised to find that both instances of looper continue
to run at roughly the same speed.
The output from the aps utility might explain why:
+-------- CPU Time ------+-- Critical Time -- Partition name id | Budget | Max | Used | Budget | Used --------------------+------------------------+------------------- System 0 | 60% | 100% | 0.29% | 100ms | 0.000ms partitionA 1 | 10% | 100% | 2.26% | 0ms | 0.000ms partitionB 2 | 30% | 100% | 97.46% | 0ms | 0.000ms --------------------+------------------------+------------------- Total | 100% | |100.00% |Note that partitionB is using more than its budget of 30%. This occurs because the other partitions aren't using their budgets. Instead of running an idle thread in the other partitions, the thread scheduler gives unused time to the partitions that need it.
- Start another instance of the greedy application in partitionA:
on -Xaps=partitionA ./greedy &The instance of looper in partitionA is now slower than the one in partitionB. The output of aps looks something like this:
+-------- CPU Time ------+-- Critical Time -- Partition name id | Budget | Max | Used | Budget | Used --------------------+------------------------+------------------- System 0 | 60% | 100% | 0.17% | 100ms | 0.000ms partitionA 1 | 10% | 100% | 26.89% | 0ms | 0.000ms partitionB 2 | 30% | 100% | 72.94% | 0ms | 0.000ms --------------------+------------------------+------------------- Total | 100% | |100.00% |The System partition's unused time is divided between the other two partitions, according to their shares of the CPU time.
- Start another instance of the greedy application in the System partition:
on -Xaps=System ./greedy &There's now no free time left in the system, so each partition gets only its minimum guaranteed CPU time. The instance of looper in partitionA should be noticeably slower than the one in partitionB. The output of the aps utility looks something like this:
+-------- CPU Time ------+-- Critical Time -- Partition name id | Budget | Max | Used | Budget | Used --------------------+------------------------+------------------- System 0 | 60% | 100% | 55.67% | 100ms | 0.000ms partitionA 1 | 10% | 100% | 11.84% | 0ms | 0.000ms partitionB 2 | 30% | 100% | 32.49% | 0ms | 0.000ms --------------------+------------------------+------------------- Total | 100% | |100.00% | - If you slay the instances of greedy, the instances of looper speed up, and the consumption of CPU time drops.
If you wish, you can continue to experiment, creating other partitions, modifying their budgets, and so on. Because you created the partitions at runtime instead of in your OS image, the new partitions will disappear when you restart the system.
