Смекни!
smekni.com

Windows NT Vs Unix As An Operating (стр. 2 из 2)

subsequent accesses. Synchronisation of access to allocated heaps can be done

either explicitly through Windows NT synchronisation objects, or by using an

appropriate parameter at the creation of a heap. All access to memory in that

particular heap is synchronised between threads in the process.

Memory-mapped files are also provided in Windows NT. This provides a convenient

way to access disk data as memory, with the Windows NT kernel managing paging.

This memory may be shared between processes by using CreateFileMapping()

followed by MapViewOfFile().

Windows NT provides thread local storage (TLS) to accommodate the needs of

multithreaded applications. Each thread of a subprocess has its own stack, and

may have its own memory to keep various information.

Windows NT is the first operating system to provide a consistent multithreading

API across multiple platforms. A thread is unit of execution in a process

context that shares a global memory state with other threads in that context (if

any). When a process is created in Windows NT, memory is allocated for it, a

state is set up in the system, and a thread object is created. To start a thread

in a currently executing process, the CreateThread() call is used as a function

pointer is passed in through lpStartAddr; this address may be any valid

procedure address in an application.

Windows NT supports a number of different types of multiprocessing hardware. On

these designs, it’s possible for different processors to be running different

threads an application simultaneously. Take care to use threads in an

application to synchronise access to common resources between threads.

Fortunately, Windows NT has very rich synchronisation facilities.

Most UNIX developers don’t use threads in their applications since support is

not consistent between UNIX platforms.

Handles don’t have a direct mapping from UNIX; however, they’re very important

to Win32 applications and deserve discussion. When kernel objects (such as

threads, processes, files, semaphores, mutexes, events, pipes, mailslots, and

communications devices) are created or opened using the Win32 API, a HANDLE is

returned. This handle is a 32-bit quantity that is an index into a handle table

specific to that process. Handles have associated ACLs, or Access Control Lists,

that Windows NT uses to check against the security credentials of the process.

Handles can be obtained by explicitly creating them (usually when an object is

created), as the result of an open operation (e.g. OpenEvent()) on a named

object in the system, inherited as the result of a CreateProcess() operation (a

child process inherits an open handle from its parent process if inheritance was

specified when the original handle was created and if the child process was

created with the “inherit handles” flag set), or “given away” by

DuplicateHandle(). It is important to note that unless one of these mechanisms

is used, a handle will be meaningless in the context of a process.

For example, suppose process 1 calls CreateEvent() to return a handle that

happens to have the ordinal value 0×1FFE. This event will be used to co-ordinate

an operation between different processes. Process 2 must somehow get a handle to

the event that process 1 created. If process 1 somehow “conjures” that the right

value to use is 0×1FFE, it still won’t have access to the event created by

process 1, since that handle value means nothing in the context of process 2. If

instead, process 1 calls DuplicateHandle()with the handle of process 2 (acquired

through calling OpenProcess() with the integral id of process 2), a handle that

can be used by process 2 is created. This handle value can be communicated back

to process 1 through some IPC mechanism.

Handles that are used for synchronisation (semaphores, mutexes, events) as well

as those that may be involved in asynchronous I/O (named pipes, files,

communications) may be used with WaitForObject() and WaitForMultipleObject(),

which are functionally similar to the select() call in UNIX.

Prior to 3BSD most UNIX systems were based on swapping. When more processes

existed than could be kept in physical memory, some of them were swapped out to

disk or drum storage. A swapped out process was always swapped out in its

entirety and hence any current process was always either in memory or on disk as

a complete unit.

All movement between memory and disk was handled by the upper level of a split

level scheduler, known as the (memory) swapper. Swapping from memory to disk was

initiated when the kernel ran out of free physical memory.

In order to choose a victim to evict, the swapper would first look at the

processes that were being blocked by having to wait for something such as

terminal input or a print job to respond. If more than one process was found,

that process whose priority plus residence time was the highest was chosen as a

candidate for swapping to disk. Thus a process that had consumed a large amount

of CPU time recently was a good candidate, as was one that had been in memory a

long time, even if it was mostly doing I/O. If no blocked process was available

in memory then a ready process was chosen based on the same criteria of priority

plus residence time.

Starting with 3BSD, memory paging was added to the operating system to handle

the ever larger programs that were being written. Both 4BSD and System V

implemented demand paging in a similar fashion. The theory of demand paging is

that a process need not necessarily be entirely resident in memory in order to

continue execution. All that is actually required is the user structure and the

page tables. If these are swapped into memory, the process is then deemed to be

sufficiently in memory and can be scheduled to execute. The pages of the text,

data and stack segments are brought in dynamically, one at a time, as they are

referenced, thus leaving memory free for other tasks rather than filling it with

tables of data which may be referenced only once. If the user structure and page

table are not in memory, the process cannot be executed until the swapper swaps

them into memory from disk.

Paging is implemented partly by the main kernel and partly by a process called

the page daemon. Like all daemons, the page daemon is started up periodically so

that it can look around to see if there is any work for it to do. If it

discovers that the number of free pages in memory is too low, it initiates

action to free up more pages.

When a process is started it may cause a page fault due to one of its pages is

not being resident in memory. When a page fault occurs, the operating system

takes the first page frame free on the list of page frames, removes it from the

list and reads the needed page into it. If the free page frame list is empty,

the process must be suspended until the page daemon has had time to free a page

frame from another process.

The page replacement algorithm is executed by the page daemon. At a set interval

(commonly 250 millisec but varying from system to system) it is activated to see

if the number of free page frames is at least equal to a system parameter known

as lotsfree (typically set to 1/4 of memory). If there are insufficient page

frames, the page daemon will start transferring pages from memory to disk until

the lotsfree parameter value of page frames are available. Alternatively, if the

page daemon discovers that more than lotsfree page frames are on the free list,

it has no need to perform any function and terminates until its next call by the

system. If the machine has plenty of memory and few active processes, it will be

inactive for most of the time.

The page daemon uses a modified version of the clock algorithm. It is a global

algorithm, which means that when removing a page it does not take into account

whose page is being removed. Thus the number of pages each process has assigned

to it varies in time, depending both on its own requirements and other process

requirements. The size of the data segment may vary depending upon what has been

requested, the operating system tracking allocated and unallocated memory blocks

while the memalloc function manages the content of the data segment.

Process Management, Inter-process Communication and Control

The Windows NT process model differs from that of UNIX in a number of aspects,

including process groups, terminal groups, setuid, memory layout, etc. For some

programs, such as shells, a re-architecture of certain portions of the code is

inevitable. Fortunately, most applications don’t inherently rely on the specific

semantics of UNIX processes, since even this differs between UNIX versions.

Quoting from the online help provided with the Windows NT SDK:

Win32 exposes processes and threads of execution within a process as objects.

Functions exist to create, manipulate, and delete these objects.

A process object represents a virtual address space, a security profile, a set

of threads that execute in the address space of the process, and a set of

resources or objects visible to all threads executing in the process. A thread

object is the agent that executes program code (and has its own stack and

machine state). Each thread is associated with a process object which specifies

the virtual address space mapping for the thread. Several thread objects can be

associated with a single process object which enables concurrent execution of

multiple threads in a single address space (possible simultaneous execution in a

multiprocessor system running Windows NT). On multiprocessor systems running

Windows NT, multiple threads may execute at the same time but on different

processors.

In order to support the process structure of Windows NT, APIs include:

* Support for process and thread creation and manipulation.

* Support for synchronisation between threads within a process and

synchronisation objects that can be shared by multiple processes to allow

synchronisation between threads whose processes have access to the

synchronisation objects.

* A uniform sharing mechanism that provides security features that

limit/control the sharing of objects between processes.

Windows NT provides the ability to create new processes (CreateProcess) and

threads (CreateThread). Rather than “inherit” everything always, as is done in

UNIX with the fork call, CreateProcess accepts explicit arguments that control

aspects of process creation such as file handle inheritance, security attributes,

debugging of the child process, environment, default directory, etc. It is

through the explicit creation of a thread or process with appropriate security

descriptors that credentials are granted to the created entity.

Win32 does not provide the capability to “clone” a running process (and it’s

associated in-memory contents); this is not such a hardship, since most UNIX

code forks and then immediately calls exec. Applications that depend on the

cloning semantics of fork may have to be rearchitected a bit to use threads

(especially where large amounts of data sharing between parent and child occurs),

or in some cases, to use IPC mechanisms to copy the relevant data between two

distinct processes after the CreateProcess call is executed.

If a child process is to inherit the handles of the creator process, the

bInherit flag of the CreateProcess call can be set. In this case, the child’s

handle table is filled in with handles valid in the context of the child process.

If this flag is not specified, handles must be given away by using the

DuplicateHandlecall.

Windows NT was not designed to support “dumb terminals” as a primary emphasis,

so the concept of terminal process groups and associated semantics are not

implemented. Applications making assumptions about groups of applications (for

example, killing the parent process kills all child processes), will have to

investigate the GenerateConsoleCtrlEvent API, which provides a mechanism to

signal groups of applications controlled by a parent process using the CREATE_

NEW_PROCESS_GROUP flag in the CreateProcess API.

Programs making assumptions about the layout of processes in memory (GNU EMACS,

for example, which executes, then “dumps” the image of variables in memory to

disk, which is subsequently “overlayed” on start-up to reduce initialisation

time), especially the relationship of code segments to data and stack, will

likely require modification. Generally, practices such as these are used to get

around some operating system limitation or restriction. At this level, a

rethinking of the structure of that part of the application is generally in

order, to examine supported alternatives to the “hack” that was used (perhaps

memory mapped files for particular cases like this). For those who must deal

with an application’s pages on this level, there is a mechanism by which a

process may be opened (OpenProcess), and individual memory pages, threads, and

stacks examined or modified.

There is no direct equivalent of the UNIX setuid. There are, however, a number

of Windows NT alternatives to use depending on the task to be accomplished. If

the task at hand is a daemon that runs with a fixed user context, it would be

best to use a Windows NT service (again, the online help is invaluable for this

information). A Windows NT service is equivalent to a “daemon” running with

fixed user credentials, with the added benefit of being administrable locally or

remotely through standard Windows NT administration facilities. For instances

when a process must “impersonate” a particular user, it’s suggested that a

server program be written that communicates thr

359