The ChorusOS operating system provides two alternative ways of scheduling threads. These two features are mutually exclusive:
either the ChorusOS operating system is configured with the default scheduler,
or it is configured with the ROUND_ROBIN
feature.
The default FIFO scheduler defines a pure priority-based, preemptive, FIFO (first-in first-out) policy. Priority of threads may vary from K_FIFO_PRIOMAX (0 and highest priority) to K_FIFO_PRIOMIN (255 and lowest priority). Within this policy, a thread which becomes ready to run after being blocked is always inserted at the end of its priority ready queue. A running thread is preempted only if a thread of a strictly higher priority becomes ready to run. A preempted thread is placed at the head of its priority queue, so that it will be selected when no other ready thread has a greater priority.
The ROUND_ROBIN
feature is a general framework
supporting simultaneous multiple scheduling policies or classes. The main
classes dealt with here are the CLASS_FIFO
and the CLASS_RR
policies.
The CLASS_FIFO
reproduces the behavior of the
default scheduler policy precisely.
The CLASS_RR
implements a priority-based preemptive
policy with round-robin time slicing. Priority of threads may vary from K_RR_PRIOMAX to K_RR_PRIOMIN. It is similar to
the default scheduler policy, except that an elected thread is given a fixed
time quantum. If the thread is still running at quantum expiration, it is
de-scheduled and placed at the end of its priority queue, thus yielding the
processor to other threads of equal priority (if any).
It is possible to set scheduling attributes of threads at thread creation time (using the void* schedParams parameter of threadCreate()). It is also possible to get and modify scheduling attributes of a thread dynamically through the following call.
#include <chorus.h> #include <sched/chFifo.h> #include <sched/chRr.h> #include <sched/chRt.h> #include <sched/chTs.h> int threadScheduler(KnCap* actorCap, KnThreadLid thLi, void* oldParam, void* newParam);
This service enables you to get or set scheduling parameters of any thread of any actor, as long as both the actor capability and the thread identifier are known. threadScheduler() returns the current scheduling attributes of the target thread at the location defined by oldParam, if non-null. It will also set the attributes of the target thread according to the description provided at the location defined by newParam if non-null.
As the size, layout and semantics of scheduling parameters may vary
depending on the scheduler configured in the system, or on the class of the ROUND_ROBIN
framework, parameters are untyped in the generic
interface definition. However, all scheduling parameter descriptions are similar,
at least for the initial fields:
struct KnFifoThParms { KnSchedClass fifoClass; /* Always set to K_SCHED_FIFO */ KnFifoPriority fifoPriority; } KnFifoThParms; struct KnRrThParms { KnSchedClass rrClass; /* Always set to K_SCHED_RR */ KnRrPriority rrPriority; } KnRrThParms;
The first field defines the scheduling policy applied or to be applied to the thread. The second field defines the priority of the thread within the scheduling policy.
Example 6-5 is based on the semaphore example, with a modification to the childCreate() routine so that it can receive scheduling attributes of the thread to be created. The main thread invokes this modified routine, so that the created thread will start as soon as it is created, rather than waiting for the main thread to yield the processor. Thus, the created thread must be given a higher priority than the main thread.
Refer to the threadScheduler(2K) and threadCreate(2K) man pages.
(file: progov/thSched.c) #include <stdio.h> #include <stdlib.h> #include <chorus.h> #define USER_STACK_SIZE (1024 * sizeof(long)) KnSem sampleSem; int childSchedCreate(KnPc entry, void* schedParams) { KnActorPrivilege actorP; KnDefaultStartInfo_f startInfo; char* userStack; int childLid = -1; int res; /* Set defaults startInfo fields */ startInfo.dsType = K_DEFAULT_START_INFO; startInfo.dsSystemStackSize = K_DEFAULT_STACK_SIZE; /* Get actor's privilege */ res = actorPrivilege(K_MYACTOR, &actorP, NULL); if (res != K_OK) { printf("Cannot get the privilege of the actor, error %d\n", res); exit(1); } /* Set thread privilege */ if (actorP == K_SUPACTOR) { startInfo.dsPrivilege = K_SUPTHREAD; } else { startInfo.dsPrivilege = K_USERTHREAD; } /* Allocate a stack for user threads */ if (actorP != K_SUPACTOR) { userStack = malloc(USER_STACK_SIZE); if (userStack == NULL) { printf("Cannot allocate user stack\n"); exit(1); } startInfo.dsUserStackPointer = userStack + USER_STACK_SIZE; } /* Set entry point for the new thread */ startInfo.dsEntry = entry; /* Create the thread in the active state */ res = threadCreate(K_MYACTOR, &childLid, K_ACTIVE, schedParams, &startInfo); if (res != K_OK) { printf("Cannot create the thread, error %d\n", res); exit(1); } return childLid; } void sampleThread() { int myThreadLi; int res; myThreadLi = threadSelf(); printf("I am the new thread. My thread identifier is: %d\n", myThreadLi); res = semV(&sampleSem); if (res != K_OK){ printf("Cannot perform the semV operation, error %d\n", res); exit(1); } threadDelete(K_MYACTOR, K_MYSELF); } int main(int argc, char** argv, char**envp) { int myThreadLi; int newThreadLi; int res; KnThreadDefaultSched schedParams; res = semInit(&sampleSem, 0); if (res != K_OK) { printf("Cannot initialize the semaphore, error %d\n", res); exit(1); } /* acquire my own scheduling attributes */ res = threadScheduler(K_MYACTOR, K_MYSELF, &schedParams, NULL); /* Increase priority of thread to be created */ schedParams.tdPriority -= 1; newThreadLi = childSchedCreate((KnPc)sampleThread, &schedParams); myThreadLi = threadSelf(); printf("Parent thread identifier = %d, Child thread identifier = %d\n", myThreadLi, newThreadLi); res = semP(&sampleSem, K_NOTIMEOUT); if (res != K_OK) { printf("Cannot perform the semP operation, error %d\n", res); exit(1); } return 0; }
First, the main thread needs to get its own scheduling attributes. As these are not known, a KnThreadDefaultSched structure is used as the output argument of the call to threadScheduler(). The last argument of threadScheduler() is set to null as the current scheduling attributes of the main thread wish to be preserved.
In order to give a higher priority to the created thread, decrease the numerical value of the priority. Increasing the priority value has the reverse effect.