• Aucun résultat trouvé

The heap and garbage collection

Dans le document [ Team LiB ] (Page 168-173)

Figure 4.18. A common storage arrangement for a single process

4.13.2 The heap and garbage collection

The classes described in Section 2.1 maintain the state of their data objects independently of any of the operations being called. A stack structure is therefore not appropriate for the storage of such objects, since stack storage disappears on procedure exit. A data structure called a heap is used for the storage of such long-lived objects. A client may create a new object of the type and will receive a reference to the object for use as a parameter for subsequent calls. Object references may be placed on the stack as input and output parameters in the usual way.

It may also be convenient to use a heap for the storage of large objects or where it is impossible to determine the size of an object at

compile time; that is, if the language allows data structures to be created dynamically and their size may depend on some runtime calculation or input data. In these cases a reference to an object may be put on the stack in place of the object itself. The language CLU took this approach to an extreme in that all data was stored in the heap and the stack contained only references; the language was said to have pointer semantics. Java has followed this approach.

Whenever storage space is allocated from the heap there is a problem over reclaiming it when it has fallen out of use. In some languages it is the responsibility of the programmer to deallocate unwanted storage. The problem here is that programmers make errors and may forget to deallocate storage. Storage allocation and deallocation is one of the most common sources of programming errors. Continuously running programs (that is, systems of some sort) are particularly susceptible to this form of error, which is called a storage leak and is a common cause of system crashes.

Another example is that storage may be deallocated when there are still references to it. Any use of such a reference is an error.

The alternative policy is that the language system automatically reclaims storage space by detecting when variables can no longer be reached. This is called garbage collection. It may be carried out synchronously, in which case execution halts until garbage collection completes, or asynchronously (also called on-the-fly) in parallel with program execution. There are heated debates in the systems area about whether it is worth putting up with your program going away to (synchronous) garbage-collect from time to time to avoid the possibility of a crash due to a storage leak. This is a choice that has to be made when a system implementation language is chosen. The ideal of efficient, transparent, asynchronous garbage collection is difficult to achieve.

[ Team LiB ]

[ Team LiB ]

4.14 Sequential programs with system calls

We have seen how a single process, supported by the operating system, is used to execute a single user-level application. This model is assumed in the discussion on language systems presented in Section 4.13. Each user-level process is typically independent of all others and needs only interact with the operating system (by means of system calls). Section 4.4 showed how the operating system allows a process to synchronize with the hardware by means of a WAIT primitive and event signalling. This interaction with the hardware is typically done by an operating system process (that is, a process executing operating system code) acting in response to a request by a user process. In a concurrent system it may also be necessary for user-level processes to synchronize and share information with each other.

In Chapter 1 a range of systems was described, and an aim of the book is to show how to write software to implement them. For some applications it may be appropriate to use a sequential programming language. Each unit of the system may be a single sequential process and the operating system may provide system calls to allow interaction with other processes in addition to the system calls for requesting operating system service.

The characteristics of the systems for which this approach is appropriate are that the units to be run concurrently are relatively large; the concurrency is coarse grained rather than fine grained. Also, interactions are likely to be relat-ively infrequent: perhaps an occasional synchronization rather than frequent cooperation involving extensive use of common information. That is, the processes are likely to be loosely coupled rather than tightly coupled.

A problem with the approach is portability of the system. The processes have made use of an operating system interface (a number of system calls) that has been provided to allow interactions between processes. If the system is to be able to run on any other operating system it is necessary for the same interface to be present. Syntactic differences can be allowed for when the code is ported, but semantic differences between operating systems are likely to occur in this area.

We shall revisit this approach after looking at the evolution of concurrent programming languages. Many systems are built from sequential languages such as C and C++ that have been extended to support concurrency. A standard library package such as pthreads provides the extensions. Details are given in Part II; in this chapter we focus on how more than one thread of control might be supported in a concurrent program.

[ Team LiB ]

[ Team LiB ]

4.15 Evolution of concurrency in programming languages

We now explore the support for concurrency that might be provided by a concurrent programming language. We take the extreme position that an entire system is to be written as a single concurrent program. For generality, we take the model that the system to be programmed is to run above a minimal operating system and explore the resulting requirements on the runtime system and the operating system for different types of application. In practice, some of the examples below are likely to be implemented as special-purpose systems with integrated operating system functions.

4.15.1 Examples

We consider some examples (A–D below) of systems and attempt to see how they could be programmed as a single concurrent program running above a minimal operating system. An important conclusion that will be drawn is that it is not sufficient to write a system in a concurrent programming language without knowing how the operating system on which the program is to run supports concurrency.

A file server is to run on a dedicated uniprocessor machine available to its clients across a network. It takes requests from clients and can be working on a number of requests at the same time. For example, it may need to wait for the disk to be read on behalf of one client, so starts work on behalf of another. Figure 4.21 shows the static modular structure. Refer to Figure 2.14, Chapter 6 and Section 9.1 for background and related information.

Figure 4.21. A: a dedicated file server.

A.

A simple operating system must control a number of devices, and a device-handling subsystem (Figure 4.22) is to be written in some appropriate programming language. A device handler may finish its immediate task and may need to wait for more data to be input or output. It must not lose track of what it was doing, however. If it was filling a buffer with characters coming in from the network, the buffer and associated counts and pointers should still be there when that device is serviced again.

Figure 4.22. B: a device-handling subsystem.

B.

Figure 4.23 shows a computerized control system for a chemical plant which carries out periodic data gathering, analysis and feedback, as described in Section 1.1.1. The computer system should also respond to alarm signals which are infrequent and unpredictable, but of very high priority.

Figure 4.23. C: a chemical plant controller.

C.

A shared-memory multiprocessor is available and a parallel algorithm is proposed to search for a particular value in a large amount of stored data. Parallel activities should execute code to search a partition of the data, see Section 1.2.1. A

management activity is needed to start off the correct number of parallel searches, allocate partitions of the data to them and be ready to receive the result and stop the searches when the result is found.

D.

Dans le document [ Team LiB ] (Page 168-173)