Question
Hi was wondering if you can help me to do this project. Lab06 Manual In the last project, we built a OS process manager by
Hi was wondering if you can help me to do this project.
Lab06 Manual
In the last project, we built a OS process manager by using semaphore. The PM can only assign a certain number of PID to processes (workers) at one time.
In this project, we are to simulate a time-sharing system by using signals and timers. The data structure and worker thread have been well implemented with semaphore. You will focus on implementing the scheduling algorithm (Round Robin). Since we can not change actual "time slices" in OS we use interval timers to model it. The scheduler is installed with an interval timer. The timer starts ticking when the scheduler picks a thread to use the CPU which in turn signals the thread when its time slice is finished thus allowing the scheduler to pick another thread and so on. When a thread has completely finished its work it leaves the scheduler to allow a waiting thread to enter. Please note that in this project, only the timer and scheduler send signals. The threads passively handle the signals without signaling back to the scheduler.
The goal of this project is to help you understand (1) how signals and timers work, and (2) how to evaluate the performance of your program. (3) understand how the POSIX:TMR interval timer works. You will first implement the time-sharing system using timers and signals. Then, you will evaluate the overall performance of your program by keeping track of how long each thread is idle, running, etc.
The program takes a number of arguments. Arg1 determines the number of jobs (threads in our implementation) created; arg2 specifies the queue size of the scheduler. Arg3 through argN gives the duration (the required time slices to complete a job) of each job. Hence if we create 2 jobs, we should supply arg3 and arg4 for the required duration. You can assume that the autograder will always supply the correct number of arguments and hence you do not have to detect invalid input.
The program will use these four signals:
SIGALRM: sent by the timer to the scheduler, to indicate another time quantum has passed.
SIGUSR1: sent by the scheduler to a worker, to tell it to suspend.
SIGUSR2: sent by the scheduler to a suspended worker, to tell it to resume.
SIGTERM: sent by the scheduler to a worker, to tell it to cancel.
All code that does this will be in scheduler.c.
Part I: Modify the scheduler code
-----------------------------------------------
The start code uses the scheduler thread to setup the timer and handle the scheduling for the system. The scheduler handles the SIGALRM events that come from the timer, and sends out signals to the worker threads
The starting code has initialized the scheduler with a POSIX:TMR interval timer in init_sched_queue() function in scheduler.c. CLOCK_REALTIME is used in timer_create(). The timer will be stored in the global variable "timer", which will be started in scheduler_run().
sigaction() is used in setup_sig_handlers() to install signal handlers for SIGALRM, SIGUSR1, and SIGTERM. SIGALRM triggers timer_handler(), SIGUSR1 triggers suspend_thread(), and SIGTERM triggers cancel_thread(). Notice no handler is installed for SIGUSR2; this signal will be handled differently, in worker.c which handles the signals from the scheduler.
start_worker() in worker.c immediately suspends the thread, waiting for a resume signal from the scheduler ((1) block SIGUSR2 and SIGALRM, and (2) unblock SIGUSR1 and SIGTERM). sigwait() is used in suspend_thread() to force the thread to suspend itself based on the SIGUSR1 signal and wait for a resume signal (SIGUSR2).
Step 1.
In the scheduler_run() function, start the timer. Use timer_settime(). The time quantum (1 second) is given in scheduler.h. The timer should go off repeatedly at regular intervals defined by the timer quantum.
In Round-Robin, whenever the timer goes off, the scheduler suspends the currently running thread, and tells the next thread to resume its operations using signals. These steps are listed in timer_handler(), which is called every time the timer goes off. In this implementation, the timer handler makes use of suspend_worker() and resume_worker() to accomplush these steps.
Step 2.
Complete the suspend_worker() function. First, update the info->quanta value. This is the number of quanta that remain for this thread to execute. It is initialized to the value passed on the command line, and decreases as the thread executes. If there is any more work for this worker to do, send it a signal to suspend, and update the scheduler queue. Otherwise, cancel the thread.
Step 3.
Complete the cancel_worker() function by sending the appropriate signal to the thread, telling it to kill itself.
Step 4.
Complete the resume_worker() function by sending the appropriate signal to the thread, telling it to resume execution.
Part II: Modify the evaluation code
This program keeps track of run time, and wait time. Each thread saves these two values regarding its own execution in its thread_info_t. Tracking these values requires also knowing the last time the thread suspended or resumed. Therefore, these two values are also kept in thread_info_t. See scheduler.h.
You will implement the functions that calculate run time and wait time. When the program is done, it will collect all these values, and print out the total and average wait time and run time. For your convenience, you are given a function time_difference() to compute the difference between two times in microseconds.
Step 5.
Modify create_workers() to initialize the various time variables.
Step 6.
Implement update_run_time(). This is called by suspend_worker().
Step 7.
Implement update_wait_time(). This is called by resume_worker().
Step 8.
Test your code with 4 worker threads with queue size 2. You can set quanta yourself. Your result should show the statistics at the last lines.
Here is the code. Thanks again First off is list.c
#include
#include "list.h"
/* list helper functions */
int list_size(thread_info_list *list)
{
int cnt = 0;
if (!list) return -1;
pthread_mutex_lock(&list->lock);
list_elem *le = list->head;
while (le) {
cnt++;
le = le->next;
}
pthread_mutex_unlock(&list->lock);
return cnt;
}
int list_insert_head(thread_info_list *list, list_elem *new)
{
if (!list || !new) return -1;
pthread_mutex_lock(&list->lock);
new->next = list->head;
new->prev = 0;
if (new->next) {
new->next->prev = new;
}
list->head = new;
if (list->tail == 0) {
list->tail = new;
}
pthread_mutex_unlock(&list->lock);
return 0;
}
int list_insert_tail(thread_info_list *list, list_elem *new)
{
if (!list || !new) return -1;
pthread_mutex_lock(&list->lock);
new->prev = list->tail;
new->next = 0;
if (new->prev) {
new->prev->next = new;
}
list->tail = new;
if (list->head == 0) {
list->head = new;
}
pthread_mutex_unlock(&list->lock);
return 0;
}
int list_remove(thread_info_list *list, list_elem *old)
{
if (!old || !list) return -1;
pthread_mutex_lock(&list->lock);
if (old->next) {
old->next->prev = old->prev;
}
if (old->prev) {
old->prev->next = old->next;
}
if (list->tail == old) {
list->tail = old->prev;
}
if (list->head == old) {
list->head = old->next;
}
old->next = old->prev = 0;
pthread_mutex_unlock(&list->lock);
return 0;
}
void print_list(thread_info_list *list)
{
pthread_mutex_lock(&list->lock);
list_elem *le = list->head;
while (le) {
printf("0x%X,", (unsigned int)le->info);
le = le->next;
}
pthread_mutex_unlock(&list->lock);
printf(" ");
}
Next is list.h
#ifndef __LIST_H_
#define __LIST_H_
#include
typedef struct list_elem {
struct list_elem *prev;
struct list_elem *next;
void *info;
} list_elem;
typedef struct thread_info_list {
list_elem *head;
list_elem *tail;
pthread_mutex_t lock;
} thread_info_list;
int list_size(thread_info_list *list);
int list_insert_head(thread_info_list *list, list_elem *new);
int list_insert_tail(thread_info_list *list, list_elem *new);
int list_remove(thread_info_list *list, list_elem *new);
void print_list(thread_info_list *list);
#endif /* __LIST_H_ */
Next file is makefile
CC = gcc
CCOPTS = -c -g -Wall
LINKOPTS = -g -lpthread -lrt -Wall
EXEC=scheduler
OBJECTS=scheduler.o worker.o list.o
all: $(EXEC)
$(EXEC): $(OBJECTS)
$(CC) $(LINKOPTS) -o $@ $^
%.o:%.c
$(CC) $(CCOPTS) -o $@ $^
clean:
- $(RM) $(EXEC)
- $(RM) $(OBJECTS)
- $(RM) *~
- $(RM) core.*
test: scheduler
- ./scheduler -test -f0 rr
- killall -q -KILL scheduler; true
pretty:
indent *.c *.h -kr
Next file is scheduler.c
#include
#include
#include
#include
#include
#include
#include
#include "scheduler.h"
#include "worker.h"
/*
* define the extern global variables here.
*/
sem_t queue_sem; /* semaphore for scheduler queue */
thread_info_list sched_queue; /* list of current workers */
static int quit = 0;
static timer_t timer;
static thread_info_t *currentThread= 0;
static long wait_times;
static long run_times;
static int completed = 0;
static int thread_count = 0;
static void exit_error(int); /* helper function. */
static void wait_for_queue();
/*******************************************************************************
*
* Implement these functions.
*
******************************************************************************/
/*
* This function intializes the queue semaphore and the queue itself.
*/
/*
* Update the worker's current running time.
* This function is called every time the thread is suspended.
*/
void update_run_time(thread_info_t *info) {
/* TODO: implement this function */
}
/*
* Update the worker's current waiting time.
* This function is called every time the thread resumes.
*/
void update_wait_time(thread_info_t *info) {
/* TODO: implement this function */
}
static void init_sched_queue(int queue_size)
{
/* set up a semaphore to restrict access to the queue */
sem_init(&queue_sem, 0, queue_size);
/* initialize the scheduler queue */
sched_queue.head = sched_queue.tail = 0;
pthread_mutex_init(&sched_queue.lock, NULL);
/* initialize the timer */
if(timer_create(CLOCK_REALTIME,NULL,&timer)==-1) {
perror("Can't create timer.");
exit(-1);
}
}
/*
* signal a worker thread that it can resume.
*/
static void resume_worker(thread_info_t *info)
{
printf("Scheduler: resuming %lu. ", info->thrid);
/*
* TODO: signal the worker thread that it can resume
*/
/* update the wait time for the thread */
update_wait_time(info);
}
/*send a signal to the thread, telling it to kill itself*/
void cancel_worker(thread_info_t *info)
{
/* TODO: send a signal to the thread, telling it to kill itself*/
/* Update global wait and run time info */
wait_times += info->wait_time;
run_times += info->run_time;
completed++;
/* Update schedule queue */
leave_scheduler_queue(info);
if (completed >= thread_count) {
sched_yield(); /* Let other threads terminate. */
printf("The total wait time is %f seconds. ", (float)wait_times / 1000000);
printf("The total run time is %f seconds. ", (float)run_times / 1000000);
printf("The average wait time is %f seconds. ", (float)wait_times / 1000000 / thread_count);
printf("The average run time is %f seconds. ", (float)run_times / 1000000 / thread_count);
}
}
/*
* signals a worker thread that it should suspend.
*/
static void suspend_worker(thread_info_t *info)
{
int whatgoeshere = 0;
printf("Scheduler: suspending %lu. ", info->thrid);
/*update the run time for the thread*/
update_run_time(info);
/* TODO: Update quanta remaining. */
/* TODO: decide whether to cancel or suspend thread */
if(whatgoeshere) {
/*
* Thread still running: suspend.
* TODO: Signal the worker thread that it should suspend.
*/
/* Update Schedule queue */
list_remove(&sched_queue,info->le);
list_insert_tail(&sched_queue,info->le);
} else {
/* Thread done: cancel */
cancel_worker(info);
}
}
/*
* this is the scheduling algorithm
* pick the next worker thread from the available list
* you may need to add new information to the thread_info struct
*/
static thread_info_t *next_worker()
{
if (completed >= thread_count)
return 0;
wait_for_queue();
printf("Scheduler: scheduling. ");
/* return the thread_info_t for the next thread to run */
return sched_queue.head->info;
}
void timer_handler()
{
thread_info_t *info = 0;
/* once the last worker has been removed, we're done. */
if (list_size(&sched_queue) == 0) {
quit = 1;
return;
}
/*suspend the current worker*/
if (currentThread)
suspend_worker(currentThread);
//resume the next worker
info = next_worker();
/* Update currentThread */
currentThread = info;
if (info)
resume_worker(info);
else
quit = 1;
}
/*
* Set up the signal handlers for SIGALRM, SIGUSR1, and SIGTERM.
* TODO: Implement this function.
*/
void setup_sig_handlers() {
struct sigaction act;
act.sa_flags = 0;
/* Setup timer handler for SIGALRM signal in threads */
act.sa_handler = timer_handler;
act.sa_flags = 0;
if (sigemptyset(&act.sa_mask) || sigaction(SIGALRM, &act, NULL))
{
fprintf(stderr,"Failed to set up SIGALRM handler. ");
exit(-1);
}
/* Setup cancel handler for SIGTERM signal in threads */
act.sa_handler = cancel_thread;
if (sigaction(SIGTERM, &act, NULL))
{
fprintf(stderr,"Failed to set up SIGTERM handler. ");
exit(-1);
}
/* Setup suspend handler for SIGUSR1 signal in threads */
act.sa_handler = suspend_thread;
if (sigaction(SIGUSR1, &act, NULL))
{
fprintf(stderr,"Failed to set up SIGUSR1 handler. ");
exit(-1);
}
}
/*******************************************************************************
*
*
*
******************************************************************************/
/*
* waits until there are workers in the scheduling queue.
*/
static void wait_for_queue()
{
while(!list_size(&sched_queue)) {
printf("Scheduler: waiting for workers. ");
sched_yield();
}
}
/*
* runs at the end of the program just before exit.
*/
static void clean_up()
{
/*
* destroy any mutexes/condition variables/semaphores that were created.
* free any malloc'd memory not already free'd
*/
sem_destroy(&queue_sem);
pthread_mutex_destroy(&sched_queue.lock);
}
/*
* prints an error summary and exits.
*/
static void exit_error(int err_num)
{
fprintf(stderr, "failure: %s ", strerror(err_num));
exit(1);
}
/*
* creates the worker threads.
*/
static void create_workers(int thread_count, int *quanta)
{
int i = 0;
int err = 0;
for (i = 0; i
thread_info_t *info = (thread_info_t *) malloc(sizeof(thread_info_t));
info->quanta = quanta[i];
if ((err = pthread_create(&info->thrid, NULL, start_worker, (void *)info)) != 0) {
exit_error(err);
}
printf("Main: detaching worker thread %lu. ", info->thrid);
pthread_detach(info->thrid);
/* TODO: initialize the time variables for each thread for performance evalution*/
}
}
/*
* runs the scheduler.
*/
static void *scheduler_run(void *unused)
{
wait_for_queue();
/* TODO: start the timer */
timer_settime()
/*keep the scheduler thread alive*/
while( !quit )
sched_yield();
return NULL;
}
/*
* starts the scheduler.
* returns 0 on success or exits program on failure.
*/
static int start_scheduler(pthread_t *thrid)
{
int err = 0;
if ((err = pthread_create(thrid, NULL, scheduler_run, 0)) != 0) {
exit_error(err);
}
return err;
}
/*
* reads the command line arguments and starts the scheduler & worker threads.
*/
int main(int argc, const char **argv)
{
int queue_size = 0;
int ret_val = 0;
int *quanta,i;
pthread_t sched_thread;
/* check the arguments. */
if (argc
print_help(argv[0]);
exit(0);
}
thread_count = atoi(argv[1]);
queue_size = atoi(argv[2]);
quanta = (int*)malloc(sizeof(int)*thread_count);
if (argc != 3 + thread_count) {
print_help(argv[0]);
exit(0);
}
for ( i = 0; i
quanta[i] = atoi(argv[i+3]);
printf("Main: running %d workers with queue size %d for quanta: ", thread_count, queue_size);
for ( i = 0; i
printf(" %d", quanta[i]);
printf(" ");
/*setup the sig handlers for scheduler and workers*/
setup_sig_handlers();
/* initialize anything that needs to be done for the scheduler queue. */
init_sched_queue(queue_size);
/* creates a thread for the scheduler. */
start_scheduler(&sched_thread);
/* creates the worker threads and returns. */
create_workers(thread_count, quanta);
/* wait for scheduler to finish */
printf("Main: waiting for scheduler %lu. ", sched_thread);
pthread_join(sched_thread, (void **) &ret_val);
/* clean up our resources */
clean_up();
/* this will wait for all other threads */
pthread_exit(0);
}
long time_difference(const struct timespec *time1, const struct timespec *time2) {
return (time1->tv_sec - time2->tv_sec) * 1000000 + (time1->tv_nsec - time2->tv_nsec) / 1000;
}
Next file is scheduler.h
#ifndef __SCHEDULER_H_
#define __SCHEDULER_H_
#include
#include
#include "list.h"
#define QUANTUM 2
/* typedefs */
typedef struct thread_info {
pthread_t thrid;
int quanta;
list_elem* le;
/*added for evalution bookkeeping*/
struct timespec suspend_time;
struct timespec resume_time;
long wait_time;
long run_time;
} thread_info_t;
/* functions */
void *start_worker(void *);
long time_difference(const struct timespec*, const struct timespec*);
int smp5_main(int argc, const char** argv);
/* shared variables */
extern sem_t queue_sem; /* semaphore for scheduler queue */
extern thread_info_list sched_queue; /* list of current workers */
#endif /* __SCHEDULER_H_ */
next file is worker.c
#include
#include
#include
#include
#include
#include "scheduler.h"
/* Handler for SIGTERM signal */
void cancel_thread()
{
printf("Thread %u: terminating. ",(unsigned int) pthread_self());
/* signal that done in queue */
sem_post(&queue_sem);
pthread_exit(NULL);
}
/* Handle the SIGUSR1 signal */
void suspend_thread()
{
sigset_t mask;
int signal;
printf("Thread %u: suspending. ", (unsigned int)pthread_self());
/*wait for a resume signal from the scheduler*/
if(sigemptyset(&mask) || sigaddset(&mask,SIGUSR2)) {
perror("Can't create signal mask.");
pthread_exit(NULL);
}
sigwait(&mask, &signal);
printf("Thread %u: resuming. ",(unsigned int) pthread_self());
}
/*
* waits to gain access to the scheduler queue.
*/
static int enter_scheduler_queue(thread_info_t *info)
{
/*
* wait for available room in queue.
* create a new list entry for this thread
* store this thread info in the new entry.
*/
sem_wait(&queue_sem);
list_elem *item = (list_elem*)malloc(sizeof(list_elem));
info->le = item;
item->info = info;
item->prev = 0;
item->next = 0;
list_insert_tail(&sched_queue, item);
return 0;
}
/*
* leaves the scheduler queue
*/
void leave_scheduler_queue(thread_info_t *info)
{
printf("Thread %lu: leaving scheduler queue. ", info->thrid);
/*
* remove the given worker from queue
* clean up the memory that we malloc'd for the list
* clean up the memory that was passed to us
*/
list_remove(&sched_queue, info->le);
free(info->le);
free(info);
}
/*
* Initialize thread, enter scheduling queue, and execute instructions.
* arg is a pointer to thread_info_t
*/
void *start_worker(void *arg)
{
thread_info_t *info = (thread_info_t *) arg;
float calc = 0.8;
int j = 0;
sigset_t sigset;
/* TODO: Block SIGALRM and SIGUSR2. */
if(sigemptyset(&sigset) || sigaddset(&sigset,SIGALRM)
|| sigaddset(&sigset,SIGUSR2)) {
perror("Can't initialize blocking signal mask.");
}
if(pthread_sigmask(SIG_BLOCK,&sigset,NULL) == -1) {
perror("Can't block signals.");
}
/* Unblock SIGUSR1 and SIGTERM. */
if(sigemptyset(&sigset) || sigaddset(&sigset,SIGTERM)
|| sigaddset(&sigset,SIGUSR1)) {
perror("Can't initialize unblocking signal mask.");
}
if(pthread_sigmask(SIG_UNBLOCK,&sigset,NULL) == -1) {
perror("Can't unblock signals.");
}
/* compete with other threads to enter queue. */
if (enter_scheduler_queue(info)) {
printf("Thread %lu: failure entering scheduler queue - %s ", info->thrid, strerror(errno));
free (info);
pthread_exit(0);
}
printf("Thread %lu: in scheduler queue. ", info->thrid);
suspend_thread();
while (1) {
/* do some meaningless work... */
for (j = 0; j
calc = 4.0 * calc * (1.0 - calc);
}
}
}
next file is worker.h
#ifndef __WORKER_H_ #define __WORKER_H_
void cancel_thread(); void suspend_thread(); void leave_scheduler_queue(thread_info_t*);
#endif
Thanks again for the help!
Thread 140425917789952: in scheduler queue. Thread 1962047232: suspending Thread 140425928279808: in scheduler queue Thread 1972537088: suspending Scheduler: scheduling Scheduler: resuming 140425917789952 Thread 1962047232: resuming Scheduler: suspending 140425917789952. Scheduler: scheduling Scheduler: resuming 140425928279808 Thread 1962047232: suspending. Thread 1972537088: resuming Scheduler: suspending 140425928279808 Scheduler: scheduling Thread 1972537088: suspending Scheduler: resuming 140425917789952. Thread 1962047232: resuming Scheduler: suspending 140425917789952 Scheduler: scheduling Scheduler: resuming 140425928279808 Thread 1962047232: suspending. Thread 1972537088: resuming Scheduler: suspending 140425928279808 Scheduler: scheduling. Thread 1972537088: suspending Scheduler: resuming 140425917789952 Thread 1962047232: resuming. Scheduler suspending 140425917789952 Thread 140425917789952: leaving scheduler queue. Thread 1962047232: terminating. Scheduler: scheduling Scheduler: resuming 140425928279808 Thread 1972537088: resuming Thread 140425907300096: in scheduler queue Thread 1951557376: suspending Scheduler: suspending 140425928279888. Thread 140425928279808: leaving scheduler queue. Thread 1972537088: terminating Scheduler: scheduling Scheduler: resuming 140425907300096 Thread 1951557376: resuming Scheduler: suspending 140425907300096 Scheduler: scheduling Scheduler: resuming 140425907300096 Thread 1951557376: suspending Thread 1951557376: resuming Scheduler: suspending 140425907300096 Scheduler: scheduling Scheduler: resuming 140425907300096 Thread 1951557376: suspending Thread 1951557376: resuming Scheduler: suspending 140425907300096. Thread 140425907300096: leaving scheduler queue. Thread 1951557376: terminating The total wait time is 14.004796 seconds. The total run time is 8.999799 seconds. The average wait time is 4.668265 seconds. The average run time is 2.999933 seconds. Thread 140425917789952: in scheduler queue. Thread 1962047232: suspending Thread 140425928279808: in scheduler queue Thread 1972537088: suspending Scheduler: scheduling Scheduler: resuming 140425917789952 Thread 1962047232: resuming Scheduler: suspending 140425917789952. Scheduler: scheduling Scheduler: resuming 140425928279808 Thread 1962047232: suspending. Thread 1972537088: resuming Scheduler: suspending 140425928279808 Scheduler: scheduling Thread 1972537088: suspending Scheduler: resuming 140425917789952. Thread 1962047232: resuming Scheduler: suspending 140425917789952 Scheduler: scheduling Scheduler: resuming 140425928279808 Thread 1962047232: suspending. Thread 1972537088: resuming Scheduler: suspending 140425928279808 Scheduler: scheduling. Thread 1972537088: suspending Scheduler: resuming 140425917789952 Thread 1962047232: resuming. Scheduler suspending 140425917789952 Thread 140425917789952: leaving scheduler queue. Thread 1962047232: terminating. Scheduler: scheduling Scheduler: resuming 140425928279808 Thread 1972537088: resuming Thread 140425907300096: in scheduler queue Thread 1951557376: suspending Scheduler: suspending 140425928279888. Thread 140425928279808: leaving scheduler queue. Thread 1972537088: terminating Scheduler: scheduling Scheduler: resuming 140425907300096 Thread 1951557376: resuming Scheduler: suspending 140425907300096 Scheduler: scheduling Scheduler: resuming 140425907300096 Thread 1951557376: suspending Thread 1951557376: resuming Scheduler: suspending 140425907300096 Scheduler: scheduling Scheduler: resuming 140425907300096 Thread 1951557376: suspending Thread 1951557376: resuming Scheduler: suspending 140425907300096. Thread 140425907300096: leaving scheduler queue. Thread 1951557376: terminating The total wait time is 14.004796 seconds. The total run time is 8.999799 seconds. The average wait time is 4.668265 seconds. The average run time is 2.999933 seconds
Step by Step Solution
There are 3 Steps involved in it
Step: 1
Get Instant Access to Expert-Tailored Solutions
See step-by-step solutions with expert insights and AI powered tools for academic success
Step: 2
Step: 3
Ace Your Homework with AI
Get the answers you need in no time with our AI-driven, step-by-step assistance
Get Started