Книга: Programming with POSIX® Threads

5.5.2 Scheduling policies and priorities

5.5.2 Scheduling policies and priorities

int sched_get_priority_max (int policy);

int sched_get_priority_min (int policy);

int pthread_attr_getinheritsched (

const pthread_attr_t *attr, int *inheritsched);

int pthread_attr_setinheritsched (

pthread_attr_t *attr, int inheritsched);

int pthread_attr_getschedparam (

constpthread_attr_t *attr,

struct sched_param *param);

int pthread_attr_setschedparam (

pthread_attr_t *attr,

const struct sched_param *param);

int pthread_attr_getschedpolicy (

const pthread_attr_t *attr, int *policy);

int pthread_attr_setschedpolicy (

pthread_attr_t *attr, int policy);

int pthread_getschedparam (pthread_t thread,

int *policy, struct sched_param *param);

int pthread_setschedparam (

pthread_t thread, int policy,

const struct sched_param *param);

A Pthreads system that supports _POSIX_THREAD_PRIORITY_SCHEDULING must provide a definition of the struct sched_param structure that includes at least the member sched_priority. The sched_priority member is the only scheduling parameter used by the standard Pthreads scheduling policies, SCHED_FIFO and SCHED_RR. The minimum and maximum priority values (sched_priority member) that are allowed for each scheduling policy can be determined by calling sched_get_priority_min or sched_get_priority_max, respectively, for the scheduling policy. Pthreads systems that support additional, nonstandard scheduling policies may include additional members.

The SCHED_FIFO (first in, first out) policy allows a thread to run until another thread with a higher priority becomes ready, or until it blocks voluntarily. When a thread with SCHED_FIFO scheduling policy becomes ready, it begins executing immediately unless a thread with equal or higher priority is already executing.

The SCHED_RR (round-robin) policy is much the same, except that if a thread with SCHED_RR policy executes for more than a fixed period of time (the timeslice interval) without blocking, and another thread with SCHED_RR or SCHED_FIFO policy and the same priority is ready, the running thread will be preempted so the ready thread can be executed.

When threads with SCHED_FIFO or SCHED_RR policy wait on a condition variable or wait to lock a mutex, they will be awakened in priority order. That is, if a low-priority SCHED_FIFO thread and a high-priority SCHED_FIFO thread are both waiting to lock the same mutex, the high-priority thread will always be unblocked first when the mutex is unlocked.

Pthreads defines the name of an additional scheduling policy, called SCHED_OTHER. Pthreads, however, says nothing at all regarding what this scheduling policy does. This is an illustration of an unofficial POSIX philosophy that has been termed "a standard way to be nonstandard" (or, alternately, "a portable way to be nonportable"). That is, when you use any implementation of Pthreads that supports the priority scheduling option, you can write a portable program that creates threads running in SCHED_OTHER policy, but the behavior of that program is nonportable. (The official explanation of SCHED_OTHER is that it provides a portable way for a program to declare that it does not need a realtime scheduling policy.)

The SCHED_OTHER policy may be an alias for SCHED_FIFO, or it may be SCHED_RR, or it may be something entirely different. The real problem with this ambiguity is not that you don't know what SCHED_OTHER does, but that you have no way of knowing what scheduling parameters it might require. Because the meaning of SCHED_OTHER is undefined, it does not necessarily use the sched_priority member of the struct sched_param structure, and it may require additional, nonstandard members that an implementation may add to the structure. If there's any point to this, it is simply that SCHED_OTHER is not portable. If you write any code that uses SCHED_OTHER you should be aware that the code is not portable — you are, by definition, depending on the SCHED_OTHER of the particular Pthreads implementation for which you wrote the code.

The schedpolicy and schedparam attributes, set respectively by pthread_attr_setschedpolicy and pthread_attr_setschedparam, specify the explicit scheduling policy and parameters for the attributes object. Pthreads does not specify a default value for either of these attributes, which means that each implementation may choose some "appropriate" value. A realtime operating system intended for embedded controller applications, for example, might choose to create threads by default with SCHED_FIFO policy, and, perhaps, some medium-range priority.

Most multiuser operating systems are more likely to use a nonstandard "time-share" scheduling policy by default, causing threads to be scheduled more or less as processes have always been scheduled. The system may, for example, temporarily reduce the priority of "CPU hogs" so that they cannot prevent other threads from making progress.

One example of a multiuser operating system is Digital UNIX, which supports two nonstandard timeshare scheduling policies. The foreground policy (SCHED_ FG_NP), which is the default, is used for normal interactive activity, and corresponds to the way nonthreaded processes are scheduled. The background policy (SCHED_BG_NP) can be used for less important support activities.

When you set the scheduling policy or priority attributes in an attributes object,you must also set the inheritsched attribute!

The inheritsched attribute, which you can set by calling pthread_attr_setinheritsched, controls whether a thread you create inherits scheduling information from the creating thread, or uses the explicit scheduling information in the schedpolicy and schedparam attributes. Pthreads does not specify a default value for inheritsched, either, so if you care about the policy and scheduling parameters of your thread, you must always set this attribute.

Set the inheritsched attribute to PTHREAD_INHERIT_SCHED to cause a new thread to inherit the scheduling policy and parameters of the creating thread. Scheduling inheritance is useful when you're creating "helper" threads that are working on behalf of the creator — it generally makes sense for them to run at the same policy and priority. Whenever you need to control the scheduling policy or parameters of a thread you create, you must set the inheritsched attribute to PTHREAD_EXPLICIT_SCHED.

58-118 The following program, sched_attr.c, shows how to use an attributes object to create a thread with an explicit scheduling policy and priority. Notice that it uses conditional code to determine whether the priority scheduling feature of Pthreads is supported at compilation time. It will print a message if the option is not supported and continue, although the program in that case will not do much. (It creates a thread with default scheduling behavior, which can only say that it ran.)

Although Solaris 2.5 defines _POSIX_THREAD_PRIORITY_SCHEDULING, it does not support the POSIX realtime scheduling policies, and attempting to set the policy attribute to SCHED_RR would fail. This program treats Solaris as it did not define the _POSIX_THREAD_PRIORITY_SCHEDULING option.

? sched_attr.c

#include <unistd.h>
2 #include <pthread.h>
3 #include <sched.h>
4 #include "errors.h" 5
6 /*
7 * Thread start routine. If priority scheduling is supported,
8 * report the thread's scheduling attributes.
9 */
10 void *thread_routine (void *arg)
11 {
12  int my_policy;
13  struct sched_param my_param;
14  int status;

15
16 /*
17 * If the priority scheduling option is not defined, then we
18 * can do nothing with the output of pthread_getschedparam,
19 * so just report that the thread ran, and exit.
20 */
21 #if defined (_POSIX_THREAD_PRIORITY_SCHEDULING) && !defined (sun)
22  status = pthread_getschedparam (
23  pthread_self (), &my_policy, &my_param);
24  if (status != 0)
25  err_abort (status, "Get sched");
26  printf ("thread_routine running at %s/%dn",
27  (my_policy == SCHED_FIFO ? "FIFO"
28  : (my_policy == SCHED_RR ? "RR"
29  : (my_policy == SCHED_OTHER ? "OTHER"
30  : "unknown"))),
31  my_param.sched_priority);
32 #else
33  printf ("thread_routine runningn");
34 #endif
35  return NULL;
36 }
37
38 int main (int argc, char *argv[])
39 {
40  pthread_t thread_id;
41  pthread_attr_t thread_attr;
42  int thread_policy;
43  struct sched_param thread_param;
44  int status, rr_min_priority, rr_max_priority;
45
46  status = pthread_attr_init (&thread_attr);
47  if (status != 0)
4 8  err_abort (status, "Init attr");
49
50 /*
51 * If the priority scheduling option is defined, set various
52 * scheduling parameters. Note that it is particularly important
53 * that you remember to set the inheritsched attribute to
54 * PTHREAD_EXPLICIT_SCHED, or the policy and priority that you've
55 * set will be ignored! The default behavior is to inherit
56 * scheduling information from the creating thread.
57 */
58 #if defined (_POSIX_THREAD_PRIORITY_SCHEDULING) && !defined (sun)
59  status = pthread_attr_getschedpolicy (
60  &thread_attr, &thread_policy);
61  if (status 1= 0)
62  err_abort (status, "Get policy");
63  status = pthread_attr_getschedparam (
64  &thread_attr, &thread_param);
65  if (status != 0)
66  err_abort (status, "Get sched param");
67  printf (
68  "Default policy is %s, priority is %dn",
69  (thread_policy == SCHED_FIFO ? "FIFO"
70  : (thread_policy == SCHED_RR ? "RR"
71  : (thread_policy == SCHED_OTHER ? "OTHER" 72 : "unknown"))),
73  thread_param.sched_priority);
74
75  status = pthread_attr_setschedpolicy (
76  &thread_attr, SCHED_RR);
77  if (status != 0)
78  printf ("Unable to set SCHED_RR policy.n");
79  else {
80 /*
81 * Just for the sake of the exercise, we'll use the
82 * middle of the priority range allowed for
83 * SCHED_RR. This should ensure that the thread will be
84 * run, without blocking everything else. Because any
85 * assumptions about how a thread's priority interacts
86 * with other threads (even in other processes) are
87 * nonportable, especially on an implementation that
88 * defaults to System contention scope, you may have to
89 * adjust this code before it will work on some systems.
90 */
91  rr_min_priority = sched_get_priority_min (SCHED_RR);
92  if (rr_min_priority == -1)
93  errno_abort ("Get SCHED_RR min priority");
94  rr_max_priority = sched_get_priority_max (SCHED_RR);
95  if (rr_max_priority == -1)
96  errno_abort ("Get SCHED_RR max priority");
97  thread_param.sched_priority =
98  (rr_min_priority + rr_max_priority)/2;
99  printf (
100  "SCHED_RR priority range is %d to %d: using %dn",
101  rr_min_priority,
102  rr_max_priority,
103  thread_param.sched_priority);
104  status = pthread_attr_setschedparam (
105  &thread_attr, &thread_param);
106  if (status != 0)
107  err_abort (status, "Set params");
108  printf (
109  "Creating thread at RR/%dn",
110  thread_param.sched_priority);
111  status = pthread_attr_setinheritsched (
112  &thread_attr, PTHREAD_EXPLICIT_SCHED);
113  if (status != 0)
114  err_abort (status, "Set inherit");
115 }
116 #else
117  printf ("Priority scheduling not supportedn");
118 #endif
119  status = pthread_create (
120  &thread_id, &thread_attr, thread_routine, NULL);
121  if (status != 0)
122  err_abort (status, "Create thread");
123  status = pthread_join (thread_id, NULL);
124  if (status != 0)
125  err_abort (status, "Join thread");
126  printf ("Main exitingn");
127 return 0;
128 }

The next program, sched_thread.c, shows how to modify the realtime scheduling policy and parameters for a running thread. When changing the scheduling policy and parameters in a thread attributes object, remember, you use two separate operations: one to modify the scheduling policy and the other to modify the scheduling parameters.

You cannot modify the scheduling policy of a running thread separately from the thread's parameters, because the policy and parameters must always be consistent for scheduling to operate correctly. Each scheduling policy may have a unique range of valid scheduling priorities, and a thread cannot operate at a priority that isn't valid for its current policy. To ensure consistency of the policy and parameters, they are set with a single call.

55 Unlike sched_attr.c, sched_thread.c does not check the compile-time feature macro _POSIX_THREAD_PRIORITY_SCHEDULING. That means it will probably not compile, and almost certainly won't run correctly, on a system that does not support the option. There's nothing wrong with writing a program that way — in fact, that's what you are likely to do most of the time. If you need priority scheduling, you would document that your application requires the _POSIX_THREAD_ PRIORITY_SCHEDULING option, and use it.

57-62 Solaris 2.5, despite defining _POSIX_THREAD_PRIORITY_SCHEDULING, does not support realtime scheduling policies. For this reason, the ENOSYS from sched_get_priority_min is handled as a special case.

sched_thread.c

1 #include <unistd.h>
2 #include <pthread.h>
3 #include <sched.h>
4 #include "errors.h" 5
6 #define THREADS 5 7
8 /*
9 * Structure describing each thread.

10 */
11 typedef struct thread_tag {
12  int index;
13  pthread_t id;
14 } thread_t;
15
16 thread_t threads[THREADS];
17 int rr_min_priority; 18
19 /*
20 * Thread start routine that willset its own priority.
21 */
22 void *thread_routine (void *arg)
23 {
24  thread_t *self = (thread_t*)arg;
25  int my_policy;
26  struct sched_param my_param;
27  int status;

28
29  my_param.sched_priority = rr_min_priority + self->index;
30  DPRINTF ((
31  "Thread %d will set SCHED_FIFO, priority %dn",
32  self->index, my_param.sched_priority));
33  status = pthread_setschedparam (
34  self->id, SCHED_RR, &my_param);
35  if (status != 0) 
36  err_abort (status, "Set sched");
37  status = pthread_getschedparam (
38  self->id, &my_policy, &my_param);
39  if (status != 0)
40  err_abort (status, "Get sched");
41  printf ("thread_routine %d running at %s/%dn",
42  self->index,
43  (my_policy == SCHED_FIFO ? "FIFO"
44  : (my_policy == SCHED_RR ? "RR"
45  : (my_policy == SCHED_OTHER ? "OTHER" 46 : "unknown"))),
47  my_param.sched_priority);
48  return NULL;
49 }

50
51 int main (int argc, char *argv[])
52 {
53  int count, status;

54
55  rr_min_priority = sched_get_priority_min (SCHED_RR);
56  if (rr_min_priority == -1) {
57 #ifdef sun
58  if (errno == ENOSYS) {
59  fprintf (stderr, "SCHED_RR is not supported.n");
60  exit (0);
61  }
62 #endif
63  errno_abort ("Get SCHED_RR min priority");
64  }
65  for (count = 0; count < THREADS; count++) {
66  threads[count].index = count;
67  status = pthread_create (
68  &threads[count].id, NULL,
69  thread_routine, (void*)&threads[count]);
70  if (status != 0)
71  err_abort (status, "Create thread");
72  }
73  for (count = 0; count < THREADS; count++) {
74  status = pthread_join (threads[count].id, NULL);
75  if (status != 0)
76  err_abort (status, "Join thread");
77  }
78  printf ("Main exitingn");
79  return 0;
80 }

Оглавление книги


Генерация: 1.087. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз