Microsoft KB Archive/45708
Complete Strategy for Successful CHAINing with COBOL 3.00
PSS ID Number: Q45708 Article last modified on 06-24-1993
3.00 3.00a | 3.00 3.00a MS-DOS | OS/2
Summary: This article explains how the CHAIN statement releases memory in Microsoft COBOL Versions 3.00 and 3.00a for MS-DOS and MS OS/2. Using multiple CHAINs often results in memory fragmentation. This is due to two traits of the COBOL 3.00 CHAIN. The first program that is run in the CHAIN stays in memory until all programs in the CHAIN are ended. Secondly, when a program is CHAINed-to after the first program, its memory is not released until the second subsequent CHAIN. Both of these traits and fragmentation are discussed in detail later in this article. This problem often occurs when updating COBOL 2.00, 2.10, or 2.20 programs, which rely heavily on CHAINing and in which only the current program remains in memory. A specific strategy, described below, can be used to avoid memory fragmentation when CHAINing. More Information:
Strategy for Successful CHAINing with COBOL 3.00
There are two traits of the CHAIN statement in Microsoft COBOL Version 3.00 or 3.00a: 1. The first program that starts the CHAIN, the one loaded by typing its name at the DOS prompt, stays in memory until all CHAINed programs have ended. The memory for that program is never released, and it is not reloaded from the disk even if you CHAIN back to it multiple times. 2. The first program is loaded in the CHAIN and subsequent CHAINs occur; the memory for a program is not released until the second subsequent CHAIN in the sequence. The following diagram illustrates the above two traits of the CHAIN:
Low __________________ memory| Program “A” | | CHAIN “B”. | <– This program always remains in memory. |________________| | Program “B” | | CHAIN “C”. | <— CHAIN statement #1. |________________| | Program “C” | Chain statement #2. High | CHAIN “D”. | <— When program “C” issues CHAIN to Memory|________________| program “D”, Program “B” is unloaded | Unused | from memory. This is because it is | Memory | the second CHAIN (not counting the | | first program, which is unique).
In the above diagram, the original program “A” is loaded by typing its name at the DOS prompt. When program “A” CHAINs to program “B”, the second trait takes effect. “B” is loaded from the disk and CHAINs to program “C”. “B” remains in memory until program “C” chains to program “D”. Likewise, “C” remains until “D” CHAINs to “E”. The result is that two of the programs in the CHAIN, not including “A”, always exist in memory – the current program and the previous program. Because of these two considerations, a very specific strategy must be used to construct a program that relies heavily on CHAINing.
Correct Chaining Strategy
There are three steps in the strategy for successfully CHAINing in COBOL 3.00: 1. A “DRIVER” program must do all of the CHAINing. The program that is CHAINed to always CHAINs back to the DRIVER. The DRIVER then CHAINs to the next program in the CHAIN.
Low __________________ __________________ memory| DRIVER.EXE | | DRIVER.EXE | | CHAIN “PROG1” | | CHAIN “PROG2” | |_______________ | result |_______________ | | PROG1.EXE | —–> | | | CHAIN | | PROG2.EXE | | “DRIVER” | | CHAIN | |________________| | “DRIVER” | | | | | High | Unused | | | Memory| Memory | |________________| | | | Unused | | | | Memory |
In the diagram above, DRIVER is initially loaded and CHAINs to PROG1.EXE. When PROG1.EXE CHAINs back to the DRIVER, DRIVER was the first program run and, therefore, still exists in memory exactly where it was originally loaded. Then, when DRIVER CHAINs to the PROG2.EXE above, the previous program, PROG1, is removed from memory. This is because the CHAIN from PROG1.EXE to DRIVER was the first CHAIN, and the CHAIN from the DRIVER to PROG2 is the second. On the second CHAIN in the sequence, the memory for PROG1 is released. Since the memory released is always above the DRIVER program, free memory is kept in a contiguous block. This prevents memory fragmentation. 2. You must statically link in any support modules to the DRIVER.OBJ. The COBOL support modules are .OBJ files that contain the code support for programming ACCEPT, DISPLAY, INDEXED files, and EXTERNAL files and data. A program that uses INDEXED files, ACCEPTS, and DISPLAYS uses the following link line: LINK DRIVER.OBJ+ADIS.OBJ+ADISINIT.OBJ+ADISKEY.OBJ+IXSIO.OBJ; If you use EXTERNAL data, you must add EXTERNL.OBJ to this link line. If you use EXTERNAL files, exchange EXTFH.OBJ for IXSIO.OBJ. This means that you link in EXTFH.OBJ and not IXSIO.OBJ because EXTFH.OBJ supports both EXTERNAL FILEs and INDEXED files, but IXSIO.OBJ handles only INDEXED files. Once you have linked these modules into the DRIVER program, it is not necessary to link them with any of the CHAINed programs. This is because the DRIVER is the first program run, and always remains in memory. Therefore, the support modules linked to it are always available to the current CHAINed program. The following illustrates how the program will appear in memory:
Low __________________ memory| DRIVER.EXE | |- - - - - - - - | |LINKed in support| <- These are accessible by the current | .OBJs | CHAINed program, remain stationary |_________________| throughout the program and stay low in | Current CHAINed| memory because they are attached to | program | the DRIVER. |________________ | | | High | Unused | Memory| Memory | | | | |
If you use any files, the first time the DRIVER program is executed, it must OPEN one INDEXED file and one file of any other type, RELATIVE or SEQUENTIAL, and leave them OPEN until the last CHAIN has finished. These files should be two “dummy” files so that leaving them open for the duration of the program will not cause problems. This is necessary because there are two buffer regions associated with files, a 64K buffer region used by INDEXED files and a 32K buffer region used by other files. By OPENing and leaving OPEN these files in the DRIVER program, the buffers are allocated immediately above the DRIVER.EXE program and remain stationary as long as these files are OPEN. The following diagram illustrates how they will appear in memory:
Low ___________________ memory| * DRIVER.EXE | | | | | | OPEN FILE-1 | | | (INDEXED file). | | | OPEN FILE-2 | | | (RELATIVE file).| | | CHAIN “current”.| | |- - - - - - - - - | | |linked-in support | | | .OBJs | | |________________| | | 64K INDEXED file | |-> These should be lowest in memory | buffer region | | if you use the DRIVER method. |__________________| | | 32K SEQUENTIAL | | |or RELATIVE buffer| | |__________________| | | Current CHAINed | | program | |________________| | | High | Unused | Memory| Memory | | | | |
Note that these are “buffer regions” and not the file buffers themselves. These buffer regions are memory that is set aside for allocation of file buffers. Every time a file is opened, a file buffer is allocated inside of one of these two regions. If all files are CLOSEd in the run-time system, then the memory for these areas is released. This is why the driver must leave OPEN the two “dummy” files so that the buffer regions remain stationary. The last diagram above shows how a program that relies heavily on CHAINing should appear in memory for COBOL 3.00 if the three methods described above are used. The diagram shows that with this method, the unused memory will always be above all parts of the program and will remain contiguous and nonfragmented.
There are three common causes for fragmentation; each corresponds to one of the three strategies above and should explain why those CHAINing strategies are necessary: 1. Not using a DRIVER program can cause fragmentation. Consider the following run system where PROG1 CHAINs to PROG2, which CHAINs to PROG3, which CHAINs to PROG4. Note: In all of the following diagrams, the file sizes noted (e.g. PROG1, Size: 50K) refer to how large the program is once it is loaded into memory, not its file size on disk. These two values can differ, but it is the size of the program “in memory” that is crucial.
Low __________________ __________________ memory| PROG1.EXE | This | PROG1.EXE | | Size: 50K | <-program->| Size: 50K | | CHAIN “PROG2” | never | | |_______________ | moves. |_______________ | | PROG2.EXE | | Unused Memory | ? | Size: 50K | | (50K) | <—-| | CHAIN “PROG3” | | | | |_______________ | result |_______________ | ___________ 500K | PROG3.EXE | —–> | PROG3.EXE | |PROG4.EXE| Total | Size: 300K | PROG2.EXE | Size: 300K | |Size: | Avail.| CHAIN “PROG4” | unloaded |(remains in | | 150K | | | here. | memory) | |_________| |________________| |________________| | High | Unused Memory | | Unused Memory | <—-| Memory| (100K) | | (100K) | ?
In the above diagram, PROG1 is loaded and remains stationary throughout the program. The second CHAINing trait begins here. PROG2 successfully chains to PROG3. Because the current program and the previous program in the CHAIN stay in memory, PROG3 must be loaded above PROG2 into the middle of memory. When PROG3 CHAINs to PROG4, PROG2 is unloaded. PROG3 becomes the previous program and stays in memory. However, PROG4 cannot be loaded. There is enough total memory available to load the program, but the memory is not contiguous – it is fragmented into two smaller pieces, neither of which is large enough to load PROG4. A “Load failure” run-time error 198 occurs at this point. This situation is avoided by using the DRIVER program method described in Step 1 in the “Correct Chaining Strategy” section above. 2. Fragmentation can occur if the support modules for INDEXED files, and SCREEN ACCEPTS and DISPLAYS are dynamically and not statically linked. Step 2 above shows how to statically link the support modules into the DRIVER program. The following diagram illustrates fragmentation that occurs from dynamically linking ADIS.EXE and IXSIO.EXE.
Low ___________________ __________________ memory|DRIVER.EXE (50K) | |DRIVER.EXE (50K)| | CHAIN “PROG1” | | CHAIN “PROG2” | |_______________ | |_______________ | |PROG1.EXE | | | | (Size: 150K) | | Unused | | OPEN FILE-1 | | Memory | | (INDEXED). | Result | (150K) | | DISPLAY “hi” | ——-> | | ? | AT 0101. | PROG1.EXE | | <—-| | CHAIN “DRIVER”.| is unloaded | | | |_________________| when |________________| | | IXSIO.EXE (77K) | DRIVER | IXSIO.EXE (77K)| _____| High |_______________| CHAINs to |________________| |PROG2 | Memory| ADIS.EXE (98K) | PROG2.EXE | ADIS.EXE (98K) | |Size: | |_________________| |________________| | 250K | | Unused Memory | | Unused Memory | |_______| | (125K) | | (125K) | ? | |_________________| |________________| <—–|
In the above example, the IXSIO and ADIS files are not statically linked into either the DRIVER or the CHAINed programs. When PROG1 OPENs FILE-1, which is an INDEXED file, the IXSIO.EXE module is loaded into memory in the next available position, which is above PROG1 in the middle of memory. When PROG1 issues a DISPLAY AT, the ADIS.EXE module is loaded above IXSIO.EXE. This is what it means to dynamically link support modules – to let them load at run time. Once ADIS.EXE and IXSIO.EXE are loaded into memory, they remain stationary until the entire program terminates. When PROG1 CHAINs back to the DRIVER and the DRIVER CHAINs to PROG2, there is enough total memory to load PROG2, but the memory is fragmented into two smaller parts. Fragmentation occurs because ADIS.EXE and IXSIO.EXE were loaded above PROG1.EXE into the middle of memory. A “Load failure” run-time error 198 occurs at this point. This does not happen if these modules are statically linked to the DRIVER program as described in Step 2 in the “Correct Chaining Strategy” section above. 3. There are two file buffer regions, one 64K region for INDEXED files, and one 32K region for other files. If these buffers are allocated at the wrong time during the CHAIN, they can fragment memory. This can happen even if other precautions, such as the DRIVER method, are used. Consider the following program:
Low ___________________ __________________ memory|DRIVER.EXE (50K) | |DRIVER.EXE (50K)| | CHAIN “PROG1” | | CHAIN “PROG2” | |_______________ | |_______________ | |PROG1.EXE | | | | (Size: 150K) | | Unused | | OPEN FILE-1 | | Memory | | (sequential).| Result | (150K) | | OPEN FILE-2 | ——-> | | ? | (INDEXED file).| PROG1.EXE | | <—-| | CHAIN “DRIVER”.| is unloaded | | | |_________________| when |________________| | | 32K File Region | DRIVER | 32K File Region| _____| High |_______________| CHAINs to |________________| |PROG2 | Memory| 64K File Region | PROG2.EXE | 64K File Region| |Size: | |_________________| |________________| | 250K | | Unused Memory | | Unused Memory | |_______| | (204K) | | (204K) | ? | |_________________| |________________| <—–|
In the program above, the files are opened for the first time during PROG1. This causes the file buffer regions to be allocated in the middle of memory. PROG1 does not CLOSE the files, and CHAINs back to the DRIVER. This causes the file buffer regions to remain allocated. When the DRIVER program CHAINs to PROG2, there is enough total memory to load PROG2 but the memory is not contiguous. The memory is divided into two smaller portions by the file buffer regions, and neither free block of memory is big enough to load PROG2. A “Load failure” run-time error 198 occurs at this point. Fragmentation by not correctly handling the file buffer regions can be avoided by having the driver program OPEN and leave OPEN two dummy files, as described in Step 3 in the “Correct Chaining Strategy” section above. This application note was written by Microsoft Product Support Services (June 9, 1989).
Copyright Microsoft Corporation 1993.