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.
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:
cp my_buildfile.build apsdma.build
PATH=/proc/boot:/bin:/usr/bin:/opt/bin \ LD_LIBRARY_PATH=/proc/boot:/lib:/usr/lib:/lib/dll:/opt/lib \ procnto-smp-instr
[module=aps] PATH=/proc/boot:/bin:/usr/bin:/opt/bin \ LD_LIBRARY_PATH=/proc/boot:/lib:/usr/lib:/lib/dll:/opt/lib \ procnto-smp-instr
You 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.
mkifs apsdma.build apsdma.ifs
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.
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:
by using the aps utility:
aps create -b10 partitionA aps create -b30 partitionB
The new partitions' budgets are subtracted from their parent partition's budget (the System partition in this case).
$ 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% |
on -Xaps=partitionA ./looper A 5
on -Xaps=partitionB ./looper B 5
If you switch between consoles, you'll see that the programs are running at approximately the same speed.
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
on -Xaps=partitionB ./greedy &
+-------- 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.
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.
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 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.