Você está na página 1de 8

Simulation of Multithreading on Single Threaded

Operating Systems
Chandrajit G. Joshi

ABSTRACT control within a process. Threads are a


Single threaded operating system is that which mechanism to allow a process to perform more
itself runs as single threaded and does not support than one activity at a time. Application
multithreading at the kernel level. A very good programmers choose to write multithreaded
example of this is HP NonStop™ Kernel that is programs when they want to achieve
used in most of the business critical applications
including all major stock exchanges and in • Improved performance and concurrency
finance and telecom sectors across the world.
• Simultaneous access to common resources
• Reduced usage of system resources
HP NonStop™ Kernel operating system has
"Loosely Coupled, Shared Nothing" architecture, • Efficient usage of multiprocessors
in the sense that each CPU in the system has its
own copy of operating system and RAM, and any These threads are also called as user level threads
two processes in the system communicate with since they are a part of user application
each other through messages. This paper aims to programs. There are some operating systems such
discuss ways multithreaded programs can be as Unix, which identify and support these user
written to run on such operating systems. level threads and have mechanisms at the kernel
Categories and Subject Descriptors level to manage them. But, programmers have
D.1.3 [Concurrent Programming]: problems developing multithreaded applications
for the operating systems, which do not support
General Terms user level threads. This paper suggests two
Programming, Operating System, Input/Output approaches for implementing user level threads
(I/O) in the application programs on such operating
systems.
Keywords
Multithreading, Ready list, Thread-control-block,
Task-control-block, Blocked and Unblocked I/O. 2. APPROACH 1
In this approach, a thread will be nothing but a
function-procedure executing independently of
the mainline code of process. Each thread will
1. INTRODUCTION execute on a stack different from that of main
The word "multithreading" can be translated as stack of the process. All the threads will be
multiple threads of control or multiple flows of initialized, scheduled, started and managed by a
Permission to make digital or hard copies of all or part of this work for
personal or classroom use is granted without written permission
"Thread Module" in the mainline code using data
provided that copies are not made or distributed for profit or commercial structures such as thread-control-block (ThCB),
advantage and that copies bear this notice and the full citation on the
first page. To copy otherwise, or republish, to post on servers or to and doubly linked lists called as thread-ready-list
redistribute to lists, requires prior specific permission and/or a fee. and wait list. “Thread Module” is a library of
TATA Consultancy Services Ltd, Air India Building, Mumbai function-calls that provides functions for
management of user level threads. Real challenge
in this approach is to develop the Thread Module.
A thread will represent a path of code execution
ThCBs, thread-ready-list and wait-list will be within the process. It will perform some activity
allocated and initialized at the process startup in response to a triggering event. Hence a thread
time. Thread-ready-list will contain those ThCBs will be scheduled either in response to an
that are to be launched by the Thread Module. external event like an incoming request message
Wait list will contain those ThCBs that are or an internal event like housekeeping. It will be
waiting for an event completion. The event could the responsibility of thread scheduler to swap out
be an I/O, timeout or any other application a thread when the thread initiates a blocked I/O
specific event. ThCBs will be allocated for all the or if it calls a system function that suspends the
executing threads and they will hold the context process for specified interval of time (e.g. delay
information necessary for swapping the threads function in Unix). A thread will be swapped in
in and out. This information will include when an event for which the thread is waiting for,
occurs. This event could be I/O or delay
completion. The thread ends when the function-
• Name of the function-procedure running as procedure returns.
thread
• Thread stack address
2.2 THREAD SYNCHRONIZATION
• Current stack pointer Two synchronization models can be used to
• Program counter achieve thread synchronization for concurrently
• Information about the last operation (I/O or executing threads to control program flow and
timeout) initiated by the thread access to shared resources.
• Priority of the thread, if priority based
scheduling is selected • Mutex locks allow only one thread at a time
to execute a specific section of code, or to
access specific data.
2.1 THREAD SCHEDULING
• Counting semaphores typically coordinate
Thread scheduling will be done in “First In First
access to resources. The count is the limit on
Out (FIFO)” manner. Alternatively, programmers
how many threads can have access to a
can assign a priority number for each thread.
semaphore. When the count is reached, the
Higher number will indicate high priority thread
semaphore blocks.
and so on. In priority based scheduling, ThCBs
will occupy the position in ready list depending
on their priorities, with highest priority thread at 2.3 ILLUSTRATIVE PSEUDO CODE
the head of ready list. If two threads have same Following pseudo code is just an illustration and
priority, FIFO method can be adopted for their hence variations in the implementation depending
scheduling. Fig. 1 shows the ready list. Each on the programming style and nature of the
block represents a ThCB. ThCBs are arranged in program are possible. Each pseudo code is
the descending order of their priorities i.e. P1 > followed by its explanation. “C” style pseudo
P2 > P3…. > Pn code has been used just for the ease of
understanding. One may choose to write the
HEAD TAIL actual code in any suitable programming
NEXT NEXT NEXT NEXT language.
P1 P2 P3 . . . . . . . . Pn

PREV PREV PREV PREV

Fig. 1
2.3.1 Pseudo Code of main()
main ()
{
DECLARE and INITIALIZE local variables.
The SWITCH case that follows listener()
executes appropriate case depending on the type
CALL init_Process().
of request. The function thread_Schedule()
allocates a ThCB and execution stack for the
CALL init_Thread_Module().
appropriate function depending on the type of
CALL open_Files().
request received, and places the ThCB in the
ready list. This function will then be launched as
WHILE (TRUE)
thread. process_Unknown_Request() handles all
{
the undefined requests. Function main() returns if
CALL listner(). the request type is QUIT. Process then enters the
thread execution loop and does not exit from it
SWITCH (Request Type) till there are ThCBs in the ready list.
{ thread_Dispatch() launches and executes all the
READ : CALL thread_Schedule(READ). threads in the ready list and returns only when
WRITE : CALL thread_Schedule(WRITE). there are no threads in it.
UPDATE : CALL thread_Schedule(UPDATE). thread_IO_Completion() checks for any event
QUIT : RETURN. completion (e.g. IO or timeout). If there is any
. thread that is currently waiting for the event
. completion, then it takes the corresponding
DEFAULT: CALL process_Unknown_Request(). thread from the wait list and puts it in the ready
} /* End of SWITCH */ list according to its priority.
WHILE (There are ThCBs in the ready list)
{ In this pseudo code, thread_Dispatch() is the
CALL thread_Dispatch(). most important function because when it finds a
ThCB in the ready list, it launches the thread. In
CALL thread_IO_Completion(). other words, the execution jumps to the entry
} /* End of WHILE there are ThCBs */ point of the function. Following pseudo code
} /* End of WHILE TRUE */ illustrates a function that will execute as a thread.
} /* End of main() */

2.3.3 Pseudo Code of read_From_DB()


2.3.2 Explanation of main() This function will be scheduled as a thread when
Function init_Process() initializes all the global the request type is READ. The function
data-structures except ready list, wait list and thread_Schedule() schedules it in the main().
ThCBs. init_Thread_Module() initializes all the
ThCBs, the ready list and the wait list. It also read_From_DB()
schedules all the threads that perform internal {
activities like housekeeping. It is important to /* This function reads into a buffer from a
schedule them before entering in the main given database file in blocked mode */
processing loop because these threads are not
triggered by external events. The function DECLARE and INITIALIZE local variables.
open_Files() opens all the files including
database and process files. It opens them in such CALL threadIO_Read().
a mode that it will enable performing blocked
IOs on them. Process then enters the request- RETURN to the caller.
listening loop, which is the main processing loop. } /* End of read_From_DB() */
Function listener() listens to all kinds of external
requests. It returns as soon as it gets a request.
2.3.4 Explanation of read_From_DB()
threadIO_Read() reads from a given file in Fig. 2 shows how program execution will jump
blocked mode. But, before that it saves the from main stack of the process to the thread stack
information about thread stack in ThCB. This is Function thread_Start() will be a “Thread
important because the thread will be swapped out Module” function that sets up the thread
when it initiates the I/O for READ operation. environment for read_From_DB() function. It
This function also puts the ThCB in the wait list. also de-allocates all the thread related resources
threadIO_Read() can only be called by a thread. allocated to read_From_DB() when it returns.
Finally, the function returns to the caller. The
caller here is a “Thread Module” function i.e.
thread_Start(). Details about it are given in Fig.

Direction of stack growth


Direction of stack growth
2.

In this approach, process need not manage delays


and timeouts, since OS itself will manage them.
Delays and timeouts will especially be useful in
the housekeeping threads. Such threads will thread_Dispatch() read_From_DB()
normally run throughout the life cycle of the Jumps to

main() thread_Start()
process. In other words, these are long running
threads. The following pseudo code shows this. Main stack Thread stack
Fig. 2

2.3.5 Pseudo Code of garbage_Cleanup() 3. APPROACH 2


garbage_Cleanup()
{
The second approach is based on the assumption
/* This is a housekeeping function. This
that the operating system should provide setjmp()
function runs as a thread after every 30 and longjmp() or similar system-calls. The first
minutes and in each run, it cleans up all one is to save the current process context in a
the scratch files created by the process */ buffer and the second one is to unconditionally
jump to the saved process context from anywhere
DECLARE and INITIALIZE local variables. within the process. For example, HP NonStop™
Kernel provides this service by providing two
WHILE (TRUE) system-calls called SETJMP_ and LONGJMP_
{
CALL delete_Stray_Files().
In this approach, a task that is performing a set of
CALL thread_Delay() for 30 minutes. sequential and interrelated activities can be
} /* End of WHILE */ treated as a thread. As in the first approach, a
} /* End of garbage_Cleanup() */ thread will be scheduled as a result of a
triggering event.

2.3.6 Explanation of garbage_Cleanup()


This function executes an infinite loop, since it is Unlike the first approach, here all the threads will
a long running thread. delete_Stray_Files() does execute on the main stack of the process. In this
the job of file deletion. thread_Delay() saves the approach, the process will have two modes of
context information of the thread in its ThCB, put execution - kernel mode and task-execution
it in wait list and calls system function delay(). mode. Each task will be identified by a data
When 30 minutes are over, execution starts from structure called task-control-block (TCB) and the
the code-statement following the call to delay(). task scheduling will be managed using a task-
ready-list. A TCB will contain all the context In task-execution mode, process will call
information about the task. This will include appropriate function according to the dispatch-
state of the task. The function will perform the
sequential and interrelated activities, which the
• Name of the task task is meant for. The process will return back to
• Next dispatch-state of the task. This tells the kernel mode whenever it initiates an event like
task what to do next I/O or timeout completion. In this approach, the
• Event for which task is waiting for process should initiate all the I/Os and timeouts
in unblocked mode.
• Latest event occurred for which the task was
waiting
• Timeout value when the task wants to Unlike in the first approach, management of
timeout or cancel the operation if the event delay or timeouts will have to be managed by the
does not occur in specified interval of time process itself. This can be done by a timeout list.
It will be similar to ready list, but the TCBs will
• Result of last I/O completion
be placed in the ascending order of their timeout
• Pointers to the buffers if the task is using any values i.e. T1 < T2 < T3… < Tn as represented in
for doing any operation Fig. 3
• Priority of the task, if priority based
scheduling is selected HEAD TAIL

• The value of step. If a task performs 5 NEXT NEXT NEXT NEXT

sequential and inter-related activities and if TO = T1

PREV
TO = T2

PREV
TO = T3

PREV
. . . . . . . . TO = Tn

PREV

currently it is performing 3rd activity, then


step will have value 3. This is explained in Fig 3

more details in the pseudo codes


3.1 ILLUSTRATIVE PSEUDO CODE
Task-ready-list will be a doubly linked list
similar to the thread-ready-list, where TCBs will
3.1.1 Pseudo Code of main()
be placed. These will be the TCBs representing main ()
those tasks that are to be launched by the process {
while in Kernel mode. The scheduling of tasks DECLARE and INITIALIZE local variables.
can either be FIFO or programmers may adopt
the priority based scheduling in this approach as CALL init_Process().
well. Any one task will be active at any point in
time. A global structure pointer, say CurTCB, CALL open_Files().
will identify currently active / executing task.
CALL thread_SetEnv(jmpBuf).

In Kernel mode, process will be responsible for IF ready list is NOT empty
establishing threading environment for the GOTO launch_The_Task.
process. In this mode, it will also be responsible
for detecting and processing I/O and timeout CALL listener().
completions, setting the next dispatch-states for
various tasks according to the result of last I/O or SWITCH (Request Type)
timeout completion and launching the tasks {
according to their respective dispatch states. READ: CALL process_Read().
WRITE: CALL process_Write().
UPDATE: CALL process_Update().
QUIT: RETURN.
E.g. for READ request, process_Read() is called.
.
process_Read() allocates a TCB for this request
.
and sets all the structure members of TCB like
DEFAULT: CALL process_Unknown_Request().
name of task, its priority, to appropriate values.
} /* End of SWITCH */
A check for TCBs in the ready list is again made,
IF ready list is NOT empty
and the tasks are launched if there are any.
GOTO launch_The_Task.

If there are no tasks in the ready list, a check for


IF checkTO() THEN timeout completion is made. This is done by
process_TO_Completion().
checkTO(). It returns TRUE if there is a timeout
completion for a task. It determines this by
IF checkIO() THEN
scanning through the timeout list.
Process_IO_Completion().
Process_TO_Completion() sets the appropriate
next dispatch-state for the task that has timed out
Launch_The_Task:
and puts the TCB in the ready list according to its
SET CurTCB to the first TCB in ready list
priority.

SET CurTCB->step TO 1. If there is no timeout completion, process checks


for I/O completion by calling checkIO(). It
SWITCH (Next Dispatch State of CurTCB)
returns TRUE if there is an I/O completion.
{
process_IO_Completion() determines for which
READ : read_From_DB().
task, the I/O has completed, saves the result of
WRITE : write_In_DB().
I/O in the TCB, sets the appropriate next
UPDATE : update_DB().
.
dispatch-state for the task and puts it in the ready
.
list according to its priority.
.
} /* End of SWITCH */ Under the label Launch_The_Task, CurTCB is
set to the first TCB in the ready list. “step”
} /* End of main */
parameter of CurTCB is then set to 1 to indicate
that the task will perform first in the whole set of
3.1.2 Explanation of main() activities. Finally, depending on the dispatch-
As in the first approach, init_Process() initializes state of the TCB, appropriate function is called.
all the global data-structures. But here,
initialization includes TCBs and the ready list In this approach, how the process determines I/O
also. open_Files() opens all the database and completion is completely OS dependent.
process files. It opens them in such a mode that Normally, operating systems provide system-calls
they will enable performing unblocked IOs on to determine this. They also provide calls to
them. Process enters the Kernel mode in the cancel an I/O if it does not complete within
thread_SetEnv() function. This function calls specified time limit. In this way, process can
setjmp() or an appropriate similar function perform timeouts on I/O operations.
provided by OS, to establish the threading
environment. If there are TCBs in the ready list,
the task at the head is launched (this is done by 3.1.3 Pseudo Code of read_From_DB()
read_From_DB()
GOTO to Launch_The_Task). listener() listens to
{
all kinds of external requests. Depending on the
/* This function performs READ operation on a
type of request, appropriate function is called.
given database file in unblocked mode */
Write_In_DB task

DECLARE and INITIALIZE local variables.

Read_From_DB
IF (CurTCB->step IS EQUAL TO 1) Other tasks PROCESS
task
DATABASE
{
CALL thread_ReadDB(FILE1).
Update_DB
task

SET CurTCB->step TO 2.
Fig. 4

CALL thread_Swapout(). There are possibilities of race conditions in this


} /* End of step 1 */
approach. E.g., the memory pool for buffers used
in the I/O operations is a common resource and
IF (CurTCB->step IS EQUAL TO 2)
two or more tasks can compete for acquiring
{
buffers from it. To avoid race conditions and to
CALL thread_ReadDB(FILE2).
synchronize various tasks, the data structure
CALL thread_Swapout().
governing the pool will have a queue. If a task
} /* End of step 2 */
tries to acquire a buffer from this pool and the
} /* End of read_From_DB */
buffer of required size is not available, TCB will
put in this queue. When the buffer becomes
available, the TCB will be put back in the ready
3.1.4 Explanation of Pseudo Code list from where it will be launched by the Kernel
As evident from the pseudo code, the function is mode.
used to read from two database files. Hence the
whole activity has been divided in two steps. So
these steps become small inter-related activities, 4. CONCLUSION
in the sense that process cannot go to second Programmers can use any of these approaches in
before it finishes first. thread_ReadDB() sets up developing multithreaded applications to run on
the environment for initiating the I/O and then single threaded OS to get the benefits of
initiates READ in unblocked manner. It then sets multithreading at the application level, if not at
the step of CurTCB to 2 so that when the the kernel level. Products, which have adopted
function is called the next time, it will directly go these approaches, are already available on HP
to step 2 in the function. thread_Swapout() calls NonStop™ platform.
longjmp() with an appropriate return value.
Hence the control goes back to setjmp() in the
main() function. After each step, the function
gives control back to Kernel and waits for the I/O
initiated in the earlier step to complete.

The process is depicted in the Fig. 4. Each task is


executing independently of each other. At any
point in time, any one task is active and all other
tasks are either waiting for an event or are
suspended.
5. ACKNOWLEDGMENTS
I am thankful to my colleague Mr. Jyoti Ranjan
Nayak for reviewing this document and
providing some very good suggestions. I would
also like to thank my Project Leader Mr. Arun
Jambotkar, Account Manager Mr. Royen
Fernandes and Group Leader Mr. Gopal Menon
for their continuous support.

6. REFERENCES
[1] Hewlett-Packard, “User Level Native Thread
Primitives”, www.hp.com
[2] “Advanced Linux Programming”,
www.advancedlinuxprogramming.com

Você também pode gostar