Apply different scheduling policies on created threads
- Assignment 2: Implement a program to apply different scheduling policies on created threads
- Linux Scheduling Policy
SCHED_FIFO
- Requirements
- Main thread
- Worker Thread
- Linux Scheduling Policy
Implement a program to apply different scheduling policies on created threads
Q1: Describe how you implemented the program in detail.
Step 0: Create new program
1 | vim sched_demo_312551002.c |
Step 1: Define and include related library
1 |
I enabled the GNU extensions because I’ll using functions like CPU_ZERO
, CPU_SET
, and pthread_setaffinity_np
, if not enabled the GNU extensions, when compile will occur errors and warnings.
Step 2: Define a struct for collecting required thread information
1 | typedef struct { |
Step 3: Implement the Parse program arguments
1 | testcases: |
1 | void parse_args(int argc, char *argv[], int *num_threads, double *time_wait, |
- Command-line arguments are parsed using the
getopt
function. The arguments include the number of threads-n
, time to wait-t
, scheduling policies-s
, and priorities-p
. The values are stored in appropriate variables for later use. - Special attention is given to the
-s
and-p
arguments, which can have multiple values separated by","
. These values are split and stored in arrays for individual thread configurations.
Step 4: Set CPU affinity and thread attribute
1 | void set_thread_attributes(pthread_attr_t *attr, int sched_policy, int sched_priority) { |
CPU_ZERO
andCPU_SET
are used to bind threads to a specific CPU (CPU 0 in this case), ensuring that they run on the designated processor.- If the scheduling policy is
SCHED_FIFO
, additional attributes such as scheduling policy and priority are set.SCHED_FIFO
is a real-time scheduling policy where a thread runs until it either finishes or is preempted by a higher priority thread. If scheduling policy isNORMAL
will skip this part.
Step 5: Implement the Worker Thread
1 | void *thread_func(void *arg) { |
- The worker thread’s function is defined here. Each thread waits at the barrier until all threads are ready to start simultaneously. This ensures synchronized start across all threads.
- Inside the loop, each thread performs its task for a specified duration
time_wait
. This is achieved using a busy-wait loop, calculating elapsed time withclock_gettime
.
Step 6: Implement the Main Thread
1 | int main(int argc, char *argv[]) { |
- The main function initializes the threading environment and starts the worker threads.
- It begins by parsing command-line arguments to determine the number of threads, their wait times, scheduling policies, and priorities.
- A barrier is initialized to synchronize the start of all worker threads.
- Threads are created in a loop. Each thread is given a unique number, its wait time, a reference to the barrier, and its scheduling policy and priority.
- Thread attributes are set according to the specified scheduling policy and CPU affinity.
- Threads are then started with
pthread_create
. - Finally, the main thread waits for all worker threads to complete using
pthread_join
and performs necessary cleanup operations like destroying the barrier and freeing allocated memory.
Complete sched_demo_312551002.c program like below:
1 |
|
Compile implemented program
1 | gcc -o sched_demo_312551002 sched_demo_312551002.c |
Test the program
1 | sudo ./sched_test.sh ./sched_demo ./sched_demo_312551002 |
Screenshot of test result:

Q2: Describe the results of ./sched_demo -n 3 -t 1.0 -s NORMAL,FIFO,FIFO -p -1,10,30 and what causes that.

- CFS and NORMAL Policy: Thread 0 and Thread 2, which are both scheduled under the NORMAL policy, are managed by the CFS. This ensures they receive fair CPU time slices. Despite their lower priority compared to the FIFO threads, CFS allocates them CPU time in a manner that prevents starvation and ensures fair allocation. This is why these threads are scheduled intermittently even amidst the higher priority FIFO threads.
- FIFO Policy and Preemption: Threads 1 and 3, scheduled under the FIFO policy, are expected to run based on their priority levels. Thread 3, with the highest priority (30), should theoretically preempt other threads and run to completion first. However, the interleaved scheduling of Thread 0 and Thread 2 indicates that CFS intervention allows these NORMAL policy threads to receive CPU time, thus not strictly adhering to FIFO priorities.
Q3: Describe the results of ./sched_demo -n 4 -t 0.5 -s NORMAL,FIFO,NORMAL,FIFO -p -1,10,-1,30, and what causes that.

- CFS and NORMAL Policy: Although Thread 0 and Thread 2 have a NORMAL scheduling policy, the CFS ensures they both receive a fair portion of CPU time. CFS is designed to equitably distribute CPU time slices and does not allow real-time processes to completely overshadow regular ones. This design accounts for why Threads 0 and 2 are scheduled intermittently, even though there are FIFO threads with higher priorities running.
- FIFO Policy and Preemption: For FIFO threads such as Thread 1 and Thread 3, the scheduling is based on their priority levels. Thread 3, with the highest priority (30), should, in a perfect FIFO system, run to completion before Thread 1, which has a lower priority (10), begins. However, the interleaving of Thread 0 and Thread 2 suggests that CFS is providing time slices to NORMAL policy threads, thereby not strictly adhering to FIFO priority rules.
Q4: Describe how did you implement n-second-busy-waiting?
1 | void *thread_func(void *arg) { |
- Time Measurement: The
clock_gettime
function withCLOCK_MONOTONIC
is used to retrieve the current time at the start of the busy-waiting loop and then repeatedly during the loop to measure the elapsed time. - Busy-Wait Loop: Each thread enters a while loop that continually checks whether the elapsed time since the start of the loop has exceeded the specified waiting time (time_wait).
- Elapsed Time Calculation: Inside the loop, clock_gettime is called again to get the current time, and the elapsed time is calculated by subtracting the starting time (start) from the current time (current). The result includes both the seconds (tv_sec) and nanoseconds (tv_nsec) components.