RU/2: Форум. Общение пользователей и разработчиков OS/2 (eCS). : Multithreading (XIV)


Список сообщений | Написать новое | Ответить на сообщение | Домой Поиск:
Предыдущее сообщение | Следующее сообщение
From : ???
To : All
Subj : Multithreading (XIV)

System Services Flow

Very few threads actually belong to the kernel of OS/2. Every executable program(.EXE)
has at least one thread, but the kernel itself has very few. Most of the OS/2 threads
are part of executables, such as the spooler and the shell.

Except for the scheduler/dispatcher, the OS/2 kernel really does not "run" per se; it uses
the application or user threads to accomplish its work.

Let's trace a system call from the application, down through the kernel and device
drivers, all the way back up to the application. Let's assume the user has selected to
save a file. This will generate a call to DosWrite at some point. Even if the programmer
of this application has used the C runtime function write(), the call will ultimately be
made to DosWrite.

DosWrite has several parameters, including the buffer to be written and a handle
to the file(obtained previously by a call to DosOpen). Inside the program's code, there
is a call to a function in a DLL called DOSCALL1.

This function is the actual code for DosWrite. Recall that this system code resides in
DLLs to eleminate multiple copies of the code in the system, and to provide
maintainability(Note that beginning with OS/2 Warp, many of these functions have been
merged for system performance into a DLL called PMMERGE.DLL, but the external
references to the functions are still where they were, such as PMWIN.DLL and PMGRE.DLL).

The address resolution for this call was done at load time by the OS/2 program loader,
which sets up the set of addresses to system calls. In the executable file header is a list
of all DLLs that are referenced by the code being loaded. When the loader sets up the
code at load time, these references are fixed up to the actual addresses where the code
in those DLLs lives.

This process is known informally as "fixing up", and the actual code referrerd to as
"fixups", which will be elaborated on in a later lesson.

Looking back at the example function call, one can see that the thread belonging to the
process executing the DosWrite jumped from executing code from the EXE into executing
code in the DLL. At some point, there is code in the DLL to make a raw file system call,
which is not published.

It does not need to be. The interface to write a file in OS/2 is DosWrite. The subsystem
in DOSCALL1.DLL translates the file handle into raw file system information and calls
the file system functions down in the kernel. This layer of abstraction relieves the
applications of the burden of knowing what the underlying file system is; if any
modifications need to be done to the low-level code, applications need not be aware
of it.

The raw file system call transfers the thread from executing code in the DLL to code
in the kernel - the file system, to be exact. There may be several calls and returns
between the DLL and the file system; that is not important. The point here is that the
application's thread is still doing all this work.

At some point, the file system will use the services of the kernel and call into the
physical disk device driver. The file system knows about sectors, disk directory structures
and so on. The physical disk device driver is used to tell the disk to move the head, write
the bytes at this spot on the disk, and so on. Again, the application's thread is doing all
this work.

When the request goes to the physical decice driver, the thread executes code that tells
the disk controller hardware to perform the specified action. When this occurs, the thread
blocks. There is nothing for it to do until the hardware signals with a hardware interrupt
that it is done. At this time, another thread can be dispatched to utilize the processor
efficiently.

There is no reason the processor should set idle waiting for disk to get done if there are
other threads waiting to run.

When the physical device signals that it is done, the waiting thread is put back on the
ready list with a somewhat higher priority, since it was in the processor last and gave
it up voluntarily. The scheduler/dispatcher dispatcher will dispatch this thread, which
will return up the function call chain down which it came, finally ending up at the
application with a return code.

The device driver has some return indicator, which is given to its caller, the file system.
The file system interrupts the return code and formulates one for its caller, the DosWrite.
The code in the DLL for DosWrite takes this information and formats it in the structure or
return code that is returned from that API.

Notice that through all of this, no kernel or OS/2 system thread was involved. One can
start to see why you will want to make use of multiple threads to perform tasks that
take time, while allowing your users to perform other actions.

All OS/2 system services follow this same flow of execution. This is a very simplified
example - many APIs cause threads to be created and destroyed on behalf of the
application - but understanding the basic flow is vital.

By looking at the flow of this function and multiplying it by the number of threads that
can be running at a given moment, you can see how the subsystem and the device drivers
handle overlapped I/O. In the example just given, assume another thread comes into the
device driver while the first is either still doing work in there or blocked waiting on the
device.

This other thread will simply execute the same code in another context, block waiting
on the device, and will be dispatched when it is next on the ready list. It is a simple flow
multiplied many times.

Because of this flow of execution, it is really the application's threads that perform all of
the system coordination and multitasking resource synchronization. Since it is the
application threads executing the code in the file system and device drivers, for example,
any coordination is done by those threads.

In the code for the file system, video subsystem, keyboard subsystem, and all parts of
OS/2, there are many semaphores and other control structures that will serialize access
to the various system resources. For example, if two applications (actually, two threads)
try to gain access to a single device, such as an output port, the first thread will grab a
semaphore when inside the subsystem code.

When the second thread comes along to try to execute the same function, it will try to
grab the same semaphore when inside the subsystem code. Since the first thread is not
done, the semaphore will not be available, so the second thread will have to wait until
the first is done.

This is the premise behind OS/2's multitasking and common subsystems. The threads of the
application will coordinate themselves, not because of the application code, but because
all the threads execute a common set of code in the OS/2 subsystems.

When you think about it, the subsystems coordinate the execution, but the subsystems
don't really run, since they don't have any threads. The application threads coordinate
execution, because they are the parts of the system that run.

Excursion:

The Scheduler Thread and Process Control Block Reference in OS/2:

The following control blocks work with the scheduler:

(A.) Thread Control Block (TCB)

(B.) Thread Swappable Data (TSD)

(C.) Per Task Data Area (PTDA)

(D.) The local exception handler Long-Jump Buffer (ljmp)

(E.) Local Information Segment (LISEG)

(F.) Global Information Segment (GISEG)

(G.) Process Information Block (PIB)

(H.) Thread Information Block (TIB)

(I.) System Stack Frames and Client Register Information

(J.) Exit List Data Structure (EXENT)

(K.) Exception Handler Structures

The relationships between various Scheduler and Task Management control blocks are managed by

(1.) Process Management

(2.) Thread Management

(3.) the Scheduler Finite State Machine

(4.) Thread Tree for a Process

(5.) Process Trees, Subtrees and Zombies

(6.) Orphaned and Adopted Processes

(7.) Exception Management

(8.) Exception Handler Stack Frames










Sat 07 Feb 2004 14:56 Mozilla/4.61 [en] (OS/2; U)




Programmed by Dmitri Maximovich, Dmitry I. Platonoff, Eugen Kuleshov.
25.09.99 (c) 1999, RU/2. All rights reserved.
Rewritten by Dmitry Ban. All rights ignored.